Sidebar:
- New active-channel pill below the connection pill,
color-coded so production deployments look visibly
different from local-dev. Click opens Settings; tooltip
explains the channel taxonomy. Polled in lockstep with the
health probe so it tracks CLI-driven `fai channel switch`.
Store:
- Featured strip above the main grid renders editorial
picks as bigger hero tiles with a subtle gradient
backdrop, version + status pills, and a one-click
Install. Hidden the moment the operator types in the
search box or picks any filter — the result list is the
answer they want, not editorial chrome.
StoreItem now carries the `featured` flag so the page can
filter without an extra round-trip.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two Phase-2 Store improvements that operators feel
immediately:
- Per-module update badge: store cards compare the operator's
installed version against the index's `best_version`, raise
an "update" pill + "Update to vX.Y.Z" button when newer is
available. Result: a single glance at the Store reveals
what's behind. The page now fetches `listModules` in
parallel with the search so the comparison is on the
current snapshot.
- Inline README rendering: detail sheet has a new
Documentation section. "Load documentation" button (lazy —
no network until clicked) calls FetchModuleDocs and renders
the markdown via flutter_markdown, with the operator's
theme colors and link-tap routed through SystemActions.
Auth / not-found / network failures fall back to a
friendly fix-hint panel + "Open repository in browser"
button so the operator never hits a dead end.
Adds flutter_markdown ^0.7.7 to pubspec; the package is
maintenance-only upstream but is the only mature option
for Material-themed markdown right now. Easy to swap to
markdown_widget if/when we need GitHub-flavored extras.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
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>
UI parity for Windows operators who never touch a shell:
- Module sheet gains an Uninstall button at the bottom.
Two-step confirmation, then calls UninstallModule + closes
the sheet + refreshes the Modules page.
- Doctor page picks up two new sections:
* Daemon files — log / config / audit DB / modules /
flows / pid, each with a one-click "Open" button that
shells out to the OS handler (open / xdg-open /
explorer).
* Daemon control — Restart / Stop / Status buttons that
spawn `fai daemon …`. Captured stdout/stderr renders
inline so the operator sees what happened.
- Update banner gains an "Apply update" button that spawns
`fai update apply --channel <c>`. The previous version
showed the command as text — Windows users had no way to
execute it.
- Event log panel gains a "Verify now" button that re-runs
the chain check (via the existing doctor refresh).
New SystemActions helper resolves the `fai` binary via
$FAI_BIN → PATH → `~/.fai/bin/fai` (or the Windows
equivalent), so the buttons work whether the operator
restarted their shell after installing or not.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Pending tab keeps the existing card layout for actionable
approvals. New History tab shows decided rows (approved /
rejected / expired) as compact one-liners, color-coded by
status with reviewer + decided-at metadata. Click expands to
the full record (prompt, payload preview, reason, ids).
Closes the visibility gap from the recent fix: rejected
approvals were correctly written to the hash-chained audit
log but invisible to operators on the Approvals page itself.
HubService gains `listApprovalsRecords(statuses:)` returning
the decided-side fields the Studio wire previously dropped.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Audit drilldown's explanation panel now surfaces whether the
answer came from the cache:
- "cached" pill next to the SYSTEM AI label, tooltip shows
the original generation date + hit count.
- Latency renders as "orig 47000 ms" so the operator sees
what a live call would have cost.
- Refresh icon next to the latency triggers Regenerate —
drops the entry, re-asks the model.
System-AI editor shows a cache-status row when the cache is
non-empty with a one-click Clear button. Hidden on fresh
installs to keep the dialog quiet.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Operators can now clear dev-noise from the audit log without
dropping to the CLI. Top-bar broom icon next to the filter
chips opens a two-field dialog (reviewer pre-filled from $USER,
reason required). On submit the hub call returns the purged
count + active channel; both are shown back in a snackbar.
Compliance-locked channels (beta/production) are refused
server-side; the dialog surfaces the rejection reason via a
friendly error message instead of the raw gRPC status string.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Previous patch dropped the year for current-year events,
showing `05-04 21:25:39` — locale-ambiguous (US reads May 4,
EU reads 5 April). Operators should not have to guess. Now
every non-today timestamp renders as `YYYY-MM-DD HH:mm:ss`;
same-day events keep the compact `HH:mm:ss`.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Previously the audit list only rendered HH:mm:ss, so a row showing
"21:25:39" gave no hint whether it was today, last week, or last
year. The detail dialog already had the full ISO timestamp, but
the operator had to click each row to find out.
_formatTime now keeps the compact HH:mm:ss form for same-day
events, prefixes MM-dd for older days in the current year, and
shows YYYY-MM-dd for the tail of the log. Always rendered in the
operator's local time zone.
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>
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>
Tapping a module card on the Modules page slides up a detail
sheet showing the full manifest data: directory path
(monospaced + selectable), every capability with version, and
every declared permission with a semantic icon (net = globe,
fs.read = folder, fs.write = edit, env = terminal, hub =
shield).
Empty permission list shows "(none — pure-computation module)"
explicitly — the operator should see *why* a module needed
zero perms, not be left guessing.
Backed by HubAdmin.ModuleInfo. New widget FaiModuleSheet with a
small drag-handle, FutureBuilder for the load state, error
surfacing if the RPC fails.
Bumps fai_studio 0.5.0 -> 0.6.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>
Studio's three MVP pages now pull live data from a running hub
via fai_dart_sdk. Mock fixture removed.
- data/hub.dart: HubService singleton wraps fai_dart_sdk's
HubClient and exposes UI-friendly types (ModuleSummary,
AuditEvent, PendingApproval) so pages don't import protobuf.
- pages/modules.dart: FutureBuilder against listModules,
groups CapabilityEntry rows by module, retry-on-error UI.
- pages/audit.dart: 2s polling Timer, status bar shows
"live (polling 2s)" or "disconnected — <error>".
- pages/approvals.dart: live listApprovals, Approve sends
DecideApproval(APPROVE), Reject opens a reason dialog and
sends DecideApproval(REJECT). Both refresh after success.
- Connection-error states across all pages: cloud_off icon +
hint to start `fai serve` + Retry button.
When no hub is running the pages render their disconnected
state instead of crashing. When a hub is running, the data is
real — no more mock fixture.
Bumps fai_studio 0.1.0 -> 0.2.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>