feat(studio): clearer store + channel switcher + approvals & welcome polish
Some checks failed
Security / Security check (push) Failing after 2s

- 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>
This commit is contained in:
flemming-it 2026-06-18 21:29:39 +02:00
parent 62ed3951e5
commit 5220f19ff8
10 changed files with 865 additions and 148 deletions

View file

@ -527,6 +527,9 @@ class _ApprovalCard extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Status row: selection + state + expiry. No flow id here
// the headline below leads with WHAT is being asked, not the
// technical flow name.
Row(
children: [
Checkbox(
@ -540,15 +543,7 @@ class _ApprovalCard extends StatelessWidget {
tone: ChainPillTone.warning,
icon: Icons.pending_outlined,
),
const SizedBox(width: ChainSpace.sm),
Expanded(
child: Text(
'${approval.flowName} ${approval.stepId}',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
),
const Spacer(),
if (approval.expiresAt != null)
ChainPill(
label: _expiresInLabel(context, approval.expiresAt!),
@ -558,7 +553,38 @@ class _ApprovalCard extends StatelessWidget {
],
),
const SizedBox(height: ChainSpace.lg),
Text(approval.prompt, style: theme.textTheme.bodyLarge),
// Headline = the human question the flow author wrote ("what
// am I approving"). Falls back to a clear sentence when the
// step left the prompt empty, so the card is never reduced to
// a cryptic flow id.
Text(
approval.prompt.trim().isEmpty
? l.approvalsRequestFallback
: approval.prompt,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
// Technical context, demoted to a small metadata line.
Row(
children: [
Icon(
Icons.account_tree_outlined,
size: 13,
color: theme.colorScheme.onSurfaceVariant,
),
const SizedBox(width: 6),
Expanded(
child: Text(
l.approvalsFlowStepMeta(approval.flowName, approval.stepId),
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
],
),
if (approval.payloadPreview != null) ...[
const SizedBox(height: ChainSpace.md),
Padding(