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

@ -1003,18 +1003,37 @@ class _DocsRow extends StatelessWidget {
],
);
}
final cardWidth = (constraints.maxWidth - ChainSpace.md) / 2;
return Wrap(
spacing: ChainSpace.md,
runSpacing: ChainSpace.md,
children: [
for (final d in _kDocs)
SizedBox(
width: cardWidth,
child: _DocCard(entry: d),
// Pair the cards into equal-height rows: IntrinsicHeight
// + stretch makes the two cards in a row match height
// instead of the Wrap's ragged tops (their blurbs differ
// in length, especially across locales). An odd trailing
// card spans the full width so it reads as intentional
// rather than a lonely half-box with dead space beside it.
final rows = <Widget>[];
for (var i = 0; i < _kDocs.length; i += 2) {
if (rows.isNotEmpty) {
rows.add(const SizedBox(height: ChainSpace.md));
}
final left = _kDocs[i];
final right = i + 1 < _kDocs.length ? _kDocs[i + 1] : null;
if (right == null) {
rows.add(_DocCard(entry: left));
} else {
rows.add(
IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(child: _DocCard(entry: left)),
const SizedBox(width: ChainSpace.md),
Expanded(child: _DocCard(entry: right)),
],
),
),
],
);
);
}
}
return Column(children: rows);
},
),
],