Commit graph

33 commits

Author SHA1 Message Date
flemming-it
b40da56b8d fix(studio): localize sidebar tooltips that stayed English
Some checks failed
Security / Security check (push) Failing after 1s
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>
2026-05-31 23:28:49 +02:00
flemming-it
e522267ec1 feat(studio): pencil-into-editor + back button + collapsible rail
Some checks failed
Security / Security check (push) Failing after 2s
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>
2026-05-30 12:04:05 +02:00
flemming-it
bb793e64a9 feat(studio): inline flow editor — YAML highlighting + Run (v0.50.0)
Some checks failed
Security / Security check (push) Failing after 1s
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>
2026-05-30 01:10:05 +02:00
flemming-it
1903babc5a feat(settings): multi-version uninstall picker + default_scope editor
Some checks failed
Security / Security check (push) Failing after 2s
Two operator surfaces shipped together:

**Multi-version uninstall picker** — when more than one version
of a `(provider, name)` is installed side-by-side, both the
module-sheet "Uninstall" affordance and the store-detail
"Uninstall" affordance now ask the operator which version to
remove before calling the RPC. The hub's wire-level support for
this (UninstallModuleRequest.version) was already there; Studio
just wasn't using it. Picker pre-selects the highest version so
single-version flows still take one click.

  - `HubService.installedVersions(name)` enumerates the installed
    versions via the capabilities list.
  - `HubService.uninstallModule(name, version: ...)` forwards
    the version into the RPC.
  - `_UninstallVersionPickerDialog` (module sheet) and
    `_StoreUninstallVersionPickerDialog` (store) host the
    picker — separate widgets so each surface can evolve copy
    independently. Uses `RadioGroup<String>` for Flutter
    3.32+ deprecation compliance.

**Default scope editor** — new DEFAULT SCOPE panel in Settings
that calls the freshly-added HubAdmin RPCs
`GetDefaultScope` / `SetDefaultScope`. Operators can:

  - reorder publisher segments with up/down buttons
    (first match wins in the bare-form resolver),
  - delete entries (hub still rejects empty list — Studio
    surfaces the constraint inline),
  - add arbitrary entries via the text field,
  - add catalog-known publishers via suggestion chips
    (sorted alphabetically, populated from the catalog).

Every change persists to `~/.fai/config.yaml` via the hub
and hot-swaps the in-memory copy without a daemon restart.

Bumped pubspec to 0.48.0. dart analyze clean (No issues
found!); flutter test green (11 tests).

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-29 00:21:51 +02:00
flemming-it
327bc0fea2 feat(store): advisory-version badge for federated entries
Some checks failed
Security / Security check (push) Failing after 1s
Store cards + detail-sheet now prefix the version pill with
`~` and attach a tooltip for federated entries (MCP / n8n /
Temporal). Upstream services don't honour semver, so the
version pill is a label, not a contract — the tilde says
"this number may change without a version bump."

Three render sites updated to stay visually consistent:
- compact card pill in grid view
- expanded card pill row
- detail-sheet header pill cluster

EN + DE l10n entries added (`storeAdvisoryVersionTooltip`)
and `app_localizations.dart` regenerated.

dart analyze clean; flutter test green (11 tests).

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-28 23:26:49 +02:00
flemming-it
c5a96bc8d4 feat(settings): hub bearer-token panel + auto-attach on every RPC
Some checks failed
Security / Security check (push) Failing after 1s
Studio gains a new "HUB AUTHENTICATION" panel in Settings
mirroring the existing registry-credentials panel:

- `lib/data/hub_auth_token.dart` — `~/.fai/hub-auth-token`
  helper (read / write / clear, mode 0600 on Unix). Sister
  of `RegistryToken` with the same on-disk hygiene.
- `HubService.loadPersistedEndpoint` now reads the token at
  startup and reconnects with it. `reconnect()` grew an
  `authToken:` parameter with a sentinel that distinguishes
  "keep current" from "drop". `reloadAuthToken()` is the
  one-liner Settings calls after save / clear.
- `_HubAuthTokenPanel` in `fai_settings_dialog.dart` — paste
  with show/hide toggle, save button, clear button, status
  pill ("Configured (40 chars)" / "Not set (anonymous)"),
  storage-location hint. Trimmed token length only — the
  secret never round-trips back into the UI after save.
- EN + DE ARB entries (`hubAuthToken*`) + regenerated
  `app_localizations.dart` keep the bilingual surface
  consistent.

The hub side (auth.tokens config + tower middleware) shipped
on the platform side 2026-05-28 (43a54a2). Until now an
operator had no GUI path to consume it: they had to find the
file in their home dir, paste the token by hand, and bounce
Studio. This panel closes that loop.

Bumped pubspec to 0.47.0. dart analyze clean (No issues
found!); flutter test green (11 tests pass).

Block E item 1 of 4 done. Capability-picker badges,
default_scope editor, multi-version uninstall picker follow.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-28 23:24:15 +02:00
flemming-it
ceed8aab02 feat(studio): temporal source bucket + provenance pill on store grid
Some checks failed
Security / Security check (push) Failing after 1s
Adds the missing UI surface for SPARK / Temporal federated
capabilities. Previously only mcp and n8n showed provenance
pills; temporal-sourced entries fell through to the default
"no badge" case.

  - storeSourceTemporal + storeProvenanceTemporal l10n keys
    (en + de). Pill format: "temporal · <provider>", icon
    Icons.timer_outlined, accent tone.
  - Source filter chip row gains a "Temporal" option in the
    Store filter dialog.
  - `_StoreProvenancePill` adds a `case 'temporal'` branch.

Operators federating a SPARK cluster (`services: -
kind: temporal`) now see their workflows visually labelled
as Temporal-sourced in the Store grid + filter UI.

dart analyze green. No test changes required — the
provenance pill is render-only.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-28 14:15:39 +02:00
flemming-it
91706cea2b feat(studio): theme-plugin picker + translate-hook wrapper
Some checks failed
Security / Security check (push) Failing after 2s
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>
2026-05-25 23:02:21 +02:00
flemming-it
c11461b0f9 feat(studio): trust pass — friendly errors + store clarity + MCP i18n
Second half of the May-2026 trust pass. Drops the wall of
gRPC trailers from every error surface and makes the Store
honest about what is and isn't installable.

Friendly errors:

- New `friendlyError(Object, AppLocalizations)` mapper turns
  GrpcError + arbitrary throwables into a localised headline,
  optional recovery hint, and a verbatim detail string kept
  behind a "Show details" expander. Duck-typed on `.code` /
  `.message` so Studio doesn't have to depend on package:grpc
  directly.
- `FaiErrorBox` gains an `error:` constructor that runs the
  mapper. Every call site that used to render
  `snap.error.toString()` (flows, welcome, store) switches to
  it.
- 9 .arb entries per locale cover the gRPC codes we actually
  emit (INVALID_ARGUMENT, NOT_FOUND, ALREADY_EXISTS,
  PERMISSION_DENIED, FAILED_PRECONDITION, INTERNAL,
  UNAVAILABLE, UNAUTHENTICATED) plus copy/details affordances.
- `test/friendly_error_test.dart` — 6 unit tests for the
  mapper. Covers the mapping table, locale-switching, and the
  non-gRPC fallback so future regressions show up in CI.

Capability discovery:

- New `HubService.allCapabilities()` reads the kind-aware
  capability list (wasm + builtin + federated) and returns a
  Dart-side `CapabilityInfo` value type. The flow page's
  missing-dependency check uses it so `system.approval` and
  federated MCP/n8n tools count as "available" — fixes the
  Run button staying disabled forever.
- `HubService.listModules()` filters to kind=wasm so the
  Modules page doesn't sprout synthetic "system" entries that
  the operator can't uninstall.

Store clarity:

- New "Installable only" filter, on by default. Roughly 2/3
  of seed entries currently carry `status: planned`; the
  default view stops being noise.
- Featured-strip cards for planned modules now show a
  "Coming soon" pill instead of an empty action area.
- Main-grid cards for non-installable modules dim to 60%
  opacity so the eye lands on actionable cards first.
- Detail-sheet "Nicht installierbar" tooltip → inline hint
  box. The reason is visible without hovering.

MCP localisation:

- `_kMcpSuggestions` no longer holds 11 hardcoded English
  description strings. The `description` field is replaced
  with a `resolveDescription(AppLocalizations)` lookup that
  switches on the suggestion `name` to read the matching
  `mcpSuggestion*Desc` .arb key. EN + DE shipped.
- New `FaiEnBadge` widget renders a small `[EN]` pill when
  the active locale isn't English. Used next to MCP /
  federated store entries' tagline + description because
  the server supplies them in English and we can't translate
  on the fly yet — the badge is the honest signal until the
  planned `studio.translate` plugin lands.

Plus housekeeping: removed the unused `_keepImport` lint
escape in the test and the dangling library doc-comment in
`format.dart`.

Signed-off-by: flemming-it <sf@flemming.it>
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-25 12:36:14 +02:00
flemming-it
34f2b7b313 feat(studio): typed flow outputs + on-disk docs + UX polish
Documentation system (the original "whole docs system is broken"
complaint):
- Studio reads inline docs from disk via the new
  getInstalledModuleDocs RPC. No network, no auth, no provider
  lock-in.
- store.dart probes for MODULE.md eagerly when a module-detail
  sheet opens (a single file stat). The Documentation section
  only renders when the bundle actually shipped docs.
- Shared FaiTheme.markdownStyle helper used by Welcome's
  DocReaderSheet and Store's DocsPanel so every inline doc
  reads in the same typography. The shared style forces
  code.backgroundColor = transparent to suppress the per-span
  bands flutter_markdown's default code style draws on dark
  themes.

Flow run dialog:
- Run-button gating now strips the @version suffix from
  installed capabilities before the contains() check. Without
  this fix the button stayed disabled for every flow with
  dependencies, even after a successful install.
- Studio derives a MIME type from the picked file's extension
  (small per-suffix map; .pdf, .docx, .txt, .json, ...) and
  forwards it to the hub. Fixes "unsupported MIME type:
  application/octet-stream" from text.extract.
- Dialog title tracks the future's state: running -> "extract
  laeuft", success -> "extract -- Ergebnis", failure ->
  "extract -- fehlgeschlagen".
- Output rendering moved from String stringification to a
  sealed FlowOutput type (Text / Json / Bytes / File / Unknown).
  A new FaiFlowOutput widget dispatches per variant: markdown
  for text/markdown (heuristic), pretty JSON for proto Struct,
  inline image preview + Save-As for bytes, Open for file URIs.

Byte sizes:
- New humanBytes() helper renders 23.3 kB / 1.04 MB style values
  with three significant digits, matching Finder / GNOME Files.
  Wired into flow-card pills, picked-file readouts, and the
  bytes-payload preview line.

Signed-off-by: flemming-it <sf@flemming.it>
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-25 11:17:58 +02:00
flemming-it
7416c34d7c feat(studio): contextual "Add registry token" CTA on install auth wall (v0.46.0)
Some checks are pending
Security / Security check (push) Waiting to run
The hub's v0.10.91 install error message already tells the
operator exactly what to do — set FAI_REGISTRY_TOKEN or save
to ~/.fai/registry-token via Settings. But that text lives in
a FaiErrorBox inside the install dialog. Reading the buried
recovery instruction, finding Settings, scrolling to the
Registry credentials panel, pasting, then re-clicking the
install pill is five hops for what should be one.

When at least one item in the install dialog fails with the
"registry returned an HTML page" / "no registry token
configured" signature, the dialog now surfaces a prominent
filled "Add registry token" button next to Close. Click closes
the install dialog and opens the Settings dialog directly so
the operator pastes + saves without losing context. After
Settings dismisses, the operator is back at the flow card and
re-clicks the same pill to retry.

Detection is signature-based (substring match on the hub's
error text) so it activates whether the install was a single-
pill click or an "Install all" sequence. Other failure modes
(network down, signature invalid, sha mismatch) still show
just the Close button — the CTA is specific to the auth-wall
case it can actually unblock.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 19:32:13 +02:00
flemming-it
3db7db7330 feat(studio): hub maintenance panel with reset action (v0.45.0)
Some checks are pending
Security / Security check (push) Waiting to run
UI parity with the platform CLI: `fai reset` (v0.10.93) is now
reachable from Settings. The panel shows the reset blurb plus
two checkboxes mirroring the CLI flags (Keep modules / Keep
audit log + saved flows), then a destructive-styled "Reset hub
state" button. Click goes through a confirm dialog before
SystemActions.faiReset is invoked.

After the CLI returns, Studio bounces its gRPC channel against
the same endpoint (the daemon restarts on the same channel +
port, so no Settings change needed) and reloads every panel:
channel status, system AI, MCP clients, n8n endpoints, registry
token. Output (success or failure) is shown in a copyable error
box so any unexpected stderr lands somewhere the operator can
paste from.

Sits at the bottom of the dialog under "HUB MAINTENANCE",
separated from the everyday config so a destructive button
doesn't crowd the day-to-day controls.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 18:52:19 +02:00
flemming-it
878f0a4e28 feat(studio): registry credentials panel in Settings (v0.44.0)
The hub install path now reads `~/.fai/registry-token` as a
fallback when `FAI_REGISTRY_TOKEN` is unset (platform v0.10.92).
Studio now writes that file directly: a fresh operator pastes
the PAT into Settings → Registry credentials, hits Save, and
the next install attempt resolves the auth wall without any
shell or env-var setup.

The token never round-trips back into Studio after save —
status is shown only as "Configured (40 chars)" / "Not set"
with no display of the secret itself. The Clear action deletes
the file. On Unix the file is chmod-ed to 0600 (owner-only
read/write); Windows leaves the default user ACL in place.

Storage is strictly local: `~/.fai/registry-token`, never sent
to a remote service. The hint text under the field says so to
make the data flow explicit.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 13:38:47 +02:00
flemming-it
c389876a2f feat(studio): clickable install pills + open-in-editor on flow cards (v0.43.0)
When a saved flow is missing modules, each capability pill in the
"Needs:" row is now clickable: tap one and a per-item progress
dialog walks the bare capability name through installModule, then
the flows page refreshes. When more than one capability is missing,
an extra "Install all (N)" button runs the same dialog over the
whole list sequentially so a fresh operator can take an unrunnable
flow and one click later have its dependencies resolved.

Also adds a pencil icon next to Run that hands the YAML path to
the OS via SystemActions.openInOs — the operator's default editor
for .yaml decides what opens. Makes the path next to the flow name
actionable instead of decorative.

The install dialog renders one row per spec with a status icon
(pending circle / spinner / check / error), shows the installed
version on success, and surfaces the full error in a copyable
FaiErrorBox on failure so the operator can paste it back.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-09 11:58:24 +02:00
flemming-it
2002486828 feat(studio): operator QoL — flow runnability, audit grouping, batch approvals, welcome celebration (v0.42.0)
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>
2026-05-09 11:24:57 +02:00
flemming-it
f90d8cc7a7 feat(studio): typed run-flow form, file picker, copyable errors (v0.41.0)
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>
2026-05-09 10:18:08 +02:00
flemming-it
63e19974c0 feat(studio): @-syntax file inputs in flows, copyable doc errors, services-row overflow (v0.40.0)
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>
2026-05-09 02:07:31 +02:00
flemming-it
eb447c6ec0 fix(studio): Welcome layout exception, drop awkward header, Store at slot 2 (v0.39.1)
Three fixes against the v0.39.0 user feedback.

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

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

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

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

The four checked signals:

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

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

UX:

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

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

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

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

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

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

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

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

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

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

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

Phase A:

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

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

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

Changes:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Changes:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Removed the now-orphaned storeFederationNudge* ARB keys.

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

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

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

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

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

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

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

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

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

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

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 01:39:42 +02:00
flemming-it
ee83eb851a feat(studio): app-wide i18n via flutter_localizations (v0.24.0)
Replaces the Store-only DE/EN toggle with an app-wide one
parked in the sidebar footer next to the theme button.
Pressing it flips every translated string at once: nav
labels, page titles, common buttons, the bilingual
store-index content.

Implementation:
- Adds `flutter_localizations` + `intl` to pubspec, plus
  `flutter.generate: true` so `flutter gen-l10n` runs in the
  build pipeline.
- ARB sources at `lib/l10n/app_en.arb` and `app_de.arb`. The
  EN file is the template; DE carries the German strings.
  Initial coverage: navigation, common buttons, page titles,
  channels / store / audit / modules / approvals headers,
  hub-unreachable copy, MCP + n8n panel headers + hints.
  Rest of the UI strings are still English-literal — those
  fall in incrementally as we touch each surface.
- Generated `AppLocalizations` lives at
  `lib/l10n/app_localizations*.dart` (regenerated via
  `flutter gen-l10n` on every ARB edit).
- `StudioAppState` gains `localeNotifier` alongside
  `modeNotifier`; persisted via SharedPreferences key
  `locale.code`.
- Sidebar `_LanguageToggle` reads/writes through the
  notifier. The Store's per-page locale state is gone:
  `_locale` now reads `Localizations.localeOf(context)
  .languageCode`, so the bilingual store-index content
  follows the global setting without a second toggle.
- `_NavPage.label` becomes `_NavPage.id` + `labelOf(context)`;
  Cmd+K palette and Sidebar both read the localized label.

Out of scope this iteration: localizing the remaining
~80% of UI strings (Settings dialog labels, Store search
hint, error messages). Those land incrementally — the i18n
infrastructure now means each is a one-line ARB edit + one
call-site swap.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-05-08 01:19:32 +02:00