feat(studio): rich theme picker (presets + custom colour) + editor 0.11.0
Some checks failed
Security / Security check (push) Failing after 1s
Some checks failed
Security / Security check (push) Failing after 1s
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>
This commit is contained in:
parent
69864da934
commit
c1c60d5434
30 changed files with 1280 additions and 846 deletions
|
|
@ -21,6 +21,7 @@ class _ApprovalsPageState extends State<ApprovalsPage>
|
|||
late final TabController _tab;
|
||||
late Future<List<ApprovalRecord>> _pendingFuture;
|
||||
late Future<List<ApprovalRecord>> _historyFuture;
|
||||
|
||||
/// IDs the operator currently has multi-selected for a
|
||||
/// batch operation. Empty set hides the batch action bar
|
||||
/// and falls back to per-row Approve / Reject buttons.
|
||||
|
|
@ -32,7 +33,8 @@ class _ApprovalsPageState extends State<ApprovalsPage>
|
|||
late final String _reviewer = _defaultReviewer();
|
||||
|
||||
static String _defaultReviewer() {
|
||||
final user = Platform.environment['USER'] ??
|
||||
final user =
|
||||
Platform.environment['USER'] ??
|
||||
Platform.environment['USERNAME'] ??
|
||||
'studio';
|
||||
return '$user@studio';
|
||||
|
|
@ -124,9 +126,11 @@ class _ApprovalsPageState extends State<ApprovalsPage>
|
|||
}
|
||||
if (!mounted) return;
|
||||
setState(() => _batchInFlight = false);
|
||||
_toast(failed == 0
|
||||
? l.approvalsBatchApproveDoneToast(ok)
|
||||
: l.approvalsBatchPartialFailure(ok, failed));
|
||||
_toast(
|
||||
failed == 0
|
||||
? l.approvalsBatchApproveDoneToast(ok)
|
||||
: l.approvalsBatchPartialFailure(ok, failed),
|
||||
);
|
||||
_refresh();
|
||||
}
|
||||
|
||||
|
|
@ -148,9 +152,11 @@ class _ApprovalsPageState extends State<ApprovalsPage>
|
|||
}
|
||||
if (!mounted) return;
|
||||
setState(() => _batchInFlight = false);
|
||||
_toast(failed == 0
|
||||
? l.approvalsBatchRejectDoneToast(ok)
|
||||
: l.approvalsBatchPartialFailure(ok, failed));
|
||||
_toast(
|
||||
failed == 0
|
||||
? l.approvalsBatchRejectDoneToast(ok)
|
||||
: l.approvalsBatchPartialFailure(ok, failed),
|
||||
);
|
||||
_refresh();
|
||||
}
|
||||
|
||||
|
|
@ -231,10 +237,7 @@ class _ApprovalsPageState extends State<ApprovalsPage>
|
|||
onBatchReject: _batchReject,
|
||||
onRetry: _refresh,
|
||||
),
|
||||
_HistoryList(
|
||||
future: _historyFuture,
|
||||
onRetry: _refresh,
|
||||
),
|
||||
_HistoryList(future: _historyFuture, onRetry: _refresh),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
@ -391,8 +394,11 @@ class _BatchActionBar extends StatelessWidget {
|
|||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.check_box_outlined,
|
||||
size: 18, color: theme.colorScheme.primary),
|
||||
Icon(
|
||||
Icons.check_box_outlined,
|
||||
size: 18,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
Text(
|
||||
l.approvalsBatchSelected(selectedCount),
|
||||
|
|
@ -546,10 +552,7 @@ class _ApprovalCard extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
const SizedBox(height: FaiSpace.lg),
|
||||
Text(
|
||||
approval.prompt,
|
||||
style: theme.textTheme.bodyLarge,
|
||||
),
|
||||
Text(approval.prompt, style: theme.textTheme.bodyLarge),
|
||||
if (approval.payloadPreview != null) ...[
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
Padding(
|
||||
|
|
@ -648,8 +651,7 @@ class _HistoryRow extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
final (tone, icon, label) = _statusPresentation(context, record.status);
|
||||
final decided = record.decidedAt;
|
||||
final timeStr =
|
||||
decided == null ? '' : _formatTimestamp(decided.toLocal());
|
||||
final timeStr = decided == null ? '' : _formatTimestamp(decided.toLocal());
|
||||
return InkWell(
|
||||
onTap: () => showDialog<void>(
|
||||
context: context,
|
||||
|
|
@ -728,8 +730,10 @@ class _HistoryDialog extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final (tone, icon, label) =
|
||||
_HistoryRow._statusPresentation(context, record.status);
|
||||
final (tone, icon, label) = _HistoryRow._statusPresentation(
|
||||
context,
|
||||
record.status,
|
||||
);
|
||||
return AlertDialog(
|
||||
title: Row(
|
||||
children: [
|
||||
|
|
@ -752,14 +756,23 @@ class _HistoryDialog extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (record.decidedAt != null)
|
||||
_kv(theme, AppLocalizations.of(context)!.approvalsDialogDecided,
|
||||
'${_formatTimestamp(record.decidedAt!.toLocal())} '
|
||||
'· ${record.decidedBy.isEmpty ? "(unknown)" : record.decidedBy}'),
|
||||
_kv(theme, AppLocalizations.of(context)!.approvalsDialogCreated,
|
||||
_formatTimestamp(record.createdAt.toLocal())),
|
||||
_kv(
|
||||
theme,
|
||||
AppLocalizations.of(context)!.approvalsDialogDecided,
|
||||
'${_formatTimestamp(record.decidedAt!.toLocal())} '
|
||||
'· ${record.decidedBy.isEmpty ? "(unknown)" : record.decidedBy}',
|
||||
),
|
||||
_kv(
|
||||
theme,
|
||||
AppLocalizations.of(context)!.approvalsDialogCreated,
|
||||
_formatTimestamp(record.createdAt.toLocal()),
|
||||
),
|
||||
if (record.reason.isNotEmpty)
|
||||
_kv(theme, AppLocalizations.of(context)!.approvalsDialogReason,
|
||||
record.reason),
|
||||
_kv(
|
||||
theme,
|
||||
AppLocalizations.of(context)!.approvalsDialogReason,
|
||||
record.reason,
|
||||
),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.approvalsDialogPrompt,
|
||||
|
|
@ -769,10 +782,7 @@ class _HistoryDialog extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
SelectableText(
|
||||
record.prompt,
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
SelectableText(record.prompt, style: theme.textTheme.bodyMedium),
|
||||
if (record.payloadPreview != null) ...[
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
Text(
|
||||
|
|
@ -834,12 +844,7 @@ class _HistoryDialog extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SelectableText(
|
||||
v,
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
Expanded(child: SelectableText(v, style: theme.textTheme.bodySmall)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue