Commit graph

26 commits

Author SHA1 Message Date
flemming-it
bb793e64a9 feat(studio): inline flow editor — YAML highlighting + Run (v0.50.0)
Some checks failed
Security / Security check (push) Failing after 1s
New top-level destination "Editor" (Cmd+5) ships as Studio's
fifth surface. The editor reads + writes flow YAML directly
under ~/.fai/data/flows/ via dart:io — the hub picks up the
changes on the next listFlows / runSavedFlow call.

Layout: two-pane shell. Left (240 px) is the file list; right
flexes to the code pane and an optional results column when
Run produces output. Top toolbar exposes:
  * filename + dirty-mark
  * New flow (scaffolds from a debug.echo template)
  * Save (writes the active file to disk)
  * Run (saves first if dirty, calls
    HubService.runSavedFlow, surfaces typed FlowOutputs in
    a side panel)
  * Refresh

YAML highlighting via flutter_code_editor + the highlight
package's yaml language. Lightweight style map mapping the
five token classes that actually appear in flow YAML
(attr / string / number / comment / subst for the
${{ ... }} template syntax) to FaiTheme colors — keeps the
editor visually consistent with the rest of Studio.

New-flow naming uses a FilteringTextInputFormatter that
restricts the name to [a-z0-9_-]. A "name already exists"
SnackBar surfaces the conflict instead of silently
overwriting.

Bilingual strings shipped (en.arb + de.arb) for every
operator-facing string: toolbar buttons, dialogs, empty
states, file-exists error, run-output header.

New deps:
  * flutter_code_editor ^0.3.5
  * highlight (transitive — pinned as direct so the
    yaml-language import has its declared dependency).

Smoke-test (test/flow_editor_test.dart) pumps the page and
asserts the empty-state + toolbar render without throwing
on hosts that don't have ~/.fai/data/flows yet. The full
file-list + open-on-tap flow needs a writable HOME override
which dart:io's read-only Platform.environment doesn't allow
inside a test isolate — that path lives in the integration
suite as a follow-up.

Version bumps:
  * pubspec.yaml: 0.49.1 → 0.50.0
  * main.dart kStudioVersion: 0.42.0 → 0.50.0 (had drifted
    behind pubspec; brought back into sync as part of this
    bump)

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-30 01:10:05 +02:00
flemming-it
23529bae82 chore(deps): absorb fai_client_sdk 0.17.0 (grpc 5, protobuf 6)
Some checks failed
Security / Security check (push) Failing after 2s
Lifts every direct dependency Studio carries to its latest
available version:

  fai_client_sdk: 0.16.0 → 0.17.0  (grpc 5, protobuf 6, new
                                    bindings)
  + the SDK major dragged dbus 0.7.13, xml 7.0.1, analyzer
    13.0.0, _fe_analyzer_shared 100.0.0 via the constraint
    solver in the platform→sdk→studio chain.

What Studio's call sites needed in code: nothing. `HubClient`
already wraps every gRPC + protobuf type, so the major bumps
land transparently. The integration tests against the live
hub fixture pass without changes (11/11 green).

Remaining outdated transitives are all Flutter SDK 3.44.0
pins:
  - meta 1.18.0 → 1.18.2
  - vector_math 2.2.0 → 2.3.0
  - win32 5.15.0 → 6.3.0

These lift with the next Flutter stable bump. No action
possible from Studio's pubspec today.

Bumped pubspec to 0.49.0.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-29 14:36:30 +02:00
flemming-it
1d68858899 chore(deps): bump file_picker 8→11 + transitive lifts
Some checks failed
Security / Security check (push) Failing after 1s
`flutter pub upgrade` lifts every transitive dep that the
constraint solver can reach: google_cloud 0.4.1→0.5.0,
googleapis_auth 2.3.0→2.3.1, hooks 1.0.3→2.0.0, objective_c
9.3.0→9.4.1 (plus xml 6.6.1).

Direct: file_picker 8.0.0 → 11.0.0. The major jump is the
move from instance API (`FilePicker.platform.pickFiles(...)`)
to static API (`FilePicker.pickFiles(...)`). Two call sites
updated (`lib/pages/flows.dart` for the run-flow form's
file input, `lib/widgets/fai_flow_output.dart` for the
flow-output "Save as" affordance) — both functionally
identical, just the surface that lost the `.platform.`
hop.

Locked behind external pins (no action possible from
Studio side this pass):
- grpc 4.2.0 → 5.1.0  ─┐
- protobuf 4.2.0 → 6.0.0 ┤── pinned by fai_client_sdk; a
                          │   major SDK bump is its own
                          │   piece of work (regenerated
                          │   bindings, possibly Studio
                          │   call-site shifts).
- characters / meta / vector_math / win32 / native_toolchain_c
   ─ all pinned by the Flutter SDK; lift with the next
     Flutter SDK upgrade.

dart analyze clean; flutter test green (12 tests pass).
Bumped pubspec to 0.48.1.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-29 07:15:25 +02:00
flemming-it
a5bd51de33 chore(studio): switch to fai_client_sdk (renamed from fai_dart_sdk)
Some checks failed
Security / Security check (push) Failing after 1s
The Dart SDK package was renamed package: fai_dart_sdk →
fai_client_sdk and the dir + Forgejo repo got a -dart language
suffix per the three SDK families convention in
fai/platform/docs/architecture/sdks.md. Updates the path
dep, dep name, every `package:fai_dart_sdk/...` import, and
the one comment that named the SDK.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-26 13:46:32 +02:00
flemming-it
69b54629d3 chore(studio): flutter_markdown_plus migration + integration-test scaffold
Two follow-ups to the May-2026 trust pass.

flutter_markdown_plus migration:

- pubspec swaps `flutter_markdown ^0.7.7` (discontinued
  upstream) for `flutter_markdown_plus ^1.0.3`, the
  actively-maintained fork. API surface
  (MarkdownStyleSheet, Markdown, MarkdownBody) is
  unchanged — the four import sites in welcome, store,
  flow_output, and theme.dart get an updated package
  string and that's it.
- All Studio analyzer + unit-test suites stay green.

Integration test scaffold:

- New `test/integration/hub_fixture.dart` boots a real
  `fai serve` subprocess on a free port against a temp
  FAI_DATA_DIR, polls until Healthy, exposes a ready
  HubClient. Idempotent teardown wipes the temp dir.
- Resolves the `fai` binary from PATH first, then from
  `../fai_platform/target/release/fai`. When neither
  exists, the fixture calls `markTestSkipped` with a
  clear message — fresh checkouts don't fail.
- One canonical test in `capabilities_test.dart` asserts
  on the bug class the May trust pass surfaced: that
  `system.approval` appears in `list_capabilities` with
  `kind=builtin` so Studio's missing-deps check never
  tries to install it. Plus a contract-shape test that
  every cap's `kind` is one of the three known wire
  values.
- README documents the cold-start gotcha (first
  `fai serve` per machine takes ~30s to build the
  curated-model DB) plus the manual warmup recipe.

Not in CI yet — wiring needs the platform build job to
publish `fai` as a CI artifact for downstream consumption.
Deferred until enough integration tests exist to justify
the CI minutes.

Signed-off-by: flemming-it <sf@flemming.it>
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-25 13:22:04 +02:00
flemming-it
2002486828 feat(studio): operator QoL — flow runnability, audit grouping, batch approvals, welcome celebration (v0.42.0)
Four UX threads stitched into one commit. Each pulls Studio
toward Stefan's "zero-learning-curve" goal — the feedback
that earned its own memory entry.

1. Flow-Runnability-Indikator
   ─────────────────────────
   The Flows tab now fetches `listFlows` and `listModules`
   in parallel. Each card compares the flow's
   `requiredCapabilities` against the installed-modules'
   capability set; rows with missing modules show a "Needs:
   text.extract@^0" red pill row beneath the path and have
   their Run button greyed out + tooltip
   "Install the missing modules first." Operators stop
   hitting Run → cryptic hub error → frustration.

2. Welcome-Checklist Celebration
   ─────────────────────────────
   Once all four checklist signals flip to done, an
   `_AllDoneCelebration` card replaces the bare
   "All four steps complete" + Hide button. Three concrete
   next-threads with action buttons: "Read the audit log",
   "Set up the daily Today story" (opens the Flows / Today
   doc inline via `_DocReaderSheet`), and "Build your own
   module" (opens the architecture doc). Operator who just
   got set up sees what to do next instead of an empty
   "what now?" feeling.

3. Audit-Page Time-Bucket Headers + Flow-Run Detail
   ────────────────────────────────────────────────
   The flat event list grows tiny "TODAY / YESTERDAY /
   EARLIER THIS WEEK / OLDER" section headers — bucket is
   computed in the operator's local timezone so an event at
   23:55 yesterday in Berlin doesn't end up in "today"
   because UTC happened to spill into a new day.
   Plus: the event-detail dialog gains a "View flow run"
   action when the picked event has a `flow_execution`. It
   opens a drill-down that lists every event in the
   already-fetched 100-event window sharing the same
   execution id, sorted ascending — the operator reads the
   run from step.started top to flow.completed bottom.

4. Approvals-Batch-Aktionen
   ────────────────────────
   Each pending approval card grows a checkbox. When ≥1
   selected, a floating action bar appears at the bottom
   with "N selected · Select all · Clear · Reject all ·
   Approve all". The parent loops sequentially through the
   per-record SDK calls so a partial failure produces
   "X done, Y failed" instead of a confusing all-or-nothing
   rollback. Reject prompts for a reason once and applies
   to the whole picked set.

13 new ARB keys cover the strings the four features
needed. Studio's tests stay green.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 11:24:57 +02:00
flemming-it
f90d8cc7a7 feat(studio): typed run-flow form, file picker, copyable errors (v0.41.0)
The free-form key=value run-flow dialog produced two
unhelpful failures every time an operator hit Run:

  1. Submit empty → "step references missing value
     '\$inputs.document'" from the hub. Studio sent a zero-
     entry inputs map because nothing told the operator the
     flow declared a required input.
  2. Type `document=@/path/to/file` → "path not found".
     Dart's `File()` doesn't expand `~`, and on macOS
     sandboxed Studio can't read arbitrary paths anyway.

Both failure modes are gone. The dialog is now a typed form:

- On open, fetches `getFlowDefinition(name)` (new SDK
  v0.15.0 wrapper around the v0.10.89 hub RPC). While that
  resolves, a small spinner shows
  "Loading inputs…"; on failure, an inline `FaiErrorBox`
  with the exact RPC error and a copy button replaces the
  spinner.
- Renders one form-field per declared input. The flow
  YAML's verbatim type tag drives the widget choice:
  `bytes` / `file` → "Choose file…" button + picked-file
  readout, plain TextField for everything else. The type
  tag is shown next to the input name as a pill so a flow
  author who picks a less-common type still gets a hint.
- Bytes inputs route through `file_picker` with
  `withData: true`, which means the OS file dialog handles
  read access — sandboxed Studio gets the bytes inline
  rather than a path it can't open. Falls back to
  `File(path).readAsBytes()` on Linux configs that don't
  honour `withData` for large files; failures show a
  copyable SnackBar.
- The Run button enables only when every declared input has
  a value (text non-empty / file picked). Empty submission
  is impossible; the cryptic hub-side
  "missing value" error stops surfacing.
- The run-result dialog's error path uses `FaiErrorBox`
  instead of the previous plain `SelectableText`, so any
  hub-side flow error (permission denied, module crash,
  whatever) is one click to clipboard.

11 new ARB keys cover the form's labels, the file-picker
states, the loading / failed-definition messages, the
"This flow declares no inputs" empty state, and the
run-error dialog title.

`file_picker: ^8.0.0` added to pubspec.yaml.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 10:18:08 +02:00
flemming-it
ce97300a12 feat(studio): daily Today-Hero proposal pipeline (v0.31.0)
Cross-store research (Apple, Play, Steam, Docker, VS Code,
Chrome Web Store, Flathub) consistently rewards editorial
curation over algorithmic recommendations — but manual
copywriting per release does not survive a solo-dev cadence.
This commit lands a daily-build pipeline so the Today-Hero
card stays fresh without operator hand-edits per release.

Pipeline shape (full design in docs/today-pipeline.md):

1. tools/today/collect.sh aggregates "what happened in the
   last 24 hours" across the F∆I monorepos: git log per repo,
   store-index seed.yaml diffs, architecture/system-gaps doc
   changes, Studio release tags, and (opt-in) audit-log
   highlights. Outputs plain text.

2. tools/today/propose.sh feeds the signal summary plus
   prompt.template.md to the operator's already-configured
   System-AI (Ollama default; OpenAI-compatible endpoints
   work via env-var override). Drafts N candidate stories as
   YAML files under ~/.fai/today/proposals/<date>/.

3. tools/today/accept.sh validates a chosen candidate against
   the today/v1 schema and the no-marketing-speak banned-word
   list, then atomic-renames it into ~/.fai/today/active.yaml.

4. Studio reads active.yaml at store-page init via the new
   TodayStoryLoader (lib/data/today_story_loader.dart). On any
   failure (file missing, schema mismatch, banned-words hit,
   parse error) it falls back to the compiled-in
   _kFallbackTodayStory so KRITIS deployments and fresh
   installs always render something sensible.

Trust + audit:
- All proposed and accepted stories live as plain YAML on disk.
- The pipeline calls only the operator's already-configured
  System-AI; it never reaches a CMS, never phones home, works
  air-gapped if the System-AI does.
- The bash accept gate AND the Dart loader both enforce the
  banned-word list — a hand-edited active.yaml that bypassed
  the shell still won't reach the UI.
- Removing the cron entry disables the pipeline; Studio falls
  back to the const story and continues to work.

Cron / launchd / systemd recipes documented in
tools/today/README.md.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 12:59:33 +02:00
flemming-it
ee83eb851a feat(studio): app-wide i18n via flutter_localizations (v0.24.0)
Replaces the Store-only DE/EN toggle with an app-wide one
parked in the sidebar footer next to the theme button.
Pressing it flips every translated string at once: nav
labels, page titles, common buttons, the bilingual
store-index content.

Implementation:
- Adds `flutter_localizations` + `intl` to pubspec, plus
  `flutter.generate: true` so `flutter gen-l10n` runs in the
  build pipeline.
- ARB sources at `lib/l10n/app_en.arb` and `app_de.arb`. The
  EN file is the template; DE carries the German strings.
  Initial coverage: navigation, common buttons, page titles,
  channels / store / audit / modules / approvals headers,
  hub-unreachable copy, MCP + n8n panel headers + hints.
  Rest of the UI strings are still English-literal — those
  fall in incrementally as we touch each surface.
- Generated `AppLocalizations` lives at
  `lib/l10n/app_localizations*.dart` (regenerated via
  `flutter gen-l10n` on every ARB edit).
- `StudioAppState` gains `localeNotifier` alongside
  `modeNotifier`; persisted via SharedPreferences key
  `locale.code`.
- Sidebar `_LanguageToggle` reads/writes through the
  notifier. The Store's per-page locale state is gone:
  `_locale` now reads `Localizations.localeOf(context)
  .languageCode`, so the bilingual store-index content
  follows the global setting without a second toggle.
- `_NavPage.label` becomes `_NavPage.id` + `labelOf(context)`;
  Cmd+K palette and Sidebar both read the localized label.

Out of scope this iteration: localizing the remaining
~80% of UI strings (Settings dialog labels, Store search
hint, error messages). Those land incrementally — the i18n
infrastructure now means each is a one-line ARB edit + one
call-site swap.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 01:19:32 +02:00
flemming-it
d2a6ed32e2 feat(studio): n8n-endpoints editor in Settings (v0.23.0)
Mirror of the MCP-clients panel: Add / Remove / Refresh
with last-discovery health (workflow count or error_kind),
sister Add-dialog with name + base_url + optional
api_key_env + notes. HubService gains the four passthroughs
(`listN8nEndpoints`, `refreshN8nEndpoints`,
`addN8nEndpoint`, `removeN8nEndpoint`) and a
`N8nEndpointInfo` data class.

Settings dialog now hosts: hub endpoint, channels,
system AI, MCP clients, n8n endpoints — all behind the
existing 520×640 SingleChildScrollView so the layout
doesn't crowd.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 01:00:17 +02:00
flemming-it
9ef32df31e feat(studio): MCP-clients editor + federated entry rendering (v0.22.0)
The viral-bridge story now has a UI surface. Settings →
MCP Clients lists every configured server, shows last-
discovery health (green dot + tool count, red dot + error
kind), and offers Add / Remove / Refresh.

Add-server dialog: name (no dots), endpoint, optional
api_key_env, optional notes. On submit the hub persists +
re-runs discovery in one round-trip; the Store fills with
synthetic `mcp.<name>.<tool>` entries the operator can see
immediately.

StoreItem now carries `kind` and `provider`. Federated
entries route through a bridge — no install bundle to
download — and Studio's install button renders accordingly
(via the existing "Not installable" path; the Phase-2
follow-up adds an explicit "Configure bridge" call-to-action).

Settings dialog grew to 520×640 with a SingleChildScrollView
so the additional panels don't crowd. Channel pill, system-AI
panel, MCP-clients panel all coexist without a redesign.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-07 22:48:21 +02:00
flemming-it
11b0ac97a1 feat(studio): Cmd+K palette, recent-activity panel, icon/screenshot rendering (v0.21.0)
Three sweeps in a single bundle.

Universal command palette (⌘K / ctrl+K):
- Modal at top-of-window with a single search input. Indexes
  pages, installed modules, store entries, saved flows on
  open; filters client-side as the operator types.
- Keyboard nav (↑/↓ + Enter), grouped sections, hover-to-
  highlight. Hit selection navigates / opens the right
  surface in one keystroke. Closes with Esc.
- Designed to mirror the VSCode / Linear / 1Password
  ergonomics — gives non-CLI operators a "jump anywhere"
  affordance that scales with the number of installed
  modules.

Modules page recent-activity panel:
- Top-of-page strip lists the last 10 install / uninstall
  events from the audit log, color-coded by direction.
  Hidden when no relevant events exist (fresh installs).
  Same locale-unambiguous timestamp format used in audit.

Store detail sheet now renders icons + screenshots:
- `_ModuleIcon` widget loads the explicit `iconUrl` if
  provided, falls back to the category glyph on missing
  URL or load failure (no broken-image rectangle).
- Screenshots strip below the description: 320×200 tiles,
  horizontally-scrollable, click opens full-size via the
  OS handler. Placeholder card on load failure.

StoreItem extended with `iconUrl`, `screenshotUrls`,
`docsUrl` so the new content paths through unchanged.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-07 21:51:47 +02:00
flemming-it
26691b7471 feat(studio): channel pill in sidebar + Featured strip in Store (v0.20.0)
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>
2026-05-07 21:05:15 +02:00
flemming-it
04cc12bc54 feat(studio): inline README + per-module update badge in Store (v0.19.0)
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>
2026-05-07 20:46:16 +02:00
flemming-it
73e394b39f feat(studio): module uninstall + Doctor daemon control (v0.17.0)
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>
2026-05-07 17:46:46 +02:00
flemming-it
d82738e17e feat(studio): cached/Regenerate UX in audit Explain (v0.16.0)
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>
2026-05-07 17:29:36 +02:00
flemming-it
b32b4b5bda feat(studio): clear-audit-log button on Audit page (v0.15.0)
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>
2026-05-07 17:10:02 +02:00
flemming-it
942c29a395 feat(studio): hardware-aware model recommendations + sidebar clock (v0.14.0)
System-AI editor now fetches the detected hardware tier and the
hub's curated model database on open. Each model chip's colour
+ tooltip combines both: green star when curated quality is
good/excellent AND min_hw_tier ≤ host; orange when quality is
fine but the host is below min_hw_tier; warning for "basic"
curated entries. Tooltip exposes the curator's note, parameter
count, context window, and licence. Banner above the form
shows "Detected: <summary> · curation reviewed <date>".

Sidebar footer gains a small monospaced live clock between the
theme toggle and the settings icon. Tooltip exposes full local
ISO + UTC for cross-checking against audit-log timestamps.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-07 15:39:27 +02:00
flemming-it
a83a138259 feat(system-ai): auto-load models on open + suitability hints (v0.13.1)
Two operator-felt papercuts on the editor:

  * Models only appeared after a manual Refresh click. Now the
    editor calls `_refreshModels()` from initState (post-frame)
    so chips populate immediately. Provider switch also re-fires
    the refresh because endpoint / model set changes too.

  * Operator had no signal which model is suitable. Tiny models
    fail at instruction following; huge models stall on a
    laptop. Adds a heuristic suitability rating (parses
    parameter count from common names like `gemma3:4b`,
    `llama3.2:70b`; falls back to family hints for OpenAI /
    Claude). Each chip now carries:
       - a coloured leading dot (green=balanced, orange=small
         or large, red=huge, grey=unknown)
       - a star icon for the hand-curated "tested with F∆I"
         allowlist (gemma3:4b, llama3.2:3b, qwen2.5-coder:7b,
         gpt-4o-mini, claude-haiku-4-5, …)
       - a tooltip ("balanced", "huge — likely too slow on a
         laptop", …)
    Sorting also moves recommended → balanced → others to the
    front so the operator's eye lands on green first.

  * Compact legend below the chip wrap explains the colour
    code in place — no lookup needed.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-07 14:14:03 +02:00
flemming-it
88c5f2da1f fix(studio): Cmd+; opens Settings (Cmd+, is reserved by macOS) (v0.12.1)
Operator reported Cmd+, doing nothing. macOS reserves Cmd+,
for the native app-Preferences menu item which Flutter Desktop
doesn't wire up — the keystroke is captured by the OS before
the Shortcuts widget sees it.

Adds Cmd+; as the working alternative; keeps Cmd+, registered
too so a future Flutter version (or a native-menu setup) can
light it up. Tooltip on the sidebar gear icon now reads
"Settings (⌘;)" so the discoverable affordance also tells you
the shortcut.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-07 13:49:21 +02:00
flemming-it
4c4e2bb1eb feat(studio): "Explain this" + System AI in Settings (v0.11.0)
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>
2026-05-07 13:18:22 +02:00
flemming-it
060c8003d5 feat(studio): operator-mode pass — Store + audit detail + channel switcher (v0.10.0)
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>
2026-05-07 11:54:31 +02:00
flemming-it
18db6ff164 feat(doctor): update banner via HubAdmin.CheckUpdate (v0.9.0)
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>
2026-05-06 15:53:52 +02:00
flemming-it
521a8baab8 feat(ui): hub-endpoint settings dialog with persistence
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>
2026-05-05 22:12:47 +02:00
flemming-it
c94504247f feat(ui): warm-minimalism redesign — design tokens + 6 primitives + animated ∆
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>
2026-05-05 21:49:37 +02:00
flemming-it
a173eec1d0 feat: F∆I Studio MVP scaffold (Tier-2 desktop GUI)
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>
2026-05-05 14:19:39 +02:00