Commit graph

96 commits

Author SHA1 Message Date
flemming-it
eb447c6ec0 fix(studio): Welcome layout exception, drop awkward header, Store at slot 2 (v0.39.1)
Three fixes against the v0.39.0 user feedback.

- Layout exception "BoxConstraints forces an infinite
  height". `_PillarRow`'s wide-window path used
  `Row(crossAxisAlignment: CrossAxisAlignment.stretch)`
  inside a `Column` inside a `SingleChildScrollView` — Row
  inherits unbounded height, can't stretch to anything,
  Flutter throws. Wrapped the Row in `IntrinsicHeight` so
  the Row's height is bounded to the tallest child first.
  Same shape `_DocsRow`'s narrow-window path was triggering
  with `Wrap` + `SizedBox(width: double.infinity)` — replaced
  with a plain `Column` for that path; the wide path keeps
  the Wrap with the proper finite cardWidth.

- Dropped the "WIE ES ZUSAMMENPASST" / "HOW IT FITS
  TOGETHER" section header. Operator-feedback was that the
  label sounded like a question without an answer. The hero
  already establishes the page; the three Hub/Module/Flow
  cards are self-explaining beneath it. Header was visual
  clutter and the underlying ARB keys are gone.

- Reordered the sidebar: Welcome, **Store**, Doctor, Flows,
  Audit, Approvals. Store is the operator's daily-use
  surface and belongs above Doctor (which is a diagnostics
  page). Welcome stays slot 0 as the first-launch landing.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 00:42:39 +02:00
flemming-it
930e246649 feat(studio): Welcome page Phase C — live getting-started checklist (v0.39.0)
Final slice of the Welcome surface. Four steps that prove
the operator has the basic loop wired up; each step is
a live probe against the running hub, no manual ticks.

The four checked signals:

  1. System AI configured
     → `HubService.systemAiStatus().enabled`
  2. Public capability source added
     → `listMcpClients()` returns at least one entry
  3. Text module installed
     → `listModules()` contains a name starting with `text.`
  4. Saved flow run
     → any `flow.completed` event in the last 100 audit
       events

All four probes fire in parallel from `_OnboardingChecklist`'s
`_refresh()`. Failures stay false (catchError) so a hub that
is briefly unreachable doesn't blank the whole row — the
operator hits the explicit Refresh icon to retry.

UX:

- Renders between hero and pillars so the actionable path
  beats the educational content for screen real estate.
- Each row: status icon (○ pending / ✓ done) + title +
  one-line hint pointing the operator at the right Studio
  surface + status pill.
- Done rows strike-through the title and dim it; pending
  rows stay full-strength.
- When all four flip to done, an "All four steps complete"
  footer appears with a `Hide checklist` button. Clicking
  persists `welcome.checklist.dismissed = true` via
  SharedPreferences; the section is gone for good (we don't
  re-nag operators who chose a non-default path).

15 new ARB keys for the section header, body, four rows
plus done/pending pills, all-done footer, dismiss /
refresh / refreshing button labels.

Three-phase plan from `docs/landing-page-design.md` is now
fully shipped: Phase A scaffolding (v0.37.0), Phase B
embedded doc reader (v0.38.0), Phase C live checklist
(v0.39.0).

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 00:28:41 +02:00
flemming-it
3b07d340d5 feat(studio): Welcome page Phase B — embedded doc reader (v0.38.0)
Second slice of the Welcome surface. Operator-facing
documentation now lives inside Studio as bundled assets and
renders inline via a modal sheet — no browser, no external
link, air-gap-tauglich.

- Four operator-readable explainers under `assets/docs/`,
  each with an EN + DE pair:
    architecture[_de].md  — Hub / Module / Flow + how they fit
    security[_de].md       — sandbox model, declared perms,
                              operator ceiling
    audit[_de].md          — hash-chained log, WORM-1 mechanics,
                              `fai admin verify-events`
    flows[_de].md          — flow YAML, templating reference,
                              extract→summarize example
  These are short (≈ 300-500 words each), operator-shaped
  prose. Not copies of the architecture docs in
  fai_platform/docs/architecture/ — those are
  contributor-dense.

- pubspec.yaml declares `assets/docs/` so the markdown ships
  inside the Studio binary. Air-gap deployments read them
  with no network access.

- New `_DocReaderSheet` modal: 85 %-of-viewport bottom sheet,
  drag handle + title bar with the doc icon and close button,
  scrollable Markdown body styled to match Studio chrome.
  Loads `assets/docs/<slug>_<locale>.md` first, falls back to
  the EN file. Locale comes from
  `Localizations.localeOf(context)`.

- `_DocsRow` on the Welcome page sits below the trust-posture
  deck. Two-column grid on ≥ 640 dp, single column below.
  Each card is icon + title + one-line blurb + chevron;
  click opens the reader sheet for that slug.

- 12 new ARB keys for the docs section (header, blurb, four
  card titles + blurbs, close button, error message).
- 4 new icons reused: `account_tree_outlined` (architecture),
  `shield_outlined` (security), `verified_outlined` (audit),
  `alt_route_outlined` (flows).

Implements Phase B of `docs/landing-page-design.md`. Phase C
(live getting-started checklist with persistent state) is the
last remaining slice.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 00:24:31 +02:00
flemming-it
3a7d0e74ad feat(studio): Welcome page — Phase A scaffolding (v0.37.0)
First slice of the new sidebar destination outlined in
docs/landing-page-design.md. Static content only — embedded
docs (Phase B) and the live getting-started checklist
(Phase C) follow.

Phase A:

- New `WelcomePage` at `lib/pages/welcome.dart`, registered
  as sidebar slot 0 above Doctor. Default selectedIndex stays
  0, so a fresh launch lands on Welcome.
- Hero card: gradient backdrop matching the Today-Hero in the
  store, F∆I Platform headline, subtitle taken from CLAUDE.md
  ("deterministic workflow engine for AI-assisted document
  processing in regulated environments").
- Three-pillar row "Hub / Module / Flow" — operator-readable
  prose, not architecture-doc dense. Stacks to a column under
  640 dp window width so card text never gets cropped.
- Trust-posture deck — "Sandbox by default", "Tamper-evident
  audit log", "Air-gap ready". Same content that used to
  rotate as carousel slides in the store; reading them as a
  single deck with full prose works better than rotating
  through fragments.
- AppBar follows the same `titleSpacing: FaiSpace.xl`
  alignment as the Store so the title sits flush with the
  body padding.
- 13 new ARB keys for the page content (EN + DE).

Smoke test now expects "Welcome" in the navigation rail; the
existing `Doctor / Store / Flows / Audit / Approvals`
expectations stay unchanged.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 00:17:54 +02:00
flemming-it
711436b71d feat(studio): carousel size lock, store-actionable today stories, HTTPS MCP suggestions in settings (v0.36.0)
Three concrete changes against operator feedback that the
Today carousel was visually jumping when arrowed and
genre-mixing platform-architecture education with store-
actionable highlights.

- Carousel size pinned. The hero's outer Container picks up
  `BoxConstraints(minHeight: 240)` so a slide with one
  paragraph and a slide with three render at the same
  height. Prev/next no longer reflows the rest of the page.

- Today fallback stories trimmed and re-themed. The four
  shipped slides drop to three:
  - "Public sources" (DeepWiki / Semgrep one-click) — kept
  - "Three text modules already in the store" — new,
    points the operator at text.extract / text.summarize /
    text.translate in the grid below
  - "Try the extract → summarize flow" — new, points at
    flows/extract-summarize.yaml
  Architecture-education stories (sandbox model, hash-
  chained audit, air-gap posture) are gone from this surface
  — they belong on the Welcome page that
  `docs/landing-page-design.md` lays out.

- DeepWiki + Semgrep added to the Settings → MCP-Clients
  add-server suggestion-chip catalogue. Until now the chips
  were nine stdio servers that need Node + npx; the two
  HTTPS public sources only existed as one-click cards in
  the Today hero. Operators who dismissed the hero had no
  in-Settings path to find them. The new entries sit at the
  top of the catalogue with an explicit "Public HTTPS — no
  Node, no API key" descriptor and the same icons the Today
  hero already uses.

- `docs/landing-page-design.md` (new). Captures the design
  for a sidebar Welcome page that hosts the three-pillar
  intro, the trust-posture deck, the getting-started
  checklist, and an embedded-doc reader so operator-facing
  documentation stays inside Studio (`flutter_markdown`
  rendering of bundled `assets/docs/*.md`) instead of
  clicking out to a browser. Three-phase implementation
  plan: scaffolding, embedded docs, computed checklist.
  Build is gated on operator alignment; this doc is the
  alignment artefact.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 23:53:05 +02:00
flemming-it
a7e2eb101a feat(studio): drop Modules tab, fold its content into Store detail (v0.35.0)
Modules-as-a-tab was redundant with the Store after the
"Installed" filter and the federation work landed. Two pieces
of unique content kept it alive: the per-module declared
permissions list and the on-disk module directory. Both now
live inside the Store detail sheet.

Changes:

- Sidebar nav loses the Modules entry. `_pages` no longer
  carries a `'modules'` slot. The unused `import
  'pages/modules.dart'` is dropped from main.dart. Smoke test
  updated to skip the Modules-text expectation.

- Store detail sheet (`_StoreDetailSheet`) gains two new
  sections, rendered only when the entry is installed and the
  hub returned `ModuleDetail` for it:

    Declared permissions  →  same icon-prefixed list the
                              old Modules sheet shipped
                              (`net:`, `fs.read:`, `fs.write:`,
                              `env:`, `hub:`).
    Module directory      →  selectable mono path so the
                              operator can paste it into a
                              shell.

  An async `moduleInfo` fetch fires from `initState` only when
  `widget.item.installed` is true, so the regular
  not-installed detail-sheet path takes no extra round-trip.
  Failures stay silent — the sections just hide.

- The `FaiModuleSheet` widget stays intact. Cmd+K still uses
  it as a quick-info modal for installed modules; the
  longer-form Store detail sheet covers the same data plus
  the description, screenshots, and docs that operators
  reach for in the Store.

Three new ARB keys: `storeSectionPermissions`,
`storeSectionDirectory`, `storeSectionPermissionsNone`.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 14:57:51 +02:00
flemming-it
3b5fb027c3 feat(studio): de-jargonize, reveal-in-OS, copyable errors, today carousel, AppBar alignment, F∆I window title (v0.34.0)
Sweeping pass against six user reports collected this session.

1. "Capabilities ist nicht deutsch, Chain auch nicht."
   The DE locale still leaked English vocabulary. Replaced
   "Capabilities" → "Fähigkeiten" and "Chain" / "Hash-Chain"
   → "Kette" / "Hash-Kette" everywhere — store search hint,
   recommended-source body, federated toast, doctor summary
   chain row, modules panel summary, MCP / n8n hints, the
   approvals history blurb. Wire-level identifiers
   (`chain.reset`) stay as code.

2. "Bei Fehlern unten muss man die auch ins clipboard
   kopieren können." New `FaiErrorBox` widget: selectable
   monospace block with a small copy-to-clipboard icon
   button that flips to a checkmark for two seconds after
   click. Applied to the Doctor update banner output and
   the Settings channel toast — the two places long
   stderr / stdout lands.

3. "Öffnen bei Log kann es nicht öffnen. Audit-DB auch
   nicht. PID auch nicht."
   Cause: `SystemActions.openInOs` shells out to `open` /
   `xdg-open` on file paths the OS has no default handler
   for (SQLite DB, PID file, log without an .ext that
   binds). New `revealInOs` uses `open -R` on macOS,
   `explorer /select,` on Windows, and the parent
   directory via `xdg-open` on Linux. Doctor's path rows
   carry an `isDirectory` flag that routes through the new
   `openOrReveal` so files reveal in Finder / Explorer
   instead of failing silently.

4. "Oben im Store könnte man diesen Redaktionshinweis auch
   so bauen, dass man mit pfeil nach rechts links auch
   weitere anzeigen kann."
   The Today-Hero became a carousel. Curated fallback
   list grew from one entry to four (public sources, the
   sandbox-by-default permission story, the hash-chained
   audit story, the air-gap-ready single-binary pitch).
   Hero gets prev / next chevrons plus a dot indicator
   when the current snapshot has more than one slide.
   Operator-accepted stories stay single — the carousel
   collapses when there's only one to show.

5. "Ich fände es schöner wenn rechts und links im Store
   die Abstände konsistent sind, das Reload-Symbol rechts
   ist zu weit rechts und Store links auch nicht bündig."
   AppBar now has `titleSpacing: FaiSpace.xl` so the
   title's left edge sits flush with the body's left
   padding (24 dp), and the trailing `SizedBox` after the
   reload icon shrunk so the icon's outer edge meets the
   right edge of the rightmost grid card.

6. "Oben der Titel zeigt fai_studio an, das sollte F∆I
   Studio sein." The OS window title was the
   pubspec-derived "fai_studio". Macos/Linux/Windows
   runners now hard-code "F∆I Studio" (with the U+2206
   triangle escape so the C++ source stays ASCII). macOS
   bundle name and display name lifted out of the
   PRODUCT_NAME variable for the same reason.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 14:48:33 +02:00
flemming-it
22a851b7e2 fix(studio): doctor i18n + light-mode AppBar contrast (v0.33.1)
Two reports:

1. "In Diagnose ist viel nicht übersetzt, wie pending, chain
   etc." The Doctor summary strip (Modules / Approvals / Audit
   / Services tiles) and the modules panel still rendered
   English wire labels: "pending", "chain", "declared",
   "loaded", "empty", "attention", "No pending approvals",
   "No host services declared", and the matching "{n} modules
   · {m} capabilities" / "{n} approvals awaiting review"
   strings.

   Fix: 13 new ARB keys covering the summary tiles, modules
   panel, and services panel; doctor.dart now reads them via
   AppLocalizations. German operators no longer see English
   labels on Diagnose.

2. "Im Light mode ist der kontrast zur überschrift falsch,
   store ist weiß auf weiß." The AppBar title rendered
   white-on-white in light mode.

   Cause: `appBarTheme.titleTextStyle = textTheme.headlineSmall`
   passed a TextStyle built fresh from GoogleFonts.inter(...)
   with `color: null`. Material's "merge foregroundColor at
   draw time" path didn't always populate it — depended on
   build configuration. The colour fell through to whatever
   the surrounding DefaultTextStyle had, which on the
   light-mode AppBar was the surface colour.

   Fix: bake the foreground colour straight into the title
   style via `copyWith(color: scheme.onSurface)`. Also pin
   `iconTheme: IconThemeData(color: scheme.onSurface)` so
   actions-row icons get the same treatment defensively.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 13:43:17 +02:00
flemming-it
de22791e86 feat(studio): plain-language store, bottom-anchored ask bar (v0.33.0)
The store had three structural problems flagged in user
feedback:

1. "Lernkurve zu hoch — alle bridge, debug, alpha, planned,
   keiner weiß was das heißt." Wire-level enum values bled
   through into the UI: `bridge`, `debug`, `published`,
   `alpha`, `planned`. Operators don't share the data model.

2. "Filter / Kategorien sollten oben in die Leiste neben Store
   zum ausklappen, der Body soll übersichtlicher werden." A
   chip-row plus category strip plus result-count line ate a
   full row of viewport on every screen.

3. "Suche passt besser an den unteren Bildschirmrand." The
   chat-style ask bar belongs at the bottom — Claude / ChatGPT
   / Slack pattern — not at the top where it competes with
   the Today hero.

Plus: "Es gibt noch ganz viele overflows in den Beschreibungen."

Changes:

- Plain-language display names. New ARB keys for category
  labels (Connectors / Sample modules / AI models / Storage /
  Channels / Authentication / Orchestration / …) and updated
  status values (stable / experimental / coming soon). Wire
  ids stay in StoreEntry, RPCs, seed.yaml — only the rendered
  pills and dropdowns get translated. Helpers
  `_categoryDisplayName` and `_statusDisplayName` keep the
  mapping in one place.

- Toolbar moves into the AppBar. The new `_CategoryDropdown`
  hosts the category picker as a popup-menu with localised
  labels; the existing `_FilterButton` lives next to it; the
  reload icon stays where it always was. The body no longer
  carries any chip strip or result-count line.

- Bottom-anchored `_AskBar`. The Scaffold body becomes a
  Column of `Expanded(scrollable content)` plus a pinned
  composer row at the foot of the viewport, with a top
  divider matching the chat-input pattern of modern AI
  assistants. AI answers now render at the top of the scroll
  (above the Today-Hero) so the operator sees them right
  after submitting the question at the bottom.

- Overflow sweep on descriptions. Today-Hero header row
  becomes a Wrap (badge + deck flow naturally on narrow
  windows), the title is bound to 3 lines, the body to 6.
  Card category text gets `maxLines: 1, ellipsis` in both
  StoreCard and FeaturedTile. Recommended-source card title
  gets the same treatment.

- Better toast for the "added but zero capabilities" path.
  When MCP discovery succeeds but returns no tools (typical
  Streamable-HTTP servers without Mcp-Session-Id support, or
  servers that need `notifications/initialized`), the toast
  now explains the situation in plain language and points
  the operator at Settings → MCP Clients to retry. Old
  pluralised toast still fires when N > 0.

Hub-side `notifications/initialized` for HTTP MCP and
Mcp-Session-Id support are out of scope for this commit;
tracked as a separate fai_hub follow-up.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 13:37:35 +02:00
flemming-it
c7869870c4 feat(studio): plain-language ask bar + collapsible filter + AI search (v0.32.0)
Three changes against the user feedback "der text ist zu
zerhackt, die filter-leiste nimmt zu viel sicht weg, llm-suche
wenn das geht":

1. Today-Hero copy rewritten in flowing prose. The fallback
   story drops the code-fence templates ("`mcp.<server>.<tool>`")
   and reads as a single arc per language. Same trust model,
   gentler register.

2. Filter row collapses behind a single `_FilterButton` with a
   "Filter · N active" count badge. Status / Source / Installed
   chips now live inside `_FilterDialog` — invisible until the
   operator wants them, recovers a full row of viewport. The
   category strip stays inline because category is the most
   common cut and benefits from being one click away.

3. New `_AskBar` replaces the old single-line `_SearchField`:

   - Multi-line input (1-4 lines auto-grow) so questions don't
     overflow horizontally.
   - Cmd+Enter / Ctrl+Enter submits; bare Enter inserts a
     newline so the operator can write multi-line questions
     naturally.
   - Heuristic question detector (ends in `?` or ≥4 words)
     routes the submit to the System-AI's askAi RPC instead of
     the substring search. Live keystrokes still keyword-search
     for short queries; question-shaped input holds the grid
     steady until submit so the visible result set doesn't
     wipe with every space.
   - The LLM is asked for a strict-JSON ranking of up to 5
     modules with a one-phrase reason each. Unknown module
     names are dropped silently — operators must not see
     hallucinated entries.
   - The new `_AiAnswerCard` renders the LLM's answer plus the
     ranked match list above the grid; the grid filters to
     just those names so the answer and the visible cards
     stay coherent. Clearing the question collapses everything
     back.
   - Falls back to plain keyword search when System AI is off
     (with an explanatory hint instead of the AI hint).

The AI path uses the System-AI the operator already configured
in Settings; same privacy mode, same audit-log trail. Nothing
new leaves the hub.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 13:21:40 +02:00
flemming-it
b0f09260e1 fix(studio): store overflow + visual clutter (v0.31.1)
Two issues from the user feedback after v0.31.0:

1. RenderFlex overflow in the store body. The inner Column
   wrapped the editorial chrome (Today, Recommended,
   Featured) plus an Expanded grid; on small windows the
   chrome alone exceeded the available height and the grid
   got squeezed past zero, painting the yellow-and-black
   stripe.

   Fix: wrap the inner Column in a SingleChildScrollView so
   chrome and grid scroll as one continuous surface
   (App-Store / Play-Store behaviour). The grid switches to
   `shrinkWrap: true` + `NeverScrollableScrollPhysics()` so
   it doesn't try to claim its own viewport.

2. "Der Store ist zu unaufgeräumt, ich fühle mich
   erschlagen." — three editorial cards stacked above the
   filter row + grid was too much.

   Visual cleanup:
   - The recommended-source quick-add chips are now inlined
     into the Today hero's footer (same row as the CTA).
     The standalone `_RecommendedSourcesStrip` only fires in
     the corner case where Today is dismissed AND no
     federation exists yet.
   - The Featured strip no longer competes with Today —
     it only renders once Today has been dismissed. Today
     plays the editorial-hero role; Featured is the
     fallback editorial surface.
   - Provenance pill ("native") is dropped on native cards.
     Native is the default and doesn't earn a badge — only
     foreign code (mcp / n8n) gets the provenance pill, the
     same way "verified" is opt-in in commercial app stores.

Net: at most one editorial card above the grid at any time.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 13:06:54 +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
fe933a713f feat(studio): editorial Today hero on the store (v0.30.0)
App-store research shows that the most loved discovery
surfaces — Apple's Today tab being the canonical example —
work because editors tell stories instead of stacking
algorithmic recommendations. F∆I has no surveillance budget
and no monetization pressure, so we lean fully into editorial
curation.

This commit adds `_StoreTodayHero`, the first surface a
browsing operator sees:

- Single curated story per release, kept const in
  [_kCurrentTodayStory] so the narrative is reviewable in code
  review and ships in the audit log via the binary hash. No
  CMS, no network, no surveillance.
- Bilingual content shipped inline (`titleEn`/`titleDe`,
  `bodyEn`/`bodyDe`) so KRITIS deployments don't need a
  translation backend.
- Gradient backdrop with hero icon, deck, narrative paragraph,
  optional CTA. Visual treatment matches Apple Today's
  hierarchy: badge → headline → body → action.
- Auto-hides whenever a filter is active so a purposeful
  search isn't pushed below the fold.
- Per-session dismiss button — no permanent suppression, the
  next Studio launch shows it again so a release-bumped story
  has a chance to be seen.

Current story (Studio v0.30.x) directs operators to the new
recommended-sources strip, closing the loop between editorial
context and one-click action.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 12:44:39 +02:00
flemming-it
581ebbeaf7 feat(studio): store-side source filter, provenance pills, one-click recommended sources (v0.29.0)
The Store is now the centre of capability discovery — provider
configuration moves out of Settings into the Store itself.
Three changes work together:

1. Source filter chips next to the existing status chips:
   All sources / Native / MCP / n8n. Applied client-side after
   the hub returns results so toggling is instant. The result
   count and the "Clear filters" reset both account for the
   source filter too.

2. Per-card provenance pill (`_ProvenancePill`): shows whether
   each entry is native, mcp · <provider>, or n8n · <provider>
   so the operator can triage the source at a glance — same
   role the "verified" badge plays in commercial app stores.

3. `_RecommendedSourcesStrip` replaces the older
   `_FederationNudge`. Renders curated public MCP servers
   (DeepWiki, Semgrep) as one-click cards with an inline
   `[+ Add]` button — no Settings detour, no form. Both servers
   are HTTPS Streamable-HTTP, no API key, no subprocess.
   Useful AI was considered but its 340+ tools would drown the
   index — kept out of the curated list. The strip auto-hides
   the moment any federated entry exists.

Removed the now-orphaned storeFederationNudge* ARB keys.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 12:25:59 +02:00
flemming-it
b5a4c74c4b feat(studio): Flows + Module sheet + System-AI editor i18n (v0.28.0)
- Localize Flows page: app-bar title and reload tooltip,
  hub-unreachable / no-saved-flows empty states, run-flow input
  dialog (title with flow name, description, hint, Cancel /
  Run), running dialog (title, "Flow running…" status, no-output
  message, Close button), flow-card Run button.
- Localize the module-sheet bottom sheet: failed-to-load text,
  Capabilities and "Declared permissions" section headers,
  no-permissions placeholder copy.
- Localize the System-AI configuration dialog: title, intro
  paragraph, provider dropdown label, endpoint label, API-key
  env-var label (required vs optional) and disclaimer, privacy
  mode header and three options (Off / Redacted / Full) plus
  their descriptions, test-result panel ("Connection ok" /
  "Connection failed", "Reply: …" prefix), model picker (label,
  hint fallback, helper text with Ollama variant, Refresh /
  Pull / Pulling… buttons, list errors and empty states),
  hardware banner ("Detected: …" + " · curation reviewed …"
  suffix), suitability legend (recommended / balanced / small /
  large / huge / unknown), cache row with pluralized count and
  Clear button, cache cleared / clear-failed toasts, pull-empty
  / pull-failed errors. Suitability label moved off a getter
  onto a `labelFor(BuildContext)` method so it can read the
  current locale.

Provider preset descriptions and modelHint strings stay in
English in `_ProviderPreset.all` — they're tightly coupled to
the wire-protocol identifiers and would require a runtime
factory rebuild rather than a const list.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 02:59:08 +02:00
flemming-it
e2b2639a86 feat(studio): Store + Cmd+K palette i18n (v0.27.0)
- Localize Store page: search hint, category and status filter
  chips (All / Published / Alpha / Planned / Installed),
  pluralized result count, hub-unreachable / no-matches empty
  states, featured strip header, store-card pills (installed /
  update / version), Install / Update / Details / Read docs /
  Uninstall / Not installable buttons, install-progress dialog,
  uninstall confirm dialog, install / uninstall toasts,
  open-browser failure toast.
- Localize detail-sheet sections: Tags, Required capabilities,
  Required host services, Screenshots, Documentation, Source.
- Localize docs panel: Load documentation button, fetching
  spinner copy, Open repository in browser button, friendly
  error mapping (not_found / no_docs / auth / network / parse).
- Localize Cmd+K palette: search hint, group labels (Pages /
  Modules / Store / Flows), keyboard hint footer, indexing /
  no-matches states, dynamic-hit copy ("installed module ·
  v{version}", "saved flow", "uncategorized"). Static page
  hits now flow through the localized "Pages" group; Settings
  entry uses the localized label and hint.

Status pill values ("published", "alpha", "planned") stay as
data-derived English strings since they map directly to the
store-index `status:` enum.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 02:49:18 +02:00
flemming-it
d25b4c87ae feat(studio): MCP suggestions + Approvals/Audit/Settings i18n (v0.26.0)
- Add curated catalogue of 9 official Anthropic MCP servers as
  click-to-prefill chips in the Add-MCP-server dialog. Operator
  still confirms via "Add + discover"; nothing is auto-spawned,
  preserving the regulated-environment trust model.
- Localize Approvals page: pending/history empty states, status
  pills, payload preview, approve/reject toasts, reject dialog,
  history detail dialog, expires-in pill.
- Localize Audit page: filter chips, clear-log dialog and toasts,
  no-events / hub-unreachable empty states, live-status bar with
  pluralized event count, hash-chain-verified badge, System AI
  panel (cached pill, latency, regenerate tooltip, asking
  state), event-detail dialog actions.
- Localize Settings dialog: hub-endpoint section, channel
  blurb + popup-menu items, System AI panel headers and
  off-blurb, MCP/N8N panel headers + pluralized counts +
  refresh/add/remove tooltips, Add-MCP and Add-n8n dialogs
  (intro, suggestions label, all field labels and hints, save
  buttons, toasts).

Field labels that map directly to JSON keys (event_id, flow,
step, module, provider, endpoint, model) stay English by design
— they're technical identifiers, not UI copy.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 02:38:11 +02:00
flemming-it
349619d68e feat(studio): Doctor + Modules localized + sparse-store federation nudge (v0.25.0)
Two threads.

Translation expansion: every visible string on the Doctor
page (section headers, status text, button labels, daemon-
control card, daemon-files panel, update banner) and the
Modules page (recent-activity strip, capabilities label,
uninstall dialog + toast) flips between DE and EN with the
sidebar toggle. Adds ~50 ARB keys split between
`app_en.arb` / `app_de.arb`. Pluralised + parametrised
strings (`{n} events verified`, `Running on {name}: {endpoint}`)
use the standard ICU placeholder syntax so future locales
slot in without code changes.

Sparse-store federation nudge: the Store page renders an
inline banner above the grid when the operator has zero
federated entries — single biggest store-fullness lever is
configuring an MCP server / n8n endpoint, so the banner
says exactly that and the button opens Settings straight
to the editor. Banner dismisses automatically the next
render after a federated entry appears.

Coverage stand for translation: Doctor + Modules + sidebar
+ page titles + nav + Cmd+K labels are bilingual. Settings
dialog, Approvals cards, Audit drilldown, MCP/n8n editors,
Store search hint + filter chips remain English-literal.
The infrastructure (ARB plumbing, generator hookup,
locale-notifier) means each follow-up surface is a
one-line ARB edit + one call-site swap.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 01:39:42 +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
fb7352d182 fix(studio): live-search exception, daemon-card width, featured tile size; add Start-hub button (v0.20.1)
Three reported bugs and one onramp gap:

- Store live-search threw `setState() callback returned a
  Future`. The `=>` body of `_runSearch` evaluated to the
  Future returned by `_load()`. Switched to a block body so
  the closure resolves to void; the future itself is still
  awaited by the FutureBuilder.

- Doctor's Daemon-control card was visibly narrower than its
  siblings — its only stretching child was a Wrap of small
  buttons. Wrapped the content in `SizedBox(width: infinity)`
  so the card fills the section's width like every other
  panel. Card now leads with the live `running on local:
  127.0.0.1:50051` status row + status pill so operators see
  state at a glance, then offers Restart / Start (when
  stopped) / Stop / Status.

- Featured tiles were 320×152 against the regular grid's
  ~360×168, so the editorial picks looked subordinate to
  the dense grid. Bumped to 480×220 with bigger icon, larger
  title, larger tagline so the strip reads as a hero band.

- ConnectionPill in the sidebar now offers an inline "Start
  hub" button when the daemon is unreachable. Spawns
  `fai daemon start` via SystemActions; the shell-level
  health poll picks the daemon up on its next tick. Closes
  the chicken-and-egg for Windows users who can't open a
  shell to bootstrap.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-07 21:27:09 +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
93926741b1 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>
2026-05-07 18:52:48 +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
6e3c6df200 fix(studio): always include year in non-today audit timestamps (v0.14.2)
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>
2026-05-07 17:06:21 +02:00
flemming-it
1e0bd0b51b fix(studio): audit timestamps show date when not from today (v0.14.1)
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>
2026-05-07 16:44:39 +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
5b6641567c feat(studio): system-ai editor — test-without-save + model picker (v0.13.0)
Three operator-facing improvements driven by Stefan's testing:

  * **Test Connection** runs against the form's *current* values
    and no longer requires saving first. Previously we save-then-
    test which caused a "system AI is disabled" message when the
    form's model field was still empty. Now the test prompt
    travels with the request body so unsaved tweaks can be
    validated.

  * **Model picker** replaces the free-text Model field. A
    Refresh button calls HubAdmin.ListSystemAiModels (provider's
    `/v1/models`); installed models show up as clickable chips
    that fill the field with one tap. The text input stays for
    cases where the operator already knows the id.

  * **Pull** button (Ollama only) downloads a model via
    `/api/pull`. Type `gemma3:4b`, click Pull, spinner, then the
    new model lights up in the chip list. No more "switch to a
    terminal to ollama-pull" detour.

Helper text updates for the empty-model case ("Required: pick
from the list (Refresh) or type one. Use Pull to download from
Ollama.") so a fresh operator never wonders why Save / Test
errors out.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-07 14:02:44 +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
15ea403c86 feat(studio): in-place System AI editor (v0.12.0)
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>
2026-05-07 13:41:11 +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
753db4fc60 feat(ui): Flows page (5th destination) + keyboard shortcuts
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>
2026-05-05 23:35:48 +02:00
flemming-it
2f32023322 fix(ui): theme toggle is now actually reactive
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>
2026-05-05 23:00:45 +02:00
flemming-it
20230684a9 fix(ui): visible ∆ pulse + drop internal codename from version line
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>
2026-05-05 22:51:19 +02:00
flemming-it
d5631177e0 fix(ui): stat-tile overflow + visible build version
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>
2026-05-05 22:41:26 +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