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>
This commit is contained in:
flemming-it 2026-05-08 23:53:05 +02:00
parent a7e2eb101a
commit 711436b71d
5 changed files with 227 additions and 30 deletions

178
docs/landing-page-design.md Normal file
View file

@ -0,0 +1,178 @@
# Welcome / Landing-page design
Status: **Design draft.** Not built yet.
## Why we need it
The Today-Hero in the Store mixed two genres of editorial
content:
- **Operator-actionable**: "DeepWiki is two clicks away",
"text.summarize is in the grid", "try the extract→summarize
flow". These belong in the Store carousel — they point at
store / federation items the operator can act on.
- **Architecture education**: "every module is sandboxed",
"the audit log is hash-chained", "the hub fits in one
binary, no Docker". These are *what F∆I is* — they belong
on a Welcome page that introduces the platform on first
launch and stays available afterwards as a reference.
The operator feedback that made this distinction explicit:
> "interessante store einträge oder auch mcp etc zeigen und
> nicht einfach etwas übers system. Stattdessen brauchen wir
> als erstes im system eine art landing page die genau das
> macht und auch zu doku verlinkt, aber diese auch in das
> system optisch integriert und nicht einfach nach außen
> verlinkt."
The architectural-education stories already left the Store
carousel in the same release. The Welcome page is where they
land.
## Page shape
A new top-level Studio surface, accessed from the sidebar
above Doctor. First-launch operators land here automatically;
returning operators can come back any time.
```
┌──────────────────────────────────────────────────────────┐
│ AppBar: Welcome │
├──────────────────────────────────────────────────────────┤
│ ┌────────────────────────────────────────────────────┐ │
│ │ HERO │ │
│ │ F∆I Platform │ │
│ │ Deterministic workflow engine for AI-assisted │ │
│ │ document processing in regulated environments. │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ ┌──── Three-pillar row (Hub / Module / Flow) ────┐ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ HUB │ │ MODULE │ │ FLOW │ │ │
│ │ │ icon │ │ icon │ │ icon │ │ │
│ │ │ One bin- │ │ Does one │ │ YAML │ │ │
│ │ │ ary that │ │ thing, │ │ chains │ │ │
│ │ │ runs the │ │ sandboxed│ │ modules │ │ │
│ │ │ flows │ │ WASM │ │ together │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌──── Trust posture ─────────────────────────────┐ │
│ │ 🛡 Sandbox by default │ │
│ │ Every module declares fs / net / env perms; │ │
│ │ the hub enforces them. Click "Doctor → │ │
│ │ Modules" to see live permissions per module.│ │
│ │ │ │
│ │ ⛓ Tamper-evident audit log │ │
│ │ Every event hash-chained; Doctor verifies │ │
│ │ end-to-end on every load. WORM-1 promise. │ │
│ │ │ │
│ │ ✈ Air-gap ready │ │
│ │ One binary, SQLite state, no Docker, no │ │
│ │ external services. Once a module is │ │
│ │ installed, the flow runs without further │ │
│ │ network access. │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌──── Getting-started checklist ─────────────────┐ │
│ │ ☐ Configure System AI (Settings → System AI) │ │
│ │ ☐ Add a public MCP source (Store → DeepWiki) │ │
│ │ ☐ Install your first text module │ │
│ │ ☐ Run the extract → summarize sample flow │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌──── Documentation, embedded ───────────────────┐ │
│ │ Architecture map · Module experience · │ │
│ │ Flow composition · Compliance posture │ │
│ │ → click opens an in-app reader, not a browser │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
```
## Optical integration of docs
The user constraint:
> "diese auch in das system optisch integriert und nicht
> einfach nach außen verlinkt"
So clicking a documentation card does **not** open a browser.
Instead a `_DocReaderSheet` (modal bottom-sheet, same shape as
`_StoreDetailSheet`) renders the markdown inline using the
existing `flutter_markdown` package — same one the Store uses
for module READMEs. The doc source is bundled with Studio as
`assets/docs/*.md`, kept in sync via a build step that copies
the relevant files from `fai_platform/docs/`.
Pros:
- Air-gap-tauglich (no network call to read docs)
- Operator stays in Studio context — Cmd-Tab to a browser
breaks flow
- Same look-and-feel as module-doc rendering
- Bilingual: each doc has a `_de.md` sibling next to `.md`
Cons / tradeoffs:
- Studio binary grows by the size of bundled docs (a few
hundred KB)
- Doc-update lifecycle: a Studio release ships a fixed
snapshot. Live-fetched-from-hub is a Phase 1 question.
## Getting-started checklist
Stateful — the page tracks which items the operator has
completed and persists state to `~/.fai/studio/onboarding.json`
(same shape as theme/locale persistence in `hub.dart`). Each
checkmark is computed:
- ✓ System AI configured: `HubService.systemAiStatus().enabled`
- ✓ Public MCP source added: any
`mcp.<server>.<tool>` in store
- ✓ First text module installed: any installed module
whose name starts with `text.`
- ✓ Sample flow run: any `flow.completed` event in audit
log naming `extract-summarize.yaml` (or any flow that
uses both `text.extract` and `text.summarize`)
Operator can dismiss the checklist once everything is checked.
## Sidebar placement
The Welcome page becomes the new sidebar default — index 0,
above Doctor. First-launch experience hits it; afterward
operators most often go to Store / Audit, but the home icon
stays one click away.
`labelOf(context)` for the Welcome page returns
`l.navWelcome` ("Welcome" / "Willkommen"). New ARB keys:
`navWelcome`, plus the section-header strings (already mostly
covered by existing `doctorChain*` / `doctorPathLog` keys
that we can reference from the doc-card descriptions).
## Implementation phases
Big enough that a single commit doesn't make sense. Three
phased PRs:
1. **Phase A** — sidebar + scaffolding + hero + three-pillar
row + trust-posture row. No docs yet, just the static
content with hard-coded strings (with i18n). Lands
without the embedded-doc reader.
2. **Phase B** — bundled markdown docs + `_DocReaderSheet`.
Pick 4-5 source docs from `fai_platform/docs/architecture/`
that map to operator concerns (security, host-services,
flow-experience, compliance-gaps). Add a build script that
syncs them into `assets/docs/`.
3. **Phase C** — getting-started checklist with computed
state. Adds the persistence layer + the audit-log
inspection helper.
## Out of scope
- A "tutorial mode" that walks the operator through the
steps interactively. Editorial checklist + concise
explanations is enough for v1; tutorial state is a Phase
1+ idea.
- Live-fetched docs from `hub.fetch_doc(...)`. Air-gap
posture requires bundled docs.
- Per-deployment branding (custom logo / color). Not a
KRITIS need.

View file

@ -25,7 +25,7 @@ import 'widgets/widgets.dart';
/// Studio's own build version. Bump on every UI commit so the
/// running app self-identifies visible in the sidebar header
/// and quick-glance proof that you're seeing the current build.
const String kStudioVersion = '0.35.0';
const String kStudioVersion = '0.36.0';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

View file

@ -2802,6 +2802,12 @@ class _ScreenshotPlaceholder extends StatelessWidget {
/// recompiling Studio see docs/today-pipeline.md. When the
/// operator accepts a story, that single story becomes the
/// active item and the carousel collapses to a single slide.
///
/// **Editorial discipline:** every entry here points at a
/// concrete store / federation item the operator can act on,
/// not abstract platform philosophy. Architecture education
/// (sandbox model, hash-chained audit, air-gap posture) lives
/// on the dedicated Welcome page, not in the store carousel.
const List<TodayStoryData> _kFallbackTodayStories = <TodayStoryData>[
TodayStoryData(
badgeEn: 'PUBLIC SOURCES',
@ -2818,45 +2824,31 @@ const List<TodayStoryData> _kFallbackTodayStories = <TodayStoryData>[
cta: TodayCta.openSettings,
),
TodayStoryData(
badgeEn: 'SANDBOX BY DEFAULT',
badgeDe: 'SANDBOX VON ANFANG AN',
titleEn: 'Every module declares what it can touch',
titleDe: 'Jedes Modul deklariert, worauf es zugreifen darf',
badgeEn: 'TEXT MODULES',
badgeDe: 'TEXT-MODULE',
titleEn: 'Three text modules already in the store',
titleDe: 'Drei Text-Module sind schon im Store',
bodyEn:
'F∆I modules ship with an explicit permission list — network endpoints, files, environment variables. The hub enforces it; nothing reaches outside the sandbox without the operator approving it. Click any installed module to see exactly which permissions it asked for.',
'text.extract pulls plain text from PDFs and DOCX. text.summarize turns long material into a short briefing via your configured System AI. text.translate moves text between languages. All three install in one click from the grid below — search for "text" to find them.',
bodyDe:
'F∆I-Module bringen eine explizite Berechtigungsliste mit — Netzwerk-Endpunkte, Dateien, Umgebungsvariablen. Der Hub setzt sie durch; ohne Operator-Freigabe verlässt nichts die Sandbox. Klicke ein installiertes Modul, um seine genauen Berechtigungen zu sehen.',
'text.extract holt Plaintext aus PDFs und DOCX. text.summarize verdichtet lange Texte über die konfigurierte System-AI zu einem Briefing. text.translate übersetzt zwischen Sprachen. Alle drei lassen sich aus dem Grid unten mit einem Klick installieren — suche nach „text".',
ctaLabelEn: '',
ctaLabelDe: '',
icon: Icons.shield_outlined,
icon: Icons.menu_book_outlined,
cta: TodayCta.none,
),
TodayStoryData(
badgeEn: 'TAMPER-EVIDENT AUDIT',
badgeDe: 'MANIPULATIONSSICHERES AUDIT',
titleEn: 'Every event leaves a hash-chained trace',
titleDe: 'Jedes Ereignis hinterlässt eine hash-verkettete Spur',
badgeEn: 'SAMPLE FLOW',
badgeDe: 'BEISPIEL-FLOW',
titleEn: 'Try the extract → summarize flow',
titleDe: 'Probier den Extract → Summarize-Flow',
bodyEn:
'Flow runs, install / uninstall actions, approval decisions — all of them write into a hash-chained audit log. The Doctor page verifies the chain end-to-end on every load; if a single byte gets touched, you see it. Audit-grade by construction, not by policy.',
'The bundled `flows/extract-summarize.yaml` chains text.extract into text.summarize so a PDF in one end produces a short briefing at the other. Open the Flows tab, hit Run, point it at any document — the result lands as plain text plus an audit-log trail of which modules ran with which inputs.',
bodyDe:
'Flow-Läufe, Installationen und Deinstallationen, Freigabe-Entscheidungen — alles wird in ein hash-verkettetes Audit-Log geschrieben. Die Diagnose-Seite verifiziert die Kette bei jedem Laden komplett; ein verändertes Byte ist sofort sichtbar. Audit-tauglich qua Konstruktion, nicht qua Policy.',
'Der mitgelieferte Flow `flows/extract-summarize.yaml` reicht text.extract an text.summarize weiter — vorne kommt ein PDF rein, hinten kommt ein kurzes Briefing raus. Flows-Tab öffnen, Run klicken, beliebiges Dokument auswählen; das Ergebnis ist Klartext plus Audit-Spur welcher Modul-Schritt mit welchen Eingaben gelaufen ist.',
ctaLabelEn: '',
ctaLabelDe: '',
icon: Icons.shield_outlined,
cta: TodayCta.none,
),
TodayStoryData(
badgeEn: 'AIR-GAP READY',
badgeDe: 'AIR-GAP-TAUGLICH',
titleEn: 'One binary, no Docker, no external services',
titleDe: 'Ein Binary, kein Docker, keine externen Dienste',
bodyEn:
'The whole hub fits in a single binary that runs on Linux, macOS and Windows. SQLite for state, no message broker, no background services to babysit. Once a module is installed, the flow it powers runs without further network access — perfect for regulated environments where outbound calls have to be justified.',
bodyDe:
'Der gesamte Hub steckt in einem einzigen Binary für Linux, macOS und Windows. SQLite für den State, kein Message-Broker, keine Hintergrunddienste, die gepflegt werden müssen. Sobald ein Modul installiert ist, läuft der Flow, der es nutzt, ohne weiteren Netzwerkzugriff — ideal für regulierte Umgebungen, in denen jeder ausgehende Call begründet werden muss.',
ctaLabelEn: '',
ctaLabelDe: '',
icon: Icons.rocket_launch,
icon: Icons.account_tree_outlined,
cta: TodayCta.none,
),
];
@ -2917,6 +2909,12 @@ class _StoreTodayHero extends StatelessWidget {
final ctaLabel = isDe ? story.ctaLabelDe : story.ctaLabelEn;
return Container(
width: double.infinity,
// Min-height keeps the carousel slides at a consistent
// size so the surrounding layout doesn't jump when the
// operator clicks the prev/next chevrons. Sized to fit
// the longest story ( 6 body lines + title + CTA + chip
// row) without leaving empty space on shorter slides.
constraints: const BoxConstraints(minHeight: 240),
padding: const EdgeInsets.fromLTRB(
FaiSpace.xl,
FaiSpace.lg,

View file

@ -1403,6 +1403,27 @@ class _McpSuggestion {
}
const _kMcpSuggestions = <_McpSuggestion>[
// HTTPS / streamable-HTTP servers (no Node, no API key)
// Same set Studio promotes as one-click cards in the Today
// hero listed here so the operator finds them even after
// the hero gets dismissed.
_McpSuggestion(
name: 'deepwiki',
icon: Icons.menu_book_outlined,
endpoint: 'https://mcp.deepwiki.com/mcp',
apiKeyEnv: '',
description:
'Public HTTPS — GitHub repo documentation search (AI-powered). No Node, no API key.',
),
_McpSuggestion(
name: 'semgrep',
icon: Icons.security,
endpoint: 'https://mcp.semgrep.ai/mcp',
apiKeyEnv: '',
description:
'Public HTTPS — security scanning for code vulnerabilities. No Node, no API key.',
),
// stdio servers (require Node + npx)
_McpSuggestion(
name: 'filesystem',
icon: Icons.folder_outlined,

View file

@ -1,7 +1,7 @@
name: fai_studio
description: "F∆I Studio — desktop GUI for the F∆I hub"
publish_to: 'none'
version: 0.35.0
version: 0.36.0
environment:
sdk: ^3.11.0-200.1.beta