Studio follows the platform rename: product branding F∆I -> Ch∆In in UI
strings, command examples fai -> chain, and — critically — the spawned
hub binary path ~/.fai/bin/fai -> ~/.fai/bin/chain so Studio launches
the renamed binary. The fai_* Dart identifiers (FaiLog, widget files,
the generated SDK) stay = vendor/internal namespace. flutter analyze:
no issues.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
A new 'Föderation' destination (primary side) lists connected
satellites — name, region, version, wire version, advertised
capabilities — and adds them in one step: 'Add satellite' issues a
single-use bootstrap token bundled with the primary CA as a
ready-to-paste satellite config (the bundled CA makes the first
connect tamper-proof). Localized EN + DE, in-app help doc. Uses the
new HubService.listSatellites / issueSatelliteToken wrapping the
SDK's federation methods.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
- Connection-aware Welcome: when the hub is down, show a hero with a
primary "Start hub" CTA + install fallback instead of a dead,
all-unchecked onboarding checklist (the first-run cliff).
- Actionable binary-not-found (file picker + install link, not a
"set FAI_BIN" dead end) and a connect-failure banner after
repeated failed health polls.
- Localize six hardcoded English error/toast clusters (DE+EN ARB).
- Bundle Inter + JetBrains Mono as assets; drop the runtime
google_fonts fetch (air-gap / KRITIS safe, no font-swap flash).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two operator-attention improvements:
- The sidebar's Approvals item gains a small accent-coloured
badge (e.g. '2', '9+') when there are pending approvals,
polled on the same 5 s tick the health check uses. Operators
no longer need to open the Approvals page to discover new
pending decisions; the badge surfaces them at the rail
level.
- The Settings dialog's per-category panel titles grow an
optional help icon (?) that opens the matching bundled
docs explainer via showFaiDoc. Wired for General →
architecture, Security → security, Maintenance → audit.
Other categories deliberately stay un-iced for now —
Appearance / System AI / Integrations don't yet have a
standalone bundled doc, and showing a broken help icon
would be worse than showing none.
Studio bumped to 0.69.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
StudioFlowRunDriver gains three new methods that satisfy the
editor 0.21.0 FlowRunDriver interface:
- pendingApprovalIdForStep(flowName, stepId) — picks the
newest pending approval row matching this (flow, step)
pair from HubService.pendingApprovals().
- approveApproval(approvalId, reviewer) — delegates to
HubService.approve, same RPC the Approvals page uses.
- rejectApproval(approvalId, reviewer, reason) — delegates
to HubService.reject.
End result: when a flow run pauses on system.approval@^0,
the Run tab now renders a complete Approve / Reject form
directly under the awaiting step. Operator no longer needs
to switch tabs to make the decision; the standalone Approvals
page stays available for non-running approvals and history.
Studio bumped to 0.68.0; editor pin moves to 0.21.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Three operator-UX gaps closed:
- Settings dialog's six-category sidebar (General / Appearance /
System AI / Integrations / Security / Maintenance) was
hardcoded English; now flows through AppLocalizations
("Allgemein" / "Darstellung" / "System-KI" / "Integrationen" /
"Sicherheit" / "Wartung"). Same for the per-panel title +
description.
- Per-channel daemon-action labels ('enable autostart' /
'disable autostart' / 'daemon restart' etc.) and the
OK / Failed result line in the toast also moved to l10n,
so the system-action feedback reads as one language.
- New 'Approvals' doc bundle (en + de) under assets/docs/,
registered as a fifth doc card on Welcome plus exposed via
the new public helper. The
Approvals, Audit and Doctor app-bars grow a Help icon button
next to Refresh that opens the matching doc in the existing
bottom-sheet reader — no extra screen, no learning curve.
Studio bumped to 0.67.0; editor path-override pulls in 0.20.1
(flow-list row polish).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The previous Add-source dialog was English-only and
operator-hostile — answered 'where do I install from?' without
explaining what a private module *is*. New version, DE + EN:
- Title + intro + button labels routed through AppLocalizations
("Modul-Quelle hinzufügen" / "Installation fehlgeschlagen"
instead of raw English strings).
- 'How private modules work' explainer block (3 sentences):
what a module is (`module.yaml` + WASM), how to package
it (`fai pack <dir>` → .fai bundle), where to host it
(any URL: own Forgejo / GitHub / S3), what verification the
hub does (sha256 + signature against trust store).
- Honest about the Studio gap: the dialog can install URLs +
packed bundles, but unpacked source directories still
require `fai install --link <path>` on the CLI. The
example command lives in its own code-style box, selectable.
- Layout: SingleChildScrollView'd so the explainer doesn't
push the buttons off short viewports.
Studio bumped to 0.66.0; editor path-override pulls in 0.20.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Pulls in editor 0.19.0 which closes three operator-reported
gaps: persistent diagnostic strip across all three tabs,
analyzer-error block on Run, and copyable run failures.
Studio bumped to 0.65.1.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two host-side pieces that complete the editor 0.18 quick-fix
loop:
- FlowsPage now fetches the store catalog (`searchStore` with
a 500-item ceiling) on open and hands it as `storeCapabilities`
to the FlowEditorPage. The editor's analyzer uses it to
decide whether an unknown-cap fix shows "Install …" or the
Add-source alternative.
- New `_AddModuleSourceDialog` — wired through the editor's
`onAddModuleSource` callback. Asks the operator for a URL
or local .fai bundle path; passes the value into
`HubService.installModule(source: …)`. Honest about its
limits: includes a card pointing the operator at
`fai install --link <path>` for unpacked module source
directories (which the gRPC install API doesn't currently
support — that's CLI-only).
Studio bumped to 0.65.0; editor path-override pulls in 0.18.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
FlowsPage now passes onInstallCapability to the editor — the
editor's 'Install <cap>' button on an unknown-capability
diagnostic calls HubService.installModule, refreshes the
capability list, and returns it so the editor reanalyzes and
clears the issue.
On install failure: friendly SnackBar surfaces the error. The
operator never gets stuck on the analyzer reporting a
capability they just clicked to install.
Studio bumped to 0.64.0; editor path-override pulls in 0.17.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The Settings dialog was a single 800-line vertical scroll — Hub
endpoint, channels, System AI, MCP, n8n, registry credentials,
hub auth token, default scope, theme plugin, maintenance all
stacked. Stefan: 'inzwischen zu unübersichtlich als langes
feld, untergruppierungen wären toll, wie z.B. in den MacOS
Einstellungen'.
Splits the panel into six focused categories driven by a
left sidebar:
- **General**: Hub endpoint (host/port/TLS), channels,
default scope
- **Appearance**: theme plugin picker (applies instantly)
- **System AI**: operator-managed LLM endpoint for Studio
helpers
- **Integrations**: MCP servers + n8n endpoints
- **Security**: registry credentials + hub auth token
- **Maintenance**: reset / destructive ops
Sidebar uses macOS-style icon + label rows with a highlighted
selection pill. Content area is per-category, scrollable
within its own panel. The dialog widens from 520→820 px and
gains a fixed height (620 px) so the layout doesn't jump as
the operator switches categories.
Each panel opens with a title + one-sentence description so
the operator knows what they're looking at before scanning the
controls — same pattern Apple uses in System Settings.
Also adds the missing 'meta' dependency that FaiLog's
@visibleForTesting needed. Studio bumped to 0.63.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two operator-visible fixes prompted by 'I picked a theme but
nothing changed':
- Theme picker tile taps now show a brief "Theme applied:
<name>" snackbar — the picker applies themes instantly, so
the operator needs a confirming cue. Slow `_pluginThemes`
loads no longer read as "click did nothing".
- `_pluginThemes` failures (plugin unreachable, manifest
drift, etc.) write to `FaiLog` so `fai admin doctor` and
Studio's inline log viewer can surface them. Previously
swallowed silently, which is what made debugging this so
miserable.
- Settings dialog's primary button relabelled to
"Connect to endpoint" (was "Save & connect") so operators
don't mistake it for "save my theme choice". The theme
selection persists at tile-tap time; the Settings dialog
has nothing left to "save".
- Theme section header gains an explicit "(applies instantly)"
cue for the same reason.
Also fixes a pre-existing curly-braces lint in the n8n add-
endpoint dialog. Studio bumped to 0.62.1, editor pinned via
path override at 0.15.1.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Bundles the error-UX overhaul and the inline log viewer:
- New `FaiLog` (`~/.fai/logs/studio-errors.log`, 256 KiB rotation,
JSON-per-line). Every operator-visible failure is appended so
`fai admin doctor` and the new viewer can show the trail
without the operator having to reproduce the failure.
- New `showFaiErrorSnack` / `showFaiErrorDialog` helpers wrap
`FaiErrorBox` in copyable surfaces; 27 ad-hoc
`SnackBar(content: Text(e.toString()))` sites swept to use
them (settings, doctor, audit, store, module sheet, system-AI
editor, flow output).
- New `FaiLogViewer` modal (`showFaiLogViewer`) renders log
files inline with line numbers, JSON-key + `[level]` token
colouring, Copy-all, Refresh, Open-externally. Doctor's
daemon-paths panel grows a "View" button next to "Open" for
every `.log` row and now also lists the Studio errors log.
- Today carousel: CTAs now actually re-run search after a
`filterCategory` / `runQuery` story is tapped (was only
flipping the chip state). Fallback story list bumped to 8.
- Editor bumped to git ref carrying 0.15.0 (type-token
colouring + analyzer diagnostics).
Studio bumped to 0.62.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Three changes:
1. Onboarding checklist hints no longer mention DeepWiki,
Semgrep, text.extract, or extract-summarize.yaml by
name. Brand- and file-specific copy moved to neutral
feature pointers ('add an MCP source', 'install a
module'). Brand names belong on the welcome page only
if they're load-bearing for understanding, which they
aren't.
2. Checklist rows are now clickable. Tapping a row navigates
to the page where the operator can complete it — MCP +
AI open Settings, Module opens Store, Flow opens Flows.
StudioShellState gains a public navigateTo(pageId) helper
so descendants can drive sidebar selection without
plumbing a controller through props.
3. Theme picker hint copy rewritten. Old hint suggested
'Custom' was a single-colour tweak; new copy spells out
that Material 3 derives a full palette (primary,
secondary, tertiary, surface, …) from one seed colour.
The Custom tile shows a small tune icon so its
open-the-picker semantics is visually distinct from the
apply-immediately plugin tiles.
Editor bumped to 0.12.0 (panel toggle + LabVIEW-style port
colours).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Settings dialog's Theme Plugin section is now a grid of
swatched tiles:
- Built-in (none) — falls back to FaiTheme.light/.dark
- One tile per installed studio.theme.* plugin, each
showing the plugin's primary/secondary/tertiary as
live colour dots. Tile loads its preview lazily so a
dozen installed themes don't block the picker.
- Custom — opens a colour-picker dialog with 12 curated
Material presets + a hex input + live preview. Selecting
applies ColorScheme.fromSeed for both brightnesses.
main.dart's _pluginThemes parses a 'custom:#RRGGBB' sigil
in the same notifier slot as plugin capability ids, so the
existing persistence + restoration paths cover the custom
case with no new state.
Bumps editor to 0.11.0 (type-checked port connections +
dynamic card width fix + card-height border allowance) and
Studio to 0.58.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Picks up fai_studio_flow_editor 0.7.0 (commit 7aeeae7):
- Wires take the colour of the data they carry. Inputs-
endpoint edges show their type accent (text=blue,
bytes=orange, json=purple, file=green, number=amber)
so the operator can trace flow data by colour alone.
- Edges entering a currently-running step animate as
marching dashes in the wire's accent colour. Stops
when the step completes. The canvas comes alive
during runs.
- Canvas backdrop is a subtle gradient (corners recessed,
centre lit). Nodes get layered shadows; selected nodes
wear an accent-coloured halo. 180 ms ease-out
transitions on hover / select / status changes.
Version 0.53.6 -> 0.54.0 (minor bump — first wave of the
2026-feel visual refresh).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Picks up fai_studio_flow_editor 0.6.0 (commit 5ff7a1c):
- Edges highlight on hover (8 px hit radius around the
bezier path).
- Right-click or long-press on an edge opens a Disconnect
popup. Same on ports + step nodes — long-press is now
the trackpad-friendly alternative everywhere a context
menu lives.
- Background pattern toggle (dots / grid / blank) on the
bottom-right canvas controls.
- Live zoom indicator as a tappable % readout — tap to
snap back to 100 % without losing pan.
Version 0.53.5 -> 0.53.6.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Picks up fai_studio_flow_editor 0.5.3 (commit 5c59f3a):
- Draft connection line now extends all the way to the
cursor (previously stopped 6 px short).
- Ports respond to hover with a soft accent glow + size
bump — clear "this is interactive" affordance before
the operator clicks.
Version 0.53.4 -> 0.53.5.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Picks up fai_studio_flow_editor 0.5.2 (commit 9afb51a):
- Bezier endpoints now terminate at the port dot's outer
perimeter, not its centre. Lines "dock" cleanly into
the socket instead of vanishing under it.
- End-cap arrow removed (the dot is the terminator).
- Connected ports gain a tiny inner pin so they read as
"socket with a plug seated" rather than "hollow circle".
Version 0.53.3 -> 0.53.4.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Picks up fai_studio_flow_editor 0.5.1 (commit e4e2d45):
- Five payload types each get a distinct hue
(text/bytes/json/file/number).
- Step header shows wired/total ratio when partial,
green check when fully wired.
Version 0.53.2 -> 0.53.3.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Picks up fai_studio_flow_editor 0.5.0 (commit 099cd18):
- Each port is a single dot (no more inline + canvas
duplication). Sized to match label rows.
- Inputs endpoint dots sit on the RIGHT edge (data flows
out), labels right-aligned. Outputs endpoint stays on
the left. Step input dots stay on the left.
- Filled when the port participates in an edge, outlined
when dangling. Type-coloured for inputs-endpoint dots.
- Right-click a wired input port to Disconnect — clears
the with-field / output expression cleanly.
Version 0.53.1 -> 0.53.2.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Picks up fai_studio_flow_editor 0.4.0 (commit fb88926):
- Connection dots now line up with their inline port-row
labels (header geometry rewrite so subtitle space is
always reserved).
- Inputs endpoint colours its output ports by declared
type (text / bytes / json / file / number).
- Selecting the inputs or outputs endpoint opens a
dedicated editor in the properties panel — add /
rename / retype / remove without touching YAML.
- "Unsaved" badge replaces the easy-to-miss 8-px dot;
filename also colours primary when dirty.
Version 0.53.0 -> 0.53.1.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Picks up fai_studio_flow_editor 0.3.0 (commit 296e5bf):
- inputs/outputs endpoints now live in the layout sidecar
like every other node — they stop drifting when the
operator drags a step. The "outputs panel jumps when I
touch anything" pain Stefan flagged is gone by
construction.
- Right-click on any step in the graph tab opens a popup
menu: Duplicate (with unique id + offset), Disconnect
all inputs (clears with-field values but keeps keys),
Delete.
- Floating Reset Layout button on the canvas next to
Fit-to-screen — wipes the sidecar and re-runs
AutoLayout from scratch + auto-fits, for when manual
drags have drifted into spaghetti.
Version 0.52.4 -> 0.53.0 (minor bump — first new
operator-visible editor feature surface since 0.52).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The 1440x900 default felt too wide for first-run: the
Welcome page's 960 px content column left visible slack on
either side even after the centring fix, and the editor's
sidebar plus canvas plus properties panel still didn't
need that much horizontal room on a fresh open.
1280x900 matches the most common laptop effective
resolution, keeps every Studio surface comfortable, and
the operator can resize larger any time. Same value
across all three host platforms (macOS NSWindow setContentSize,
Linux gtk_window_set_default_size, Windows Win32Window::Size).
Version 0.52.3 -> 0.52.4.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Welcome page used ConstrainedBox(maxWidth: 960) inside a
left-aligned Column, so on the 1440x900 default window
the content hugged the left edge and left ~370 px of
empty space on the right. With Stefan's M4 sidebar
collapsed to 72 px the asymmetry was the dominant visual
in the first thing every operator sees.
Wrap the ConstrainedBox in a Center widget. 960 px stays
an opinionated reading width — long-form prose gets
uncomfortable past ~1100 px — but the slack now splits
evenly so the content sits with ~190 px breathing room
on both sides instead of all on one.
Bumps the editor-package lock pointer to ad2e5b5 (same
0.2.2 version, picks up the extract-with-approval test
commit on top of f8138f3) as a side-effect of the
flutter pub upgrade run during verification.
Version 0.52.2 -> 0.52.3.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Picks up:
- Brief green-check snackbar after every successful save
(Cmd+S keystroke now has visible confirmation).
- During a connection drag, every input port lights up
and the closest one within snap distance gets a halo
glow so the operator sees which port the drop will
snap to.
Version 0.52.1 -> 0.52.2.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Pulls in:
- Live step status on the graph during runs (the graph
highlights running / done / failed / awaiting nodes
alongside the run-tab list).
- Empty-graph call-to-action overlay with an Add step
button so the first opened-but-empty flow points the
operator at the right action.
- Fit-to-screen button + auto-fit on first open of each
flow so wide flows aren't cut off in the default
viewport.
Version 0.52.0 -> 0.52.1.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Studio's Flows destination is now the swappable
fai_studio_flow_editor's full surface: a drag-and-drop
graph view, the raw YAML text editor, and the run tab
with inputs form + live step progress + outputs.
What's gone:
- The old saved-flows list + per-row run dialog.
- The separate "flow-editor" navigation destination (one
icon, not two — operators reach the editor by clicking
Flows, period).
- _FlowEditorAdapter (no longer needed; FlowsPage hands
the runDriver to the editor directly).
What's new:
- lib/data/flow_run_driver.dart — StudioFlowRunDriver:
adapter implementing the editor's FlowRunDriver. Maps
runFlow() to HubService.runSavedFlow and events() to
HubService.streamEvents filtered to step.* event types.
Converts proto-shaped FlowOutput variants to the editor's
host-agnostic FlowOutputValue family.
- lib/pages/flows.dart — replaced with a 50-line wrapper
that loads capabilities into the editor's picker dropdown
and forwards the active locale.
Editor pinned to fai_studio_flow_editor 0.2.0 (commit
870cbc2). All editor state — file list, dirty tracking, tab
selection, graph layout sidecar, properties panel — lives in
the editor package now; Studio just supplies the hub bridge.
Version 0.51.10 -> 0.52.0 (minor bump — Flows-page contract
changed, separate flow-editor destination removed).
flutter analyze: 0 issues. flutter test: 12/12 green.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Stefan reported the editor still centered short files vertically
and the reload icon was still between New and Save — but his
pubspec.lock pinned fai_studio_flow_editor to resolved-ref
7655e0d (0.1.2), which predates both fixes:
- a7485ca (0.1.3) stretch attempt
- daf6732 (0.1.4) actual fix via CodeField expands:true +
refresh moved into the FileList header
Force lock to daf6732 so `flutter pub get` during `fai studio`
build picks up the current state of the editor's main branch
without needing a manual `flutter pub upgrade` step.
Version 0.51.9 -> 0.51.10.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Stefan observed the default Studio window opened smaller
than comfortable when launched fresh (no persisted size).
On macOS specifically the xib-stored default was around
800x600 — too tight to read the sidebar + content + run
panel side-by-side.
Set 1440x900 as the initial content size across all three
host platforms:
- macOS: MainFlutterWindow.swift sets contentSize via
NSWindow.setContentSize after the FlutterViewController
attaches, and centers on the active NSScreen. Clamped to
the screen's visibleFrame so we never open larger than
the display (matters for non-Retina external monitors).
- Linux: gtk_window_set_default_size 1280x720 -> 1440x900
- Windows: Win32Window::Size 1280x720 -> 1440x900
1440x900 is the effective Retina resolution of a 13" MacBook
(Stefan's dev machine) and the smallest "modern desktop"
footprint that lets the sidebar + content + tool panel
breathe.
Version 0.51.8 -> 0.51.9.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The connection-row tooltip, the _ConnectionLabel caption,
the footer settings tooltip, and the channel-pill explainer
were hardcoded English even when the running app was set to
German. Move all four through AppLocalizations.
Adds ARB strings (de + en):
- connectionTapToStart antippen zum Starten / tap to start
- sidebarSettingsTooltip Einstellungen (Cmd-;) / Settings (Cmd-;)
- sidebarChannelTooltip multi-line channel explainer
Existing connectionConnected / connectionUnreachable /
connectionConnecting are reused as the caption pieces; only
those two new strings (plus the channel tooltip) needed
adding.
Version 0.51.7 -> 0.51.8.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The earlier "single AnimationController for width + opacity"
commit (0.51.5) made the *visual* expand smooth but the
underlying layout still jumped because the rows themselves
changed height between states:
collapsed expanded
───────── ─────────
triangle (36 px) triangle + 2-line title (44 px)
dot (10 px) dot + multi-line pill with
optional "Start hub" button
(60-80 px when disconnected)
letter (16 px) letter + accented chip (28 px)
So everything BELOW the header — destinations list, footer —
slid down ~50 px every time the rail opened. That's the
"Versatz" Stefan kept seeing. Fading the labels in didn't
help; the row geometry was the wrong source of truth.
Structural fix: every header row is now a SizedBox with a
fixed pixel height. Collapsed-state and expanded-state
content both fit inside the same height:
_brandRowH = 48 // FaiDeltaMark (36 px) centered
_connRowH = 44 // single conceptual block, two single-
// line texts, no multi-line pill
_channelRowH = 28 // chip + single-line channel name
_rowGap = 8
The total header block is therefore a constant pixel sum.
Items below it (the destinations ListView, the footer) sit
at the same Y in both states by construction — not by lucky
math, not by animation tricks. The width animation only
moves the rail's RIGHT edge; the left + top + bottom edges
of every row are immovable.
To make the connection row fit in 44 px we drop the inline
"Start hub" tonal button. The same affordance is preserved
by making the whole row tap-handled when the daemon is
unreachable: tap the red dot (or anywhere on the row) to
fire `fai daemon start`. The tooltip is updated to spell
this out ("Disconnected · tap to start · …").
The channel row is now ALWAYS reserved (28 px placeholder
when no channel is active) so that flipping the operator
config from local→dev at runtime doesn't shift the
destinations list either. The placeholder is invisible.
Side-effects:
- _ConnectionPill is removed (nothing references it).
- New _BrandLabel + _ConnectionLabel widgets, both
trivially Column(MainAxisAlignment.center, …) so their
content sits visually centered inside the fixed row.
Version 0.51.6 → 0.51.7.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The flow-run dialog showed a single spinner + "running…" label
for the whole run. For multi-step flows the operator had no
way to see *which* step was busy or how close the run was to
finishing.
Replace the spinner with a live step list driven by a
StreamEvents subscription:
✔ extract 0.41s
⏳ summarize
◻ notify
━━━━━━━━━━──── 35%
Mirrors the `fai run` CLI rendering — one shared visual
language across both surfaces. Steps appear in execution
order as the hub emits step.started events; check + duration
on completion; cross + first-line error on failure; pause
icon on approval gates.
Implementation:
- HubService.streamEvents(backfill, types) — new public
stream-facade method that wraps HubClient.streamEvents and
maps proto LoggedEvent → AuditEvent for the rest of Studio.
Subscribed with backfill=0 so the dialog only sees events
from this very run.
- _FlowRunDialogState.initState subscribes BEFORE submitting
the run, so the first step.started never gets lost in the
gRPC handshake gap.
- Two-layer filter on incoming events: same flow name AND
timestamp >= dialog open time. The timestamp gate is what
stops a previous run's tail-end from painting stale rows
if the user re-runs the same saved flow.
- _LiveStep + _LiveStepList — insertion-ordered map renders
rows in runtime execution order (not alphabetical), so
what the operator sees matches what the hub did.
Version 0.51.5 → 0.51.6.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
+ label opacity in lockstep (no more glitchy expand)
The previous "fixed-width icon column" commit already locked
the icon's horizontal pixel-X, but the rail still _felt_
glitchy on expand because the rail's geometry and the rail's
content were animated by two different mechanisms:
- Width: AnimatedContainer over FaiMotion.fast (120 ms)
- Content: setState(_hovered = true) → conditional
`if (expanded) Expanded(label)` snaps in one frame
So on mouse-enter the label widget appeared INSTANTLY while
the rail was still 72 px wide. The label tried to render in
0 px of available space and Flutter's layout engine clamped
it; over the next 7 frames the rail grew to 220 px and the
label visibly "settled in". That's the perceived glitch.
Replace the two-source animation with a single
SingleTickerProviderStateMixin + AnimationController whose
value `t` (0..1, eased via easeInOutCubic) drives both:
- rail width = lerp(72, 220, t)
- label opacity = t
- labelsInteractive = t > 0.5 (so hidden buttons can't
eat clicks meant for the icon column)
Labels are wrapped in `t > 0 ? IgnorePointer(Opacity(...))
: SizedBox.shrink()`. Once the animation starts, the label
joins the tree at opacity ≈ 0 (invisible — no pop) and
fades up smoothly as t grows. When fully collapsed (t == 0)
the label is removed from layout entirely, so the connection
pill's tall "Start hub" affordance doesn't inflate the
collapsed rail's height (this also fixes the widget_test
vertical-overflow that 0.51.4 introduced).
Same pattern applied to the footer (settings icon stays in
the 72-px column always; theme/lang/clock fade in beside it)
and to each _SidebarItem destination row.
End result: width AND content travel together along the same
animation curve. No more "snap then catch up" — they're
mathematically inseparable.
Version 0.51.4 → 0.51.5.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Previous "left-anchor everything via padding" approach made
the math work on paper but the perceived shift persisted. The
root cause was structural, not padding-based: ListView gave
each item a tight cross-axis constraint equal to the rail's
current width (48 collapsed, 196 expanded). Inside the item,
the AnimatedContainer filled that constraint and laid out a
mainAxisSize.min Row at the start of the AC's content area.
The icon's pixel-X depended on the AC's padding _and_ the
constraint; even with equal padding the rendered layout
shifted across the expand animation because Flutter
re-resolved alignment under a moving constraint.
Replace the entire pattern with a fixed-width icon column
that doesn't care about the parent constraint:
ListView (padding 0) → Item Row
SizedBox(width: 72) → Center → icon ← anchored to literal x
if expanded → Expanded → label text ← grows into remaining space
Same pattern for the brand/connection/channel rows above. The
72 px matches the rail's collapsed width exactly, so collapsed
rail = one icon column + nothing; expanded rail = same icon
column + label slot. Icon's pixel-X is now provably constant.
Removes the AC's horizontal padding entirely — the icon
column owns the left anchor. The background highlight still
spans the full row width (typical nav-rail UX), and the rail
expand animation only moves the rail's right edge, never any
icon-bearing element.
Also passes an `iconColumnWidth: 72` to _SidebarItem instead
of hard-coding the magic number in two places.
Version 0.51.3 → 0.51.4.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two Stefan-reported fixes after the v0.51.x live test:
1. Sidebar icons appeared to shift between collapsed and
expanded. Mathematically the icons stayed at x=24 (
ListView padding + AC padding), but the Column's default
crossAxisAlignment.center re-centered the brand mark
AND the top indicators (version, connection, channel) on
every expand → in a 72 px rail the brand sits at x≈36,
in a 220 px rail it sits at x≈110. Stefan perceived this
horizontal repositioning of the brand+pills as a shift
of the icons below.
Fix: Column.crossAxisAlignment = start + left-pad every
top item (brand mark, "F∆I Studio" label, version,
connection pill, channel pill, mini-version, connection
dot, channel chip) by exactly the same offset the
destinations use (ListView.padding + AC.padding =
FaiSpace.md + FaiSpace.md). The whole rail now reads as
one stable left edge through the expand animation.
2. Pull fai_studio_flow_editor 0.1.2 — fixes the
"opening hello.yaml shows extract-with-approval content"
bug by switching `_code.text = ...` to the canonical
`_code.fullText = ...` setter in the package.
Version: 0.51.2 → 0.51.3
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Updates fai_studio_flow_editor pin from 51f9a1d to 8b918f8.
Upstream fix: the editor's dirty-detection compared
_code.text against a snapshot taken before CodeController
normalisation, so opening any flow whose loaded text the
controller silently rewrote (trailing-newline handling, YAML
fold markers, line-ending normalisation) immediately read as
dirty. Clicking a different file in the list then raised the
"Discard unsaved changes?" prompt even though the operator
hadn't typed anything.
The package now compares _code.fullText (the canonical
post-processed value) against a baseline captured AFTER the
text assignment. Comparing fullText-to-fullText eliminates
the spurious diff.
Version: 0.51.1 → 0.51.2
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two UX bugs Stefan flagged on the v0.51.0 collapsible rail:
1. Icons re-positioned when the rail expanded. Operators
already pointing at an icon had to chase it as the rail
grew. Two causes: the _SidebarItem switched between
`Center(icon)` (collapsed) and `Row(icon, gap, label)`
(expanded), and the AnimatedContainer's horizontal padding
switched between FaiSpace.sm (collapsed) and FaiSpace.md
(expanded). Both flipped the icon's effective X-position.
Now: same Row, same AnimatedContainer padding, in both
states. The icon sits at the same X-pixel always; only
the label slot toggles open beside it.
2. Top of the collapsed rail was wasted (only the brand-mark
was visible; version + connection + channel disappeared
completely). Use the same vertical real estate for
glance-able compact equivalents:
* tiny "v0.51" version text in mono below the brand
* _CollapsedConnectionDot — 10 px circle, green/red/amber
tonality matching FaiDeltaMark's mode states. Tooltip
surfaces the full endpoint string.
* _CollapsedChannelChip — 18 px circle with the channel's
first letter (P / B / D / L) in the same accent tone
the full pill uses, so "production" stays unmistakably
red even at-a-glance.
When the rail expands, the full pills replace the compact
indicators as before. Tests still green (the existing widget
test booted without hover-expand and that path stays correct
either way).
Version: 0.51.0 → 0.51.1
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The flow editor was internal to Studio (lib/pages/flow_editor.dart).
Per Stefan's review feedback ("austauschbar wäre schöner"),
extract it into its own Forgejo repo so the host can swap
the implementation without touching Studio.
New repo: https://git.flemming.ai/fai/studio-flow-editor
Studio's pubspec.yaml now references the package by git URL:
dependencies:
fai_studio_flow_editor:
git:
url: https://git.flemming.ai/fai/studio-flow-editor
ref: main
To swap the editor:
1. Fork (or write a new) fai/studio-flow-editor.
2. Keep the FlowEditorPage(initialFlowName, locale, onRun)
constructor signature — the stable host contract.
3. Point the pubspec at your fork.
4. Rebuild Studio.
Adapter pattern: _FlowEditorAdapter in main.dart resolves the
package's runtime dependencies (locale via Localizations,
onRun via HubService) from the BuildContext, then constructs
the package's FlowEditorPage. Same pattern in flows.dart for
the pencil → editor route push, so a future operator-side
locale switch propagates correctly.
The package brings its own copies of FaiSpace tokens, minimal
FaiEmptyState/FaiErrorBox widgets, and an inline EN+DE l10n
table — accepting a small amount of visual drift in exchange
for true package independence. flutter_code_editor +
highlight move from Studio's pubspec to the package's.
Deleted:
lib/pages/flow_editor.dart → package's lib/src/flow_editor_page.dart
test/flow_editor_test.dart → package's test/ (next commit there)
Bumped:
pubspec.yaml version 0.50.0 → 0.51.0
main.dart kStudioVersion 0.50.0 → 0.51.0
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Three operator-visible improvements per Stefan's review of
the v0.50.0 editor.
1. Pencil icon on Flows page now opens Studio's own flow
editor (route push) preloaded with the selected flow,
replacing the previous "open in OS default editor"
(SystemActions.openInOs) behaviour. The editor's AppBar
gains a back arrow when reached via route push; reaching
the editor via the nav rail leaves it bare. Tooltip
string updated in EN + DE.
2. New FlowEditorPage `initialFlowName` parameter. When set,
the page loads that flow on first frame (via post-frame
callback so the BuildContext is mounted before
_openByName runs). When null (the nav-rail path), the
editor opens to its empty state as before.
3. Sidebar (_Sidebar) is now collapsed-by-default: shows
just icons in a 72px-wide rail with per-destination
tooltips. Hovering the rail expands it to 220px (the
old width) with brand-mark + labels + the full footer
row (theme toggle, language toggle, clock, settings).
Collapsed footer shows the settings icon only. Brand-
mark (FaiDeltaMark) stays visible in both states so the
live/idle status dot is always glanceable.
Side-effect: SystemActions import in flows.dart is no
longer needed (the pencil no longer shells out) — removed.
widget_test.dart: dropped the per-destination Text-presence
asserts since labels are now Tooltips when collapsed.
Hover-expand testing triggers RenderFlex-overflow mid-
animation in widget tests (the AnimatedContainer's width
transitions through a constraint slimmer than the Row's
intrinsic min). The booting-without-throwing assertion
remains; per-destination presence stays in the per-page
test suites.
Both arb files updated with the new strings (navFlowEditor
already existed; added flowEditorBackTooltip + retouched
flowsOpenInEditorTooltip).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
New top-level destination "Editor" (Cmd+5) ships as Studio's
fifth surface. The editor reads + writes flow YAML directly
under ~/.fai/data/flows/ via dart:io — the hub picks up the
changes on the next listFlows / runSavedFlow call.
Layout: two-pane shell. Left (240 px) is the file list; right
flexes to the code pane and an optional results column when
Run produces output. Top toolbar exposes:
* filename + dirty-mark
* New flow (scaffolds from a debug.echo template)
* Save (writes the active file to disk)
* Run (saves first if dirty, calls
HubService.runSavedFlow, surfaces typed FlowOutputs in
a side panel)
* Refresh
YAML highlighting via flutter_code_editor + the highlight
package's yaml language. Lightweight style map mapping the
five token classes that actually appear in flow YAML
(attr / string / number / comment / subst for the
${{ ... }} template syntax) to FaiTheme colors — keeps the
editor visually consistent with the rest of Studio.
New-flow naming uses a FilteringTextInputFormatter that
restricts the name to [a-z0-9_-]. A "name already exists"
SnackBar surfaces the conflict instead of silently
overwriting.
Bilingual strings shipped (en.arb + de.arb) for every
operator-facing string: toolbar buttons, dialogs, empty
states, file-exists error, run-output header.
New deps:
* flutter_code_editor ^0.3.5
* highlight (transitive — pinned as direct so the
yaml-language import has its declared dependency).
Smoke-test (test/flow_editor_test.dart) pumps the page and
asserts the empty-state + toolbar render without throwing
on hosts that don't have ~/.fai/data/flows yet. The full
file-list + open-on-tap flow needs a writable HOME override
which dart:io's read-only Platform.environment doesn't allow
inside a test isolate — that path lives in the integration
suite as a follow-up.
Version bumps:
* pubspec.yaml: 0.49.1 → 0.50.0
* main.dart kStudioVersion: 0.42.0 → 0.50.0 (had drifted
behind pubspec; brought back into sync as part of this
bump)
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two pieces of the Studio plugin host:
- `data/theme_plugin.dart`: discovery, persistence, ColorScheme
translation. Plugins listed via list_capabilities filtered
to studio.theme.*; selection persisted in SharedPreferences;
loadThemePluginSchemes builds a ColorScheme pair from the
plugin's 14 ARGB tokens per brightness.
- `main.dart`: StudioApp gains themePluginNotifier +
setThemePlugin(). MaterialApp wraps a FutureBuilder that
re-fetches the plugin's ColorSchemes whenever the
capability flips; falls back to FaiTheme.light/dark on
null or load failure.
- `fai_settings_dialog.dart`: new `_ThemePluginPanel` shows a
dropdown of installed studio.theme.* capabilities + a
"Built-in" entry. Switching applies immediately.
- HubService.invokePluginTranslate added as the typed wrapper
for the new gRPC RPC — ready for the FaiEnBadge swap-out
in the next iteration.
- 4 new l10n keys (themePluginHeader / None / Hint / Empty),
EN+DE.
flutter analyze + flutter test (widget + friendly_error): both
green. The integration test for the theme picker is the
existing fai_runtime plugin_theme + Studio-side
invokePluginTheme path that round-trips through the new
mirror-installable studio-theme-solarized bundle.
Signed-off-by: flemming-it <sf@flemming.it>
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Four UX threads stitched into one commit. Each pulls Studio
toward Stefan's "zero-learning-curve" goal — the feedback
that earned its own memory entry.
1. Flow-Runnability-Indikator
─────────────────────────
The Flows tab now fetches `listFlows` and `listModules`
in parallel. Each card compares the flow's
`requiredCapabilities` against the installed-modules'
capability set; rows with missing modules show a "Needs:
text.extract@^0" red pill row beneath the path and have
their Run button greyed out + tooltip
"Install the missing modules first." Operators stop
hitting Run → cryptic hub error → frustration.
2. Welcome-Checklist Celebration
─────────────────────────────
Once all four checklist signals flip to done, an
`_AllDoneCelebration` card replaces the bare
"All four steps complete" + Hide button. Three concrete
next-threads with action buttons: "Read the audit log",
"Set up the daily Today story" (opens the Flows / Today
doc inline via `_DocReaderSheet`), and "Build your own
module" (opens the architecture doc). Operator who just
got set up sees what to do next instead of an empty
"what now?" feeling.
3. Audit-Page Time-Bucket Headers + Flow-Run Detail
────────────────────────────────────────────────
The flat event list grows tiny "TODAY / YESTERDAY /
EARLIER THIS WEEK / OLDER" section headers — bucket is
computed in the operator's local timezone so an event at
23:55 yesterday in Berlin doesn't end up in "today"
because UTC happened to spill into a new day.
Plus: the event-detail dialog gains a "View flow run"
action when the picked event has a `flow_execution`. It
opens a drill-down that lists every event in the
already-fetched 100-event window sharing the same
execution id, sorted ascending — the operator reads the
run from step.started top to flow.completed bottom.
4. Approvals-Batch-Aktionen
────────────────────────
Each pending approval card grows a checkbox. When ≥1
selected, a floating action bar appears at the bottom
with "N selected · Select all · Clear · Reject all ·
Approve all". The parent loops sequentially through the
per-record SDK calls so a partial failure produces
"X done, Y failed" instead of a confusing all-or-nothing
rollback. Reject prompts for a reason once and applies
to the whole picked set.
13 new ARB keys cover the strings the four features
needed. Studio's tests stay green.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The free-form key=value run-flow dialog produced two
unhelpful failures every time an operator hit Run:
1. Submit empty → "step references missing value
'\$inputs.document'" from the hub. Studio sent a zero-
entry inputs map because nothing told the operator the
flow declared a required input.
2. Type `document=@/path/to/file` → "path not found".
Dart's `File()` doesn't expand `~`, and on macOS
sandboxed Studio can't read arbitrary paths anyway.
Both failure modes are gone. The dialog is now a typed form:
- On open, fetches `getFlowDefinition(name)` (new SDK
v0.15.0 wrapper around the v0.10.89 hub RPC). While that
resolves, a small spinner shows
"Loading inputs…"; on failure, an inline `FaiErrorBox`
with the exact RPC error and a copy button replaces the
spinner.
- Renders one form-field per declared input. The flow
YAML's verbatim type tag drives the widget choice:
`bytes` / `file` → "Choose file…" button + picked-file
readout, plain TextField for everything else. The type
tag is shown next to the input name as a pill so a flow
author who picks a less-common type still gets a hint.
- Bytes inputs route through `file_picker` with
`withData: true`, which means the OS file dialog handles
read access — sandboxed Studio gets the bytes inline
rather than a path it can't open. Falls back to
`File(path).readAsBytes()` on Linux configs that don't
honour `withData` for large files; failures show a
copyable SnackBar.
- The Run button enables only when every declared input has
a value (text non-empty / file picked). Empty submission
is impossible; the cryptic hub-side
"missing value" error stops surfacing.
- The run-result dialog's error path uses `FaiErrorBox`
instead of the previous plain `SelectableText`, so any
hub-side flow error (permission denied, module crash,
whatever) is one click to clipboard.
11 new ARB keys cover the form's labels, the file-picker
states, the loading / failed-definition messages, the
"This flow declares no inputs" empty state, and the
run-error dialog title.
`file_picker: ^8.0.0` added to pubspec.yaml.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Three concrete fixes against today's user feedback.
- Flows that take binary inputs (extract / extract-summarize /
…) work from the Flows tab. The run-flow input dialog now
accepts the same `@/path/to/file` syntax `fai run --input`
uses on the CLI: any value beginning with `@` is read as
bytes and sent as a binary Payload; plain values still flow
through as text. The dialog hint copy and the example
placeholder reflect the new syntax. File-read failures
surface as a SnackBar before the run dialog opens, so a
typo in the path doesn't reach the hub. Threaded through
`_FlowRunDialog` and `HubService.runSavedFlow`, which both
carry separate `textInputs` and `fileInputs` maps now and
forward to the SDK's mixed-mode runSavedFlow (v0.14.0).
- The Welcome doc-reader's error path uses `FaiErrorBox` so
the actual underlying error is selectable + copy-to-
clipboard via the existing widget. Plus the loader throws
a richer error string that names *both* attempted asset
paths (`<slug>_<lang>.md` and the EN fallback) and the
underlying exception each, so the operator can paste a
diagnostic into a chat without us having to ship a
separate "how to read Flutter asset errors" doc.
- Doctor's empty Services panel had a horizontal RenderFlex
overflow at narrow widths because the long mono-spaced
hint ("add to ~/.fai/config.yaml under services:") and
the leading icon+text both demanded full intrinsic width
in a single Row. Now wraps via a `Wrap` widget so the
hint flows to a second line on narrow viewports and stays
in the same row when there's space.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
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>
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>
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>
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>
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>