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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Sidebar:
- New active-channel pill below the connection pill,
color-coded so production deployments look visibly
different from local-dev. Click opens Settings; tooltip
explains the channel taxonomy. Polled in lockstep with the
health probe so it tracks CLI-driven `fai channel switch`.
Store:
- Featured strip above the main grid renders editorial
picks as bigger hero tiles with a subtle gradient
backdrop, version + status pills, and a one-click
Install. Hidden the moment the operator types in the
search box or picks any filter — the result list is the
answer they want, not editorial chrome.
StoreItem now carries the `featured` flag so the page can
filter without an extra round-trip.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two Phase-2 Store improvements that operators feel
immediately:
- Per-module update badge: store cards compare the operator's
installed version against the index's `best_version`, raise
an "update" pill + "Update to vX.Y.Z" button when newer is
available. Result: a single glance at the Store reveals
what's behind. The page now fetches `listModules` in
parallel with the search so the comparison is on the
current snapshot.
- Inline README rendering: detail sheet has a new
Documentation section. "Load documentation" button (lazy —
no network until clicked) calls FetchModuleDocs and renders
the markdown via flutter_markdown, with the operator's
theme colors and link-tap routed through SystemActions.
Auth / not-found / network failures fall back to a
friendly fix-hint panel + "Open repository in browser"
button so the operator never hits a dead end.
Adds flutter_markdown ^0.7.7 to pubspec; the package is
maintenance-only upstream but is the only mature option
for Material-themed markdown right now. Easy to swap to
markdown_widget if/when we need GitHub-flavored extras.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
UI parity for operators who never touch a CLI, plus a Store
that reads more like an app store.
Settings dialog:
- Channel rows gain a popup menu: Connect / Make active /
Enable autostart / Disable autostart. "Connect" still
re-points Studio's wire; "Make active" actually writes
~/.fai/current-channel via `fai channel switch`.
- Inline output panel surfaces the spawned binary's stdout
on success or stderr on failure, so operators see what
happened without opening a terminal.
Store page rewrite:
- Big top search bar with a clear button. Live filter on
every keystroke.
- Horizontal category strip auto-populated from the index;
segmented status row (All / Published / Alpha / Planned),
Installed-only chip, result count.
- Grid of cards that reflows to fit the viewport — replaces
the previous single-column list. Each card shows
category-aware icon, version, status, tagline preview, and
a one-click Install (or Details for installed / planned).
- Per-module detail sheet renders the full bilingual
description with a DE/EN toggle, separate Required-
capabilities + Required-host-services sections, repo link,
Read-docs button. Install + Uninstall live at the bottom.
- StoreItem and HubService.searchStore now carry the German
tagline + description so the locale toggle has something
to switch to.
SystemActions extended with `faiChannelSwitch`,
`faiDaemonEnable`, `faiDaemonDisable` so Settings can spawn
the right CLI without each call site reimplementing the
`fai` resolution rules.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
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>