Replaces the read-only System AI block in Settings with a real
editor. Operator never has to touch ~/.fai/config.yaml again
to change provider, endpoint, model, API-key env-var, or
privacy mode.
UX choices that align with the zero-learning-curve rule:
* Provider dropdown lists Ollama / OpenAI / LM Studio / vLLM
/ Custom by friendly name. Each selection auto-fills the
endpoint and api_key_env defaults — but only when the
field is still empty or matches a different preset's
default, so manual overrides are never clobbered.
* Each provider has a one-line description rendered under
the dropdown ("Ollama → Local `ollama serve`. Models stay
on your machine. No API key needed.") and a model-hint
placeholder ("gemma3:4b · llama3.2:3b · qwen2.5-coder:7b")
that goes away when a model is typed.
* Privacy mode is a vertical radio group with each option's
description in place — no doc-lookup needed.
* "Test connection" sends an `ok`-ping and renders the
same error-kind → fix-hint mapping the audit drill-down
uses.
* "Save" persists + hot-reloads the hub. No daemon restart
required. UI status badge flips to "enabled · <privacy>"
immediately.
`fai_dart_sdk` 0.5.0 carries the underlying `updateSystemAi` /
`testSystemAi` RPCs.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two new operator affordances powered by `HubAdmin.AskAi`:
* **Audit drill-down → "Explain" button.** Tappable on every
failed event when System AI is configured; greyed out with
a "Configure in Settings" tooltip otherwise. Shows an
inline panel under the event fields with the LLM's plain-
language explanation, a "FIX" suggestion, the privacy-mode
badge (`redacted` / `full`), and round-trip latency. Errors
map to actionable fix hints (`env_missing` → "set $VAR in
your shell, then fai daemon restart"; `network` → "is your
provider running?"; etc.) — no raw HTTP errors surfaced.
* **Settings dialog → System AI panel.** Renders the live
HubAdmin.SystemAiStatus (provider, endpoint, model,
api_key_env name, privacy mode). When disabled, shows a
one-line explainer + pointer to docs/architecture/system-ai.md.
Read-only for now; editing the config still requires
~/.fai/config.yaml (Phase-1+ adds in-place editing).
Both surfaces follow the new zero-learning-curve UX rule:
the user never has to look up what the configurable means or
how to fix an error — every state self-explains in place.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Turns Studio from read-only dashboard into operator console.
Pieces:
* **New Store page** (sidebar destination #3): browses the
hub's bundled store-index with query + category + status
filters. Each card has an Install button that prompts for
the `.fai` bundle source (URL or local path), ships it to
HubAdmin.InstallModule, shows success / error in a progress
dialog, refreshes the list.
* **Audit drill-down**: every event row is now tappable;
opens a modal with all LoggedEvent fields including the
new `detail` JSON pretty-printed in a code block. KRITIS
forensic story: every audit row → full structured detail
in two clicks.
* **Approvals payload preview**: shipped already; now
pretty-prints JSON when the preview parses as such, plus
a clearer `PAYLOAD PREVIEW` label. Reviewer identity
defaults to `$USER@studio` instead of the hard-coded
`studio-mvp` so the audit trail records who acted.
* **Channel switcher in Settings dialog**: the dialog now
pulls HubAdmin.ChannelStatus and renders one row per
channel (local / dev / beta / production) with port,
running indicator, active marker. A "Connect" button
on a running channel sets Studio's endpoint to that
channel's port and reconnects in one click.
Cmd+1..6 keyboard shortcuts updated for the six destinations.
Widget test asserts every destination renders.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Doctor page now opens with a slim banner above the summary strip
in two cases: an actually newer version on the channel ("Update
available — v0.x.y", success-tone "new" pill, release-notes URL
when present) or the release host being unreachable
("Release host unreachable", neutral "offline" pill, with the
network reason). When local matches latest the banner stays
hidden — quiet UI is the right default.
Hooks into the existing parallel-fetch in `HubService.doctor` so
the new RPC adds zero latency to the page load.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two operator-DX additions:
1. **Flows** is the new third destination in the sidebar.
Lists saved flows from the hub via the new HubAdmin
ListFlows RPC. Each row has a Run button that opens an
input dialog (key=value pairs, one per line, all values
sent as text payloads — the binary-input case stays in
`fai run` CLI). Flow runs in a non-dismissable progress
dialog; output is shown per-key with monospace
selectable text. Errors render with the exception detail
for diagnosis.
2. **Keyboard shortcuts** at the shell level:
- Cmd+1 / Cmd+2 / Cmd+3 / Cmd+4 / Cmd+5 jump to the
matching destination (Doctor / Modules / Flows / Audit
/ Approvals).
- Cmd+, opens the Settings dialog (macOS convention).
Implemented via Flutter's Shortcuts/Actions/Intent triple
so the bindings are discoverable in IDE devtools and
composable with platform-specific overrides later.
Bumps fai_studio 0.7.3 -> 0.8.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The toggle changed _mode and called setState on StudioAppState,
but the sidebar that hosts the toggle button never rebuilt:
StudioApp returns `MaterialApp(home: const StudioShell())`, and
the const child's element doesn't get a didUpdateWidget on
parent rebuild. So the toggle's icon would only flip on the
next health-poll tick (up to 5s later) when the sidebar's own
setState fired for an unrelated reason.
Switch to a ValueNotifier-based pattern:
- StudioAppState exposes `modeNotifier` (ValueNotifier<...>)
instead of a getter.
- StudioApp wraps MaterialApp in a ValueListenableBuilder
bound to the notifier so themeMode flips immediately.
- _ThemeToggle is itself a ValueListenableBuilder against the
same notifier — its icon and onPressed always reflect the
current value without depending on parent rebuild timing.
Bumps fai_studio 0.7.2 -> 0.7.3.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two issues from live feedback:
1. The version label said "v0.7.1 · warm-minimalism". The
suffix is an internal design-direction codename, not
user-facing. Drop it — the line now just reads "v0.7.2".
2. The ∆ pulse in live mode used only alpha + blur, which is
too subtle to register at first glance. Replace with a real
scale-pulse: the whole mark grows from 0.85x → 1.15x → 0.85x
over 1.6s. Glow halo blur range expanded (8→22) and alpha
range widened. The triangle also gets a soft fill on the
up-beat. Easy to spot from across the room.
Bumps fai_studio 0.7.1 -> 0.7.2.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two issues:
1. _StatTile in the Doctor page overflowed by ~6px on narrow
widths because the Row containing the icon + label "AUDIT
CHAIN" with letterSpacing=0.6 didn't fit in the ~90px
constraint per tile (4 tiles in a Row sharing the page
width). Fix: smaller icon (16→14), tighter gap, label wrapped
in Flexible with TextOverflow.ellipsis, and the longest label
shortened "AUDIT CHAIN" → "AUDIT" with the chain count moved
to the subtitle.
2. The new redesign was easy to mistake for the previous build
from the outside. Add a small monospaced version line right
under "F∆I Studio" in the sidebar header:
F∆I Studio
v0.7.1 · warm-minimalism
Self-identifying — "wrong build" is now visible without
reading the about dialog.
Bumps fai_studio 0.7.0 -> 0.7.1.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Three-way switch in the sidebar footer next to the settings
gear. Cycles system → light → dark → system. Choice persists
via shared_preferences across app launches; restored before the
first frame so there's no theme flicker on startup.
The icon changes per state (auto / sun / moon) and the tooltip
spells it out so the cycling behaviour is discoverable.
- StudioApp is now stateful; exposes StudioApp.of(context) to
let descendants flip the theme without prop-drilling.
- ThemeModeValue enum lives in data/hub.dart so the persistence
layer stays free of flutter/material imports.
Bumps fai_studio 0.6.0 -> 0.7.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Studio is no longer hardcoded to localhost:50051. A gear icon
in the sidebar footer opens a small settings dialog where the
operator picks host / port / TLS-on-or-off. Saved values
persist via shared_preferences and Studio reconnects to the
new endpoint immediately.
- data/hub.dart: HubService.loadPersistedEndpoint() called once
before the first frame; reconnect() persists on every change.
- widgets/fai_settings_dialog.dart: typed form with port range
validation, live URL preview pill, error surfacing if the
reconnect fails.
- main.dart: gear icon at the sidebar bottom, replaces the
static "platform v0.10.42" footer.
shared_preferences added as dependency. flutter analyze clean,
flutter test 2/2, macOS debug build succeeds.
Bumps fai_studio 0.4.0 -> 0.5.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Composes the platform's health picture in one view:
- Summary strip: 4 stat tiles for modules / approvals / audit
chain / services with semantic accent colour.
- Event-log panel: WORM-1 badge + chain status, points to the
first tampered event id when integrity fails.
- Modules & approvals panel: counts plus an "attention" pill
when approvals are pending.
- Services panel: declared services from operator config or a
helpful empty-state pointing at ~/.fai/config.yaml.
Backed by the new HubAdmin RPCs (VerifyEventChain, ListServices)
plus the existing ListCapabilities and ListApprovals. One
HubService.doctor() call fans out to all four in parallel.
Doctor is the new first destination in the sidebar — that's the
page an operator wants to land on after starting the app.
Bumps fai_studio 0.3.0 -> 0.4.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Replaces the Material-default look with a deliberate visual
language. Single accent (sky-cyan), Inter + JetBrains Mono via
google_fonts, dense rows over puffy cards, micro-interactions
under 250ms. Dark-first, light reaches parity.
New foundation under lib/theme/:
- tokens.dart FaiColors / FaiSpace / FaiRadius / FaiMotion
- theme.dart ColorScheme + typography + component themes for
light and dark, plus FaiTheme.mono helper for
technical strings (IDs, paths, capability refs).
Six primitives under lib/widgets/:
- FaiCard flat card, optional accent stripe (top or
left). No shadows.
- FaiPill small inline label with five tones
(neutral / accent / success / warning /
danger), optional mono and leading icon.
- FaiStatusDot breathing dot, used as a "live" indicator.
- FaiDataRow Linear-style dense row for the audit
stream — hover-elevation, mono leading,
coloured leading stripe.
- FaiEmptyState gracious icon + title + hint + action,
replaces "(no data)" everywhere.
- FaiDeltaMark the ∆ signature element. Three modes —
idle (still), live (gentle pulse), busy
(slow rotation). Drawn from primitives,
not a font glyph. Lives in the sidebar
header so the brand is always visible.
Page-level changes:
- main.dart custom 220px sidebar replaces the Material
NavigationRail. ∆ on top, hub-connection
pill below it, hover-animated destinations,
page transitions are 200ms slide+fade.
- modules.dart FaiCard rows with capability pills.
- audit.dart FaiDataRow stream, segmented filter chips,
live status bar with hash-chain badge.
- approvals.dart FaiCard with accent-top stripe, structured
action footer, toast on approve/reject,
themed reject-reason dialog.
google_fonts added as dep. flutter analyze clean. flutter test
2/2. flutter build macos --debug succeeds.
Bumps fai_studio 0.2.0 -> 0.3.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Initial scaffold for the F∆I Platform Tier-2 generic GUI client.
Flutter Desktop (macOS, Linux, Windows). Three MVP pages with
mock data, sharing one navigation shell:
- Modules — installed modules with capabilities, declared
permissions and required services.
- Audit — event-stream view with type filter and tone-coded
rows (started / completed / failed).
- Approvals — pending system.approval@^0 reviews with prompt,
payload preview, and approve/reject buttons.
Live gRPC connection arrives in the next iteration via
fai_dart_sdk (sibling repo, currently a typed stub).
Future Forgejo path: fai/studio. Local layout matches existing
fai_platform/ convention.
Background: see docs/architecture/client.md in the platform
repo. The tier-2 client was previously called "Stage" — renamed
to "Studio" on 2026-05-05 to avoid confusion with
"staging environment".
flutter analyze: clean. flutter test: 2/2.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>