A waiting user asks two questions that a plain LinearProgressIndicator
answers as one: how far along is it, and is anything still happening.
The widget keeps them apart - the value may stand still for minutes
while a highlight driven by 'busy' keeps crossing the bar, so a
stalled percentage no longer reads as a frozen app.
Also: glides to new values over 600 ms so the polling interval behind
it stays invisible, refuses to walk backwards, renders indeterminate
rather than an empty bar when no value is known, stops animating once
the work ends, and honours reduce-motion.
Eight guards in test/progress_bar_test.dart cover each of those rules.
Writing them found a real defect: with busy: false the late-final
controller was first constructed inside dispose(), where the ticker's
context lookup is already unsafe.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The switcher used to be embedded per page (Flows/Runs/Audit/
Approvals) — invisible on the other five pages and sitting in a
different corner depending on the page (persona review 2026-08-27,
consensus finding). It now lives ONCE in the sidebar, above the
destinations: active project/area always visible, opens the same
menu everywhere, Cmd+P from anywhere. The shell listens to the
workspace, so the sidebar endpoint label can no longer lag a
sealed switch until the next health tick.
Also in this rebuild:
* Stopped sealed areas ask before starting ("Start area X?") —
a context switch must never boot a hub daemon as a click
side-effect; running areas keep switching with one click.
* The switcher tooltip told a wrong scope ("filters this view") —
it now says the choice applies everywhere and stamps new runs.
* The aggregated sealed row explains itself in place (names can
reveal client identities) and links to the Settings toggle
(Settings dialog gained an initialCategory jump).
* The active entry carries a checkmark in the menu.
* The Cmd+K palette knows projects and areas, ranked by recent
use; sealed names honour the privacy setting — while hidden,
the palette offers the guarded picker instead of the names.
* The runs empty state names the active project filter as the
cause ("No runs in project X" + show-all action) instead of
claiming the feature is off.
Tests updated to the anchor and made hermetic (scriptable
projects on the fake hub, sealed-area fake); new coverage for the
checkmark, the why-line, and the start confirmation.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two findings from the workspace persona review, both rated high:
* Switch race: the hub client re-pointed at a sealed area's hub
before the workspace announced the sealed context, so the 2 s
page pollers (runs, audit) could fetch and render that hub's
data without the sealed marking. Switches now run inside an
explicit switching window: opened before anything touches the
connection, announced optimistically in the identity bar
("switching…" + spinner, leave button hidden), pollers and the
shell health tick pause inside it, and pages drop replies whose
context epoch changed mid-flight. The sealed context is
announced only after the new hub answered healthy.
* Filter loss: entering a sealed area cleared the shared-hub
project filter and returning restored only the endpoint. The
filter is now parked on entry and restored on return; prefs
keep the parked value throughout, so live state and prefs agree
after the round trip (and after a mid-session relaunch).
Guard: workspace_switch_race_test pins both invariants
state-matrix-style against scripted hub + sealed-area fakes —
reconnects may only happen inside an open switch window, pollers
must stay silent inside it, and the filter must survive the round
trip. SealedAreaService gained a debugSetInstance seam so the
suite never scans a real ~/.chain.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The detail sheet always answers 'who maintains this?': one
selectable line per maintainer from the store index's new
maintainers list, or an honest 'not specified' when the index
names nobody (StoreMaintainersSection, public for the widget
tests). The install trust gate carries the same fact when
present and stays terse otherwise. DE+EN; dialog-harness proof
captured with the maintainer row.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The doc-verifier pass over the new trust/exposure surfaces came back
PASS with five improvements, all applied:
- the store policy notice gains a 'Learn more' into the security doc
(the notice named security.require_signatures but not where it
lives)
- 'blocked' disables the trust gate's install button — an active
button contradicted the 'install would be refused' statement right
above it (guard test added)
- the unknown-exposure tooltip now says what the operator can do
(check where the name resolves)
- dead l10n key verifPillUnverified removed (unverified is the
page-level notice, never a card pill)
- stale header comment in install_verification.dart corrected
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The trust-gate dialog replaces its generic 'no per-entry status yet
(alpha)' note with the hub's classified verification statement —
pinned store key / trusted publishers (green), installs without
signature checking (amber), install would be refused (red), bridge
entry (neutral) — via one shared describeInstallVerification mapping.
Against a pre-0.23 hub the field is empty and the old honest wording
stays (pinned by test).
Information architecture: policy-off is a GLOBAL fact, so it appears
as ONE ChainInlineHelp notice above the store grid instead of a
warning pill on every card (card noise); only 'blocked' — a genuine
per-source anomaly — earns a card pill. Doctor's host services show
the hub-classified network reach per endpoint (local only / private
network / publicly reachable with a protect-it hint / reach unknown).
Verified end-to-end against the live dev hub (guide harness): the
wire field arrives, the store page shows exactly one policy notice
and quiet cards; trust-gate variants captured via the dialog
harness. Suite 123 green.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
New ChainInlineHelp (intro strip: what this is + what will happen, with
an optional 'Learn more' into the doc sheet) and ChainFieldHelp /
ChainFieldLabel (a '?' affordance per field). First applied to the
add-satellite dialog, which asked for a bare 'name' with no hint of
what a satellite is or does (usertest): it now leads with a plain
explanation + a federation 'Learn more', and the name field carries a
'?'. Both widgets are quiet by design.
Verified: field_help_test covers the widgets + that the dialog explains
itself; dialog_shots_test.dart (a reusable headed dialog-capture
harness) proved the layout in light + dark. Studio 0.76.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The switcher listed sealed areas by name ('lbs', 'stromnetz') on
any glance or screenshot — but the names themselves often carry
client/mandate identity (usertest security finding). The sealed
section now renders one aggregated row ('2 sealed areas') with a
deliberate 'Show names' reveal per menu opening; selection still
pops the regular s:<slug> value. Settings -> Security gains 'list
sealed areas with their names right away' (WorkspacePrefs,
SidebarPrefs pattern, default off).
The aggregate row wraps to two lines — popup menus cap their
width and action texts must never be truncated (the first cut
showed '1 abgeschotte…' in the proof shot). Guard: switcher tests
cover aggregated-until-reveal and the Settings toggle; the old
direct-listing test now asserts the reveal contract. DE+EN.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The same single-select choice pattern appeared as four widgets:
audit's hover pills, the store's SegmentedButton, the store filter
dialog's ChoiceChips, and the approvals TabBar (usertest finding
#14 / night-log decision 'pill segment as canon'). The audit
pattern is promoted to a shared ChainSegments widget (optional
icons, hover, selected border, button+selected semantics) and all
four sites use it; approvals switches lists via IndexedStack so
both stay alive and switching does not refetch.
Guard per the no-bugfix-without-a-guard rule: widget tests for
selection + semantics, plus a canon sweep that bans
TabBar/TabBarView/TabController/SegmentedButton/ChoiceChip from
lib/ (comments exempt). Deliberately out of scope: the flow
editor's Graph/Text/Run tabs live in the separate editor package.
Studio 0.75.0; guide images regenerated, dark + light verified.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The widget suites used to talk to whatever listens on the real
endpoint — results depended on the operator's machine (a running
hub fed real data into a11y/responsive runs and its gRPC channel
timers caused the historic flake). Hardening round:
- HubService.instance is now injectable (debugSetInstance);
FakeHubService (test/support/fake_hub.dart) answers every member
the pages touch with healthy-empty defaults and scripts per-RPC
failures (UNIMPLEMENTED / UNAVAILABLE / detached gate) through a
GrpcError-shaped fake. Unimplemented members are recorded and
fail the sweep with the exact list.
- state_matrix_test.dart pins the app-wide invariants for every
sidebar page x hub condition: healthy => no unreachable claims
and no raw error text; hub gone => honest unreachable states;
UNIMPLEMENTED => never 'not reachable' while the sidebar shows
connected; detached gate => plain-language feature-off state.
- a11y + responsive sweeps now inject the fake (hermetic); the
6-minute idle-timer drain workaround is gone with the cause.
Real bugs the new sweep caught immediately:
- every data page (store, doctor, audit, approvals, federation)
folded ANY load failure into 'hub not reachable' — the runs-page
bug class; they now share HubLoadErrorView, which classifies
into unreachable / needs-newer-hub / load-failed-with-copyable-
detail (new generic DE+EN strings)
- the approvals page's hidden tab had no future listener: a load
failure there surfaced as an uncaught async error
- the audit status bar rendered the raw gRPC error wall verbatim;
it now shows the classified friendly headline (still selectable)
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
- Store hero: 'TODAY' badge only for an operator-accepted story; the
rotating compiled-in fallback deck now says 'FEATURED' (no false
freshness claim). Policy as a top-level function with unit tests.
- Audit filter chips: standard label typography instead of mono —
mono stays reserved for paths and identifiers.
- DE chain wording: 'Hash-Kette geprüft' as the one confirmation term
(audit header now matches the doctor pill); 'intakt' stays the
state headline. EN was already consistent.
- Workspace switcher: contrast bump for the sealed-area 'stopped'
label.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
- 'Mit Endpunkt verbinden' — no Endpoint/Endpunkt mix
- audit-chain lead term is 'Hash-Kette' everywhere (doctor pill);
chain detail says 'Verkettung lückenlos geprüft' instead of the
raw field name prev_event_sha256
- welcome checklist line says who refreshes what
- setup privacy note explains the local model instead of ending in
a bare model tag
- default project renders localized ('Allgemein') in the switcher;
sealed-area status pills explain run state on hover
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
- audit type filter: 'Alle'/'All' capitalized like every other label
- settings -> setup wizard: close Settings first (one modal layer),
stronger wizard scrim
- try-out setup profile discloses the upgrade path to a regulated
profile
- store maturity pills explain themselves on hover (experimental/
published/planned)
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
One-click installs showed no trust signal at all (top security
finding of the usertest panel). Every install path — store card,
detail sheet, and the flow list's quick fix — now routes through
one confirmation dialog showing what the hub actually knows
before download: origin store, version, license, maturity, and
required services/capabilities, plus the sandbox model and an
honest note that per-entry signature status is not in the store
index yet (verification happens hub-side at install). Widget
tests cover content and confirm/cancel semantics.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Procurement personas found no vendor, version, license, or
support information anywhere in the app (a hard checklist fail
for regulated buyers). Settings gains an About category:
product name, Studio + running-hub version, vendor Flemming.AI,
author, Apache 2.0 license, contact address, and the docs URL —
every value selectable and one-click copyable. Studio version
constant moved to data/about_info.dart so sidebar tag and About
can never drift. Bumps Studio to 0.71.0.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The icon-only rail forced first-time users to guess (usertest:
Senior, a11y, UX personas). Three changes:
- Nav tooltips appear instantly and carry the page shortcut
(Cmd+1..9, Ctrl on non-mac — Ctrl activators added); expanded
labels show the same hint. Explicit button semantics for
screen readers on every destination.
- A visible 'Search & commands' row above the footer opens the
existing Cmd+K palette, which nothing in the UI advertised.
- Settings -> Appearance gains 'Keep the navigation expanded':
pins the rail with permanent labels (persisted preference).
Footer strip and pillar toggle made overflow-safe for the
animating rail; responsive test scrolls the by-design scrollable
destinations list.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Stefan's live findings, all four addressed at the root:
- 'In 3 Fragen loslegen' read like ad copy → the entry is now plainly
'Einrichtung starten' / 'Start setup'.
- The setup button sat permanently on the Welcome page of a running
app ('setup after the app runs is backwards' — reported twice). A
fresh install now starts INSIDE the setup: SetupGateScreen hosts
the wizard embedded as the page (new embedded/onFinished modes on
GuidedSetupDialog), with an explicit 'Später einrichten' skip.
Welcome loses the setup button entirely and stays a calm intro.
- Re-running the setup later lives in Settings → General ('Run setup
again…'), the single post-first-run home.
- 'You must grant access first and only then see what will be done':
the preview used to spawn the chain CLI, whose first run could pop
the macOS permission prompt BEFORE the plan was ever shown. The
preview now calls the new PlanSetup RPC over the live hub
connection (no subprocess, nothing granted); the CLI remains only
a fallback when no hub is reachable — and applying stays the
explicit, separate step.
Widget tests: gate hosts the wizard + skip/cancel leave it; CLI-path
tests drive the fallback through the new hub-preview test seam.
Suite 76 green, analyze clean.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Review follow-ups on the auth-status work:
- Both daemon-start paths classified an auth-rejected hub as "daemon
dead" via healthy() and showed a start-failure dialog while the
shell banner above correctly blamed the token. They now share
daemonAnswers(): only an unreachable probe counts as down.
- An auth-rejected poll now re-reads ~/.chain/hub-auth-token and
reconnects when the file changed, so a token fixed outside Studio
(CLI, editor) heals the connection without a restart — previously
the client kept the stale in-memory token forever and the banner's
own advice could not work.
- An endpoint switch resets the failure streak, so a stale in-flight
probe can no longer let the unreachable banner blame the new
endpoint for the old one's misses.
- The auth-policy panel re-queries when the hub token is saved or
cleared in the panel above (reloadTick), instead of keeping a
stale admin-denied hint; it also renders the hub's new
reload_required flag as a pending-reload warning (DE+EN).
- today-pipeline.md still documented ~/.fai/today after the rename;
the FAB theme comment now states the both-themes intent.
flutter analyze clean; 71 tests green including four new ones.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Settings → Security now shows the hub's effective auth policy via the
new read-only AuthStatus RPC: active token validator (static / jwt-rs256
with issuer, audience, JWKS source), anonymous-access warning, per-token
cards with scope grants, env-var presence and rate limits, plus a
localized admin-denied story for non-admin tokens. Live-reloads on
endpoint change.
Also fixes a batch of fai→chain rename leftovers this panel's
verification uncovered: hub_auth_token.dart and registry_token.dart
read/wrote ~/.fai/ while the hub reads ~/.chain/ (stored registry
tokens never reached the hub), today_story_loader + tools/today used
~/.fai/today, chain_log legacy ~/.fai/logs migration removed per the
no-legacy-recognisers decision, and UI strings still advertised the
retired .fai bundle extension.
Includes 5 widget tests for the panel, an integration-test screenshot
harness (auth_policy_shots_test.dart, guide-shots style), and DE+EN
l10n. flutter analyze clean, 58 tests green.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Field test of the setup wizard surfaced three trust breaks in one run:
an unexplained macOS Documents permission prompt, a perceived crash,
and an error message whose copy button could not be reached.
Root causes and fixes:
- chain init failures were shown as a SnackBar, which lands BEHIND the
wizard's modal barrier: dimmed, clipped, copy unreachable — and the
click aimed at it hit the barrier, dismissing the whole wizard with
all answers (the perceived crash). Errors now open a modal dialog
ABOVE the wizard via showChainErrorDialog with a copyable detail
block, and the wizard is no longer barrier-dismissible.
- When the resolved chain binary is older than Studio and rejects
--plan-json, the wizard now explains the version skew in plain
language (binary path + update path) instead of leaking a raw clap
usage error. A missing binary gets its own localized story.
- Step 3 announces which chain binary the preview will execute; when
that binary physically lives (symlinks resolved) in a TCC-protected
folder, the wizard pre-explains the macOS folder prompt.
Supporting changes: FriendlyError passes through friendlyError()
unchanged so call sites can ship precise localized stories through the
shared presentation; SystemActions gains resolvedChainBinary() plus
run/resolve test seams; ChainErrorBox hugs its content instead of
filling an unbounded dialog; the wizard's answers file is written
synchronously (the async dart:io variants never complete under the
widget-test fake-async zone).
Verified: flutter analyze clean, 53 tests green (6 new wizard error-
path tests incl. clipboard round-trip), plus a live GUI walk on macOS
in dark + light with a stale binary (skew dialog, copy verified via
clipboard) and with the real binary (TCC pre-explanation with the
resolved path, full plan preview).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Two settings bugs the operator hit:
1. 'Test connection' appeared to do nothing. The result/error panel
renders at the bottom of the System-AI editor's scrollable,
600px-capped form, so on a tall dialog it lands below the fold —
the footer button flickers 'Testing…' and nothing visible changes.
The editor now holds a ScrollController and animates the content
to reveal the outcome whenever a test result or error appears.
2. The welcome onboarding checklist kept 'connect System-AI'
unchecked right after saving a working config. The checklist row
opens the Settings dialog (an overlay, not a navigation), and on
close the checklist never re-probed — so it showed the stale
pre-config state. Both settings-opening rows (System-AI, MCP) now
re-probe when the dialog closes; navigation-based rows already
self-heal on return.
Studio suite green; flutter analyze clean.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
- Regulated path finishes without a terminal: the signed-source
state offers 'Add a signed source…' (stores dialog with pin-a-key)
plus the per-module install buttons and a plain-language hint why
pinning the publisher's key matters — instead of a hint with no
affordance.
- Apply warnings (e.g. the empty-trusted-publishers caveat) surface
selectable in the done state instead of being swallowed.
- Truthful preview: new lines state which machine is being set up
(server/container targets configure THIS machine), that regulated
profiles always get the hash-chained audit log (even with WORM
off), and that the curated reading list is stored with the setup
record.
- Language pass: onboarding checklist in Sie-form + 'System-KI'
(was du-form + 'System-AI'), 'Audit-Sperre' jargon replaced,
answers file moved to a private per-dialog temp dir.
- Screenshot harness: GUIDE_SHOTS_THEME=light for light-parity
proof runs.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The wizard's first step now offers 'or just describe what you want
to do': the goal goes to the configured system AI, which maps it
onto the menu answers (validated against strict enum whitelists —
a hallucinated value can never reach the engine). The suggestion
comes back as an editable plain-language reflection ('this is how I
read your task') the operator can adjust step-by-step or take to
the same preview/apply the menu path uses. Trust rules per
guided-setup.md: suggestion only (never auto-apply), a privacy line
states whether the description is processed locally or sent to a
provider, and without a configured system AI the section explains
that the menu always works — no dead end.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Post-apply the wizard now renders real Studio actions instead of CLI
text: a start-hub button that polls until the daemon answers,
per-module install buttons (capability-name install via the hub's
store index) with done/progress states, and an open-the-starter-flow
button that navigates to the Flows page. Regulated plans explain in
plain language that modules come from a signed source; the preview
offers 'allow installing from the public store' as one deliberate,
reversible switch that re-assembles the plan (allow_unsigned_modules).
Fresh installs (no config, no setup-plan.yaml) auto-open the wizard
once per run — the wizard IS the onboarding — and it steps back once
a setup exists. The welcome CTA is framed honestly ('get started in
3 questions'), and after the wizard closes the onboarding checklist
remounts, re-probes, and says what the assistant already covered
(profile line from setup-plan.yaml) instead of acting as a second,
disconnected onboarding surface.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Reworks the Setup-Assistent toward the zero-learning-curve bar
(docs/architecture/guided-setup.md, phase 1.1):
- Every scenario/intent/target choice is now a localized option CARD
with a one-line plain-language explanation of what it configures
(DE+EN, Sie-form) — replacing the bare dropdowns whose labels were
English enum humanizations ('Regulated Production', 'This Laptop').
- Three explained steps with a 'Schritt n von 3' progress line
(stakes → task → environment); the two adaptive toggles move to the
last step in plain language (no 'air-gapped' jargon).
- The review step renders a localized PLAIN-LANGUAGE summary built
from 'chain init --answers --plan-json' (the structured SetupPlan) —
'Ch∆In richtet einen regulierten Betrieb ein: signierte Module
verlangt · … · geändert wird nur ~/.chain/config.yaml' — instead of
echoing the CLI's English prose. Warns when an existing config will
be overwritten. After apply: a plain 'Fertig' + next steps.
flutter analyze clean; widget tests for the step flow + German option
labels. Remaining per plan: clickable follow-up actions, signature
dead-end fix, placement/auto-open, and the LLM free-text path.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The workspace switcher now lists the operator's sealed areas (read from
~/.chain/sealed/ manifests, the same source the CLI uses) below the
shared projects, each with a lock icon and a running/stopped status.
Selecting one is a real connection switch: Studio reconnects its hub
client to the area's own port with a full state reload — one window,
one truth. A stopped area is started first (chain project start) with a
visible notice; a failure surfaces as a copyable error and rolls back to
the shared hub.
While in a sealed area an identity bar under the AppBar is painted in
the area's accent colour and names it, with a one-click Leave back to
the shared hub. The area colour is marking, not theming — Studio's blue
stays the app accent. Selecting a shared project from inside an area
switches the connection back first. The sealed connection is never
persisted across restarts.
New: SealedAreaService (manifest + PID discovery), Workspace sealed
switch logic, ChainSealedIdentityBar, SystemActions.chainProjectStart.
l10n DE+EN. flutter analyze clean; 33 tests green (switcher lists sealed
with lock+status, pill shows active area, identity bar renders in the
area colour). Runtime plumbing (discovery, start, endpoint, reach)
verified headlessly against real sealed instances under a redirected
HOME; the identity-bar screenshot is deferred (display click-automation
failed after sleep on the shared desktop — an environment issue, not a
code gap; the visible components are widget-tested).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Multi-project stage 1 against the shared hub (platform design
docs/architecture/projects.md, § Studio):
- ChainWorkspaceSwitcher in the Audit + Approvals AppBars: lists the
registry (colour dot per project, shield for protected, honesty
tooltip), 'All projects' stays reachable — a filter, not a jail.
Selection is persisted and shared via the Workspace notifier.
- Audit page: list query AND live stream re-scoped hub-side on switch.
- Approvals page: pending + history scoped; the sidebar badge counts
the active workspace's pending approvals.
- Flow runs are stamped with the active workspace; a flow file
carrying its own project: keeps it (file wins, CLI semantics).
- Data layer: listProjects/ProjectRef; project fields on AuditEvent,
PendingApproval(+Record), SavedFlow; project params through
HubService. l10n DE+EN. Widget tests for the switcher contract.
Visual verification (light+dark screenshots) still pending — the
shared desktop was in active use; code paths are covered by
flutter test (26 green).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
A Welcome 'Setup assistant' button opens a wizard that collects
scenario / intent / target (+ approval & data-local toggles), then calls
`chain init --answers` to preview the assembled plan and `--apply --force`
to write the config — reusing the Rust deterministic engine, no logic
duplicated. New SystemActions.chainInit; copyable errors via
showFaiProcessError; EN+DE l10n. analyze clean; smoke test + existing
welcome/sidebar tests pass.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The module-store manager's 'Add a store' form gains an optional PEM
public-key field; when set it pins that publisher key to the store
(per-store signing trust). Localized DE/EN.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Audit sweep after the System-AI fix: the Settings dialog rendered its
two _error states as a bare Text (hub-endpoint save, default-scope
edit), and the Audit live-status bar showed the raw load failure in a
non-selectable Text (the only place that failure surfaces — the
empty-state below shows just a generic hint). Route the Settings errors
through ChainErrorBox and make the Audit disconnected text a
SelectableText. Errors must always be clipboard-copyable.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The System-AI editor rendered its failure (test connection, save, model
pull) as a bare Text(_error) — selectable-but-not-copyable, the exact
thing the operator needs to paste back. Route it through ChainErrorBox
(selectable + one-tap copy button), and show a failed test result via
ChainErrorBox too instead of a plain SelectableText.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Use the raw store-index file in the public chain-modules/reclaim repo
(git.flemming.ai/chain-modules/reclaim/raw/branch/main/store.yaml) —
stable, no release-tag churn, fetched by the hub's StoreIndex::from_url.
The availability probe flips the row to 'Add' automatically once reclaim
publishes the file.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
- store: repair the filter dialog crash (a Spacer lived directly in
AlertDialog.actions, which is an OverflowBar, not a Flex — it threw
and rendered a broken dialog). Buttons now sit in a Row.
- store: promote the module-store manager from a bare icon to a
labelled 'Add store' button, and fully localize the dialog (DE/EN).
- store: add a curated 'Suggested stores' shelf with one-click
add/remove (first entry: Recl∆Im). Each suggestion is probed for
reachability and shows 'not available yet' until its index is
published, instead of failing only on click. Fail-open on network
errors so a transient hiccup never hides a real store.
- sidebar: the channel pill is now a one-click channel switcher
(menu with per-channel running state + active check; switching
writes ~/.chain/current-channel, restarts the daemon, and Studio
repoints to the new channel).
- approvals: lead the card with the human prompt ('what am I
releasing?') and demote the flow/step id to a metadata line; clear
fallback when the step left the prompt empty.
- welcome: tidy the docs grid into equal-height paired rows with a
full-width trailing card for the odd one out.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
A store icon in the Store app bar opens a dialog that lists the
configured module stores + the bundled seed (with per-source module
counts), lets the operator add a store by index URL (the hub fetches +
merges it live so its modules appear immediately), and remove a store.
Backed by the new ListStores/AddStore/RemoveStore RPCs + SDK methods.
This is how a domain app's published modules (e.g. reclaim's) become
visible in the Store without touching the CLI.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
The Studio design system, widgets and helpers carried a Fai* / fai_
prefix (FaiSpace, FaiColors, FaiTheme, FaiLog, 17 fai_*.dart files, the
faiBinary* l10n keys). Studio is the Ch∆In product, so rename them to
Chain* / chain_ — carefully preserving English fail/failure/failed.
Also fix stale references: the 'fai' binary in l10n strings -> 'chain',
FAI_* env vars (FAI_BIN/DATA_DIR/MODULES_DIR/TODAY/BOOTSTRAP_TOKEN) ->
CHAIN_*, fai_platform -> fai_chain, fai_hub -> chain_hub. Vendor
security-hook tooling (FAI_BANNED_TERMS_FILE) + the .fai bundle ext left.
flutter analyze + test: clean (20 passed).
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Studio is the Ch∆In product's GUI, not a F∆I-vendor app. Rename the
Flutter package, all package: imports, and the build identity across
platforms: linux/windows CMake BINARY_NAME + project, Windows Runner.rc
fields, macOS PRODUCT_NAME / bundle id (ai.flemming.chain.chainStudio) /
.app + scheme BuildableName. Update the client-SDK + flow-editor deps to
their renamed chain_* packages (path + git URL). Company/copyright fields
now read Flemming.AI. flutter analyze: clean.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Track the platform rename: the hub spawn path is now ~/.chain/bin/chain
(was ~/.fai/bin/fai.exe on Windows — both dir and binary were stale, so
Studio could not launch the hub after the config-dir rename), the
~/.fai/* help strings become ~/.chain/*, FAI_REGISTRY_TOKEN ->
CHAIN_REGISTRY_TOKEN, and the two in-app doc URLs point at the public
fai/chain repo (fai/platform was renamed to the private fai/chain-private).
The .fai module bundle extension is left unchanged (format phase).
flutter analyze: no issues.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
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>
- Strip ANSI / control characters when reading log files so daemon
logs that carry colour escape codes render clean instead of as
garbled special characters (the source side is fixed in the hub
too; this is the defensive belt for existing files).
- Generalise the file viewer (read top-down + plain mode) and add an
in-Studio "View" affordance for config.yaml / .yml / .toml on the
Doctor paths panel, so config is readable without leaving Studio.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
Brightness-aware elevation tokens (FaiElevation low/medium) applied
to cards, sheets and the sidebar active item; gentle hover-lift on
Store + Doc cards; a longer, eased sidebar expand using the
under-used FaiMotion.slow; and a branded empty state carrying the
F∆I delta mark with warmer spacing. Subtle, Linear/Raycast-style —
both light and dark themes tuned.
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>
When operators open a module's detail sheet from the Store,
the Permissions section now grows a small help icon next to
the section header. Tapping it opens the bundled security.md
explainer in the existing bottom-sheet reader, the same
in-app help system Approvals + Audit + Doctor already use.
Closes the zero-learning-curve gap: every operator-facing
declaration of permissions ('net: api.openai.com', 'fs.read:
…') now has a clickable affordance to learn what those
permission categories mean and how the sandbox enforces them.
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 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>