refactor: rename internal Fai* design system + fai_ helpers to chain
Some checks failed
Security / Security check (push) Failing after 2s

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>
This commit is contained in:
flemming-it 2026-06-16 17:53:17 +02:00
parent 68d23ab7dd
commit 891acd2ba2
52 changed files with 1225 additions and 1225 deletions

View file

@ -224,7 +224,7 @@ class _ApprovalsPageState extends State<ApprovalsPage>
tooltip: AppLocalizations.of(context)!.approvalsReloadTooltip,
onPressed: _refresh,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
],
),
body: TabBarView(
@ -288,7 +288,7 @@ class _PendingList extends StatelessWidget {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return FaiEmptyState(
return ChainEmptyState(
icon: Icons.cloud_off_outlined,
iconColor: theme.colorScheme.error,
title: l.hubUnreachable,
@ -301,7 +301,7 @@ class _PendingList extends StatelessWidget {
}
final pending = snapshot.data ?? [];
if (pending.isEmpty) {
return FaiEmptyState(
return ChainEmptyState(
icon: Icons.task_alt_outlined,
title: l.approvalsInboxZero,
hint: l.approvalsInboxHint,
@ -314,15 +314,15 @@ class _PendingList extends StatelessWidget {
children: [
ListView.separated(
padding: EdgeInsets.fromLTRB(
FaiSpace.xl,
FaiSpace.xl,
FaiSpace.xl,
ChainSpace.xl,
ChainSpace.xl,
ChainSpace.xl,
// Keep the last card clear of the floating
// batch action bar.
selected.isEmpty ? FaiSpace.xl : 96.0,
selected.isEmpty ? ChainSpace.xl : 96.0,
),
itemCount: pending.length,
separatorBuilder: (_, _) => const SizedBox(height: FaiSpace.md),
separatorBuilder: (_, _) => const SizedBox(height: ChainSpace.md),
itemBuilder: (context, i) {
final a = pending[i];
final isSelected = selectedIds.contains(a.id);
@ -337,9 +337,9 @@ class _PendingList extends StatelessWidget {
),
if (selected.isNotEmpty)
Positioned(
left: FaiSpace.xl,
right: FaiSpace.xl,
bottom: FaiSpace.lg,
left: ChainSpace.xl,
right: ChainSpace.xl,
bottom: ChainSpace.lg,
child: _BatchActionBar(
selectedCount: selected.length,
totalCount: pending.length,
@ -387,15 +387,15 @@ class _BatchActionBar extends StatelessWidget {
final l = AppLocalizations.of(context)!;
return Material(
elevation: 4,
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
color: theme.colorScheme.surfaceContainerHigh,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: FaiSpace.lg,
vertical: FaiSpace.md,
horizontal: ChainSpace.lg,
vertical: ChainSpace.md,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: Row(
@ -405,14 +405,14 @@ class _BatchActionBar extends StatelessWidget {
size: 18,
color: theme.colorScheme.primary,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Text(
l.approvalsBatchSelected(selectedCount),
style: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
if (selectedCount < totalCount)
TextButton(
onPressed: inFlight ? null : onSelectAll,
@ -434,7 +434,7 @@ class _BatchActionBar extends StatelessWidget {
),
),
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
FilledButton.icon(
onPressed: inFlight ? null : onApprove,
icon: inFlight
@ -470,7 +470,7 @@ class _HistoryList extends StatelessWidget {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return FaiEmptyState(
return ChainEmptyState(
icon: Icons.cloud_off_outlined,
iconColor: theme.colorScheme.error,
title: l.hubUnreachable,
@ -483,16 +483,16 @@ class _HistoryList extends StatelessWidget {
}
final decided = snapshot.data ?? [];
if (decided.isEmpty) {
return FaiEmptyState(
return ChainEmptyState(
icon: Icons.history,
title: l.approvalsHistoryEmpty,
hint: l.approvalsHistoryEmptyHint,
);
}
return ListView.separated(
padding: const EdgeInsets.all(FaiSpace.xl),
padding: const EdgeInsets.all(ChainSpace.xl),
itemCount: decided.length,
separatorBuilder: (_, _) => const SizedBox(height: FaiSpace.xs),
separatorBuilder: (_, _) => const SizedBox(height: ChainSpace.xs),
itemBuilder: (context, i) {
final a = decided[i];
return _HistoryRow(record: a);
@ -522,7 +522,7 @@ class _ApprovalCard extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final l = AppLocalizations.of(context)!;
return FaiCard(
return ChainCard(
accentTop: true,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -534,13 +534,13 @@ class _ApprovalCard extends StatelessWidget {
onChanged: (_) => onToggleSelected(),
visualDensity: VisualDensity.compact,
),
const SizedBox(width: FaiSpace.xs),
FaiPill(
const SizedBox(width: ChainSpace.xs),
ChainPill(
label: l.approvalsPillPending,
tone: FaiPillTone.warning,
tone: ChainPillTone.warning,
icon: Icons.pending_outlined,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Expanded(
child: Text(
'${approval.flowName} ${approval.stepId}',
@ -550,17 +550,17 @@ class _ApprovalCard extends StatelessWidget {
),
),
if (approval.expiresAt != null)
FaiPill(
ChainPill(
label: _expiresInLabel(context, approval.expiresAt!),
tone: FaiPillTone.neutral,
tone: ChainPillTone.neutral,
icon: Icons.schedule,
),
],
),
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
Text(approval.prompt, style: theme.textTheme.bodyLarge),
if (approval.payloadPreview != null) ...[
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
@ -574,27 +574,27 @@ class _ApprovalCard extends StatelessWidget {
),
Container(
width: double.infinity,
padding: const EdgeInsets.all(FaiSpace.md),
padding: const EdgeInsets.all(ChainSpace.md),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(FaiRadius.sm),
borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: SelectableText(
_prettyPreview(approval.payloadPreview!),
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
color: theme.colorScheme.onSurface,
),
),
),
],
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
Row(
children: [
Text(
approval.id,
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 10,
color: theme.colorScheme.onSurfaceVariant,
),
@ -611,7 +611,7 @@ class _ApprovalCard extends StatelessWidget {
),
),
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
FilledButton.icon(
onPressed: onApprove,
icon: const Icon(Icons.check, size: 16),
@ -663,21 +663,21 @@ class _HistoryRow extends StatelessWidget {
context: context,
builder: (_) => _HistoryDialog(record: record),
),
borderRadius: BorderRadius.circular(FaiRadius.sm),
borderRadius: BorderRadius.circular(ChainRadius.sm),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: FaiSpace.sm,
vertical: FaiSpace.sm,
horizontal: ChainSpace.sm,
vertical: ChainSpace.sm,
),
child: Row(
children: [
FaiPill(label: label, tone: tone, icon: icon),
const SizedBox(width: FaiSpace.md),
ChainPill(label: label, tone: tone, icon: icon),
const SizedBox(width: ChainSpace.md),
SizedBox(
width: 150,
child: Text(
timeStr,
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
color: theme.colorScheme.onSurfaceVariant,
),
@ -693,7 +693,7 @@ class _HistoryRow extends StatelessWidget {
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
if (record.decidedBy.isNotEmpty)
Text(
record.decidedBy,
@ -707,24 +707,24 @@ class _HistoryRow extends StatelessWidget {
);
}
static (FaiPillTone, IconData, String) _statusPresentation(
static (ChainPillTone, IconData, String) _statusPresentation(
BuildContext context,
String status,
) {
final l = AppLocalizations.of(context)!;
switch (status) {
case 'approved':
return (FaiPillTone.success, Icons.check, l.approvalsPillApproved);
return (ChainPillTone.success, Icons.check, l.approvalsPillApproved);
case 'rejected':
return (FaiPillTone.danger, Icons.close, l.approvalsPillRejected);
return (ChainPillTone.danger, Icons.close, l.approvalsPillRejected);
case 'expired':
return (
FaiPillTone.neutral,
ChainPillTone.neutral,
Icons.timer_off_outlined,
l.approvalsPillExpired,
);
default:
return (FaiPillTone.neutral, Icons.help_outline, status);
return (ChainPillTone.neutral, Icons.help_outline, status);
}
}
}
@ -743,8 +743,8 @@ class _HistoryDialog extends StatelessWidget {
return AlertDialog(
title: Row(
children: [
FaiPill(label: label, tone: tone, icon: icon),
const SizedBox(width: FaiSpace.sm),
ChainPill(label: label, tone: tone, icon: icon),
const SizedBox(width: ChainSpace.sm),
Expanded(
child: Text(
'${record.flowName} ${record.stepId}',
@ -779,7 +779,7 @@ class _HistoryDialog extends StatelessWidget {
AppLocalizations.of(context)!.approvalsDialogReason,
record.reason,
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Text(
AppLocalizations.of(context)!.approvalsDialogPrompt,
style: theme.textTheme.labelSmall?.copyWith(
@ -790,7 +790,7 @@ class _HistoryDialog extends StatelessWidget {
const SizedBox(height: 4),
SelectableText(record.prompt, style: theme.textTheme.bodyMedium),
if (record.payloadPreview != null) ...[
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Text(
AppLocalizations.of(context)!.approvalsPayloadPreview,
style: theme.textTheme.labelSmall?.copyWith(
@ -801,22 +801,22 @@ class _HistoryDialog extends StatelessWidget {
const SizedBox(height: 4),
Container(
width: double.infinity,
padding: const EdgeInsets.all(FaiSpace.sm),
padding: const EdgeInsets.all(ChainSpace.sm),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(FaiRadius.sm),
borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: SelectableText(
record.payloadPreview!,
style: FaiTheme.mono(size: 11),
style: ChainTheme.mono(size: 11),
),
),
],
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Text(
record.id,
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 10,
color: theme.colorScheme.onSurfaceVariant,
),

View file

@ -97,7 +97,7 @@ class _AuditPageState extends State<AuditPage> {
title: Text(AppLocalizations.of(context)!.auditTitle),
actions: [
Padding(
padding: const EdgeInsets.only(right: FaiSpace.lg),
padding: const EdgeInsets.only(right: ChainSpace.lg),
child: _FilterChips(
value: _typeFilter,
onChanged: (v) => setState(() => _typeFilter = v),
@ -113,7 +113,7 @@ class _AuditPageState extends State<AuditPage> {
tooltip: AppLocalizations.of(context)!.auditClearLogTooltip,
onPressed: _onClearPressed,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
],
),
body: Column(
@ -123,14 +123,14 @@ class _AuditPageState extends State<AuditPage> {
child: !_initialLoaded
? const Center(child: CircularProgressIndicator())
: _error != null && _events.isEmpty
? FaiEmptyState(
? ChainEmptyState(
icon: Icons.cloud_off_outlined,
iconColor: theme.colorScheme.error,
title: AppLocalizations.of(context)!.hubUnreachable,
hint: AppLocalizations.of(context)!.hubUnreachableHint,
)
: filtered.isEmpty
? FaiEmptyState(
? ChainEmptyState(
icon: Icons.timeline_outlined,
title: AppLocalizations.of(context)!.auditNoEvents,
hint: AppLocalizations.of(context)!.auditNoEventsHint,
@ -187,7 +187,7 @@ class _AuditPageState extends State<AuditPage> {
Color _toneFor(String type, ThemeData theme) {
if (type.endsWith('.failed')) return theme.colorScheme.error;
if (type.endsWith('.completed')) return theme.colorScheme.primary;
if (type.endsWith('.started')) return FaiColors.warning;
if (type.endsWith('.started')) return ChainColors.warning;
return theme.colorScheme.outline;
}
}
@ -220,15 +220,15 @@ class _GroupedEventList extends StatelessWidget {
final l = AppLocalizations.of(context)!;
final items = _itemsWithHeaders(events, l);
return ListView.builder(
padding: const EdgeInsets.all(FaiSpace.xl),
padding: const EdgeInsets.all(ChainSpace.xl),
itemCount: items.length,
itemBuilder: (context, i) {
final item = items[i];
if (item is _GroupHeader) {
return Padding(
padding: EdgeInsets.only(
top: i == 0 ? 0 : FaiSpace.lg,
bottom: FaiSpace.sm,
top: i == 0 ? 0 : ChainSpace.lg,
bottom: ChainSpace.sm,
),
child: Text(
item.label,
@ -242,8 +242,8 @@ class _GroupedEventList extends StatelessWidget {
}
final e = (item as _EventItem).event;
return Padding(
padding: const EdgeInsets.only(bottom: FaiSpace.xs),
child: FaiDataRow(
padding: const EdgeInsets.only(bottom: ChainSpace.xs),
child: ChainDataRow(
accent: toneFor(e.type),
leading: formatTime(e.timestamp),
title: e.type,
@ -328,7 +328,7 @@ class _FilterChips extends StatelessWidget {
children: [
for (final (v, label) in items)
Padding(
padding: const EdgeInsets.only(left: FaiSpace.xs),
padding: const EdgeInsets.only(left: ChainSpace.xs),
child: _ChipButton(
label: label,
selected: value == v,
@ -378,14 +378,14 @@ class _ChipButtonState extends State<_ChipButton> {
child: GestureDetector(
onTap: widget.onTap,
child: AnimatedContainer(
duration: FaiMotion.fast,
duration: ChainMotion.fast,
padding: const EdgeInsets.symmetric(
horizontal: FaiSpace.md,
horizontal: ChainSpace.md,
vertical: 6,
),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(FaiRadius.sm),
borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(
color: widget.selected
? theme.colorScheme.primary.withValues(alpha: 0.3)
@ -394,7 +394,7 @@ class _ChipButtonState extends State<_ChipButton> {
),
child: Text(
widget.label,
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
weight: widget.selected ? FontWeight.w500 : FontWeight.w400,
color: fg,
@ -420,8 +420,8 @@ class _LiveStatusBar extends StatelessWidget {
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(
horizontal: FaiSpace.xl,
vertical: FaiSpace.sm,
horizontal: ChainSpace.xl,
vertical: ChainSpace.sm,
),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
@ -431,11 +431,11 @@ class _LiveStatusBar extends StatelessWidget {
),
child: Row(
children: [
FaiStatusDot(
color: live ? FaiColors.success : FaiColors.danger,
ChainStatusDot(
color: live ? ChainColors.success : ChainColors.danger,
pulsing: live,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Text(
live ? l.auditLiveStatus(eventCount) : l.auditDisconnected(error!),
style: theme.textTheme.bodySmall?.copyWith(
@ -569,7 +569,7 @@ class _EventDetailDialogState extends State<_EventDetailDialog> {
return AlertDialog(
title: Text(event.type),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
),
content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 640, maxHeight: 560),
@ -616,7 +616,7 @@ class _EventDetailDialogState extends State<_EventDetailDialog> {
valueColor: theme.colorScheme.error,
),
if (detail.isNotEmpty) ...[
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Text(
l.auditDetailHeader,
style: theme.textTheme.labelSmall?.copyWith(
@ -627,17 +627,17 @@ class _EventDetailDialogState extends State<_EventDetailDialog> {
const SizedBox(height: 4),
Container(
width: double.infinity,
padding: const EdgeInsets.all(FaiSpace.md),
padding: const EdgeInsets.all(ChainSpace.md),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(FaiRadius.sm),
borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: SelectableText(detail, style: FaiTheme.mono(size: 11)),
child: SelectableText(detail, style: ChainTheme.mono(size: 11)),
),
],
if (_explanation != null || _explaining) ...[
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
_ExplanationPanel(
explaining: _explaining,
result: _explanation,
@ -729,7 +729,7 @@ class _FlowRunDialog extends StatelessWidget {
return AlertDialog(
title: Text(l.auditFlowRunDialogTitle(flowName)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
),
content: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 640, maxHeight: maxHeight),
@ -743,15 +743,15 @@ class _FlowRunDialog extends StatelessWidget {
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Flexible(
child: ListView.separated(
shrinkWrap: true,
itemCount: related.length,
separatorBuilder: (_, _) => const SizedBox(height: FaiSpace.xs),
separatorBuilder: (_, _) => const SizedBox(height: ChainSpace.xs),
itemBuilder: (context, i) {
final e = related[i];
return FaiDataRow(
return ChainDataRow(
accent: _toneFor(e.type, theme),
leading: _formatRelativeTime(e.timestamp),
title: e.type,
@ -792,7 +792,7 @@ class _FlowRunDialog extends StatelessWidget {
Color _toneFor(String type, ThemeData theme) {
if (type.endsWith('.failed')) return theme.colorScheme.error;
if (type.endsWith('.completed')) return theme.colorScheme.primary;
if (type.endsWith('.started')) return FaiColors.warning;
if (type.endsWith('.started')) return ChainColors.warning;
return theme.colorScheme.outline;
}
}
@ -831,7 +831,7 @@ class _Field extends StatelessWidget {
child: SelectableText(
value,
style: mono
? FaiTheme.mono(
? ChainTheme.mono(
size: 11,
color: valueColor ?? theme.colorScheme.onSurface,
)
@ -875,10 +875,10 @@ class _ExplanationPanel extends StatelessWidget {
: theme.colorScheme.error;
return Container(
width: double.infinity,
padding: const EdgeInsets.all(FaiSpace.md),
padding: const EdgeInsets.all(ChainSpace.md),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(FaiRadius.sm),
borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(color: color.withValues(alpha: 0.3)),
),
child: Column(
@ -887,7 +887,7 @@ class _ExplanationPanel extends StatelessWidget {
Row(
children: [
Icon(Icons.auto_awesome, size: 14, color: color),
const SizedBox(width: FaiSpace.xs),
const SizedBox(width: ChainSpace.xs),
Text(
l.auditSystemAi,
style: theme.textTheme.labelSmall?.copyWith(
@ -895,15 +895,15 @@ class _ExplanationPanel extends StatelessWidget {
letterSpacing: 0.6,
),
),
const SizedBox(width: FaiSpace.xs),
FaiPill(
const SizedBox(width: ChainSpace.xs),
ChainPill(
label: privacyMode,
tone: privacyMode == 'full'
? FaiPillTone.warning
: FaiPillTone.neutral,
? ChainPillTone.warning
: ChainPillTone.neutral,
),
if (result != null && result!.isSuccess && result!.cached) ...[
const SizedBox(width: FaiSpace.xs),
const SizedBox(width: ChainSpace.xs),
Tooltip(
message: () {
final hits = result!.cacheHits > 1
@ -913,9 +913,9 @@ class _ExplanationPanel extends StatelessWidget {
? l.auditCachedTooltipUnknown(hits)
: l.auditCachedTooltipKnown(result!.cachedAt, hits);
}(),
child: FaiPill(
child: ChainPill(
label: l.auditCachedPill,
tone: FaiPillTone.success,
tone: ChainPillTone.success,
icon: Icons.bolt,
),
),
@ -926,7 +926,7 @@ class _ExplanationPanel extends StatelessWidget {
result!.cached
? l.auditOriginalLatency(result!.latencyMs)
: l.auditLatency(result!.latencyMs),
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 10,
color: theme.colorScheme.onSurfaceVariant,
),
@ -934,7 +934,7 @@ class _ExplanationPanel extends StatelessWidget {
if (result != null &&
result!.isSuccess &&
onRegenerate != null) ...[
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Tooltip(
message: l.auditRegenerateTooltip,
child: IconButton(
@ -946,7 +946,7 @@ class _ExplanationPanel extends StatelessWidget {
],
],
),
const SizedBox(height: FaiSpace.sm),
const SizedBox(height: ChainSpace.sm),
if (explaining)
Row(
children: [
@ -955,7 +955,7 @@ class _ExplanationPanel extends StatelessWidget {
height: 14,
child: CircularProgressIndicator(strokeWidth: 2),
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Text(
l.auditAskingFull,
style: theme.textTheme.bodySmall?.copyWith(
@ -974,7 +974,7 @@ class _ExplanationPanel extends StatelessWidget {
),
),
if (result!.fixHint(l).isNotEmpty) ...[
const SizedBox(height: FaiSpace.sm),
const SizedBox(height: ChainSpace.sm),
Text(
l.auditFixLabel,
style: theme.textTheme.labelSmall?.copyWith(
@ -1063,7 +1063,7 @@ class _ClearAuditDialogState extends State<_ClearAuditDialog> {
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
TextField(
controller: _reviewer,
decoration: InputDecoration(
@ -1072,7 +1072,7 @@ class _ClearAuditDialogState extends State<_ClearAuditDialog> {
isDense: true,
),
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
ValueListenableBuilder<TextEditingValue>(
valueListenable: _reason,
builder: (_, _, _) => TextField(

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import '../data/error_presentation.dart';
import '../data/fai_log.dart';
import '../data/chain_log.dart';
import '../data/hub.dart';
import '../data/system_actions.dart';
import '../l10n/app_localizations.dart';
@ -47,7 +47,7 @@ class _DoctorPageState extends State<DoctorPage> {
tooltip: AppLocalizations.of(context)!.doctorRecheckTooltip,
onPressed: _refresh,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
],
),
body: FutureBuilder<DoctorSnapshot>(
@ -58,7 +58,7 @@ class _DoctorPageState extends State<DoctorPage> {
}
if (snapshot.hasError) {
final l = AppLocalizations.of(context)!;
return FaiEmptyState(
return ChainEmptyState(
icon: Icons.cloud_off_outlined,
iconColor: Theme.of(context).colorScheme.error,
title: l.hubUnreachable,
@ -74,34 +74,34 @@ class _DoctorPageState extends State<DoctorPage> {
s.update.updateAvailable ||
(!s.update.manifestReachable && s.update.localVersion.isNotEmpty);
return ListView(
padding: const EdgeInsets.all(FaiSpace.xl),
padding: const EdgeInsets.all(ChainSpace.xl),
children: [
if (showUpdate) _UpdateBanner(status: s.update),
if (showUpdate) const SizedBox(height: FaiSpace.lg),
if (showUpdate) const SizedBox(height: ChainSpace.lg),
_SummaryStrip(snapshot: s),
const SizedBox(height: FaiSpace.xl),
const SizedBox(height: ChainSpace.xl),
_Section(
title: AppLocalizations.of(context)!.doctorEventLogSection,
child: _EventLogPanel(snapshot: s, onRefresh: _refresh),
),
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
_Section(
title: AppLocalizations.of(
context,
)!.doctorModulesApprovalsSection,
child: _ModulesPanel(snapshot: s),
),
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
_Section(
title: AppLocalizations.of(context)!.doctorHostServicesSection,
child: _ServicesPanel(snapshot: s),
),
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
_Section(
title: AppLocalizations.of(context)!.doctorDaemonFilesSection,
child: _DaemonPathsPanel(paths: s.paths),
),
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
_Section(
title: AppLocalizations.of(context)!.doctorDaemonControlSection,
child: _DaemonActionsCard(),
@ -128,8 +128,8 @@ class _Section extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.only(
left: FaiSpace.xs,
bottom: FaiSpace.sm,
left: ChainSpace.xs,
bottom: ChainSpace.sm,
),
child: Text(
title.toUpperCase(),
@ -162,17 +162,17 @@ class _SummaryStrip extends StatelessWidget {
subtitle: l.doctorSummaryCapabilities(snapshot.capabilityCount),
icon: Icons.extension_outlined,
),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
_StatTile(
label: l.doctorSummaryApprovals,
value: snapshot.pendingApprovals.toString(),
subtitle: l.doctorSummaryPending,
icon: Icons.inbox_outlined,
tone: snapshot.pendingApprovals > 0
? FaiPillTone.warning
: FaiPillTone.neutral,
? ChainPillTone.warning
: ChainPillTone.neutral,
),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
_StatTile(
label: l.doctorSummaryAudit,
value: snapshot.chainHealthy ? '' : '',
@ -182,10 +182,10 @@ class _SummaryStrip extends StatelessWidget {
),
icon: Icons.shield_outlined,
tone: snapshot.chainHealthy
? FaiPillTone.success
: FaiPillTone.danger,
? ChainPillTone.success
: ChainPillTone.danger,
),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
_StatTile(
label: l.doctorSummaryServices,
value: snapshot.services.length.toString(),
@ -202,36 +202,36 @@ class _StatTile extends StatelessWidget {
final String value;
final String subtitle;
final IconData icon;
final FaiPillTone tone;
final ChainPillTone tone;
const _StatTile({
required this.label,
required this.value,
required this.subtitle,
required this.icon,
this.tone = FaiPillTone.neutral,
this.tone = ChainPillTone.neutral,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final accentColor = switch (tone) {
FaiPillTone.success => FaiColors.success,
FaiPillTone.warning => FaiColors.warning,
FaiPillTone.danger => theme.colorScheme.error,
ChainPillTone.success => ChainColors.success,
ChainPillTone.warning => ChainColors.warning,
ChainPillTone.danger => theme.colorScheme.error,
_ => theme.colorScheme.primary,
};
return Expanded(
child: FaiCard(
child: ChainCard(
accentLeft: accentColor,
padding: const EdgeInsets.all(FaiSpace.lg),
padding: const EdgeInsets.all(ChainSpace.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, size: 14, color: theme.colorScheme.onSurfaceVariant),
const SizedBox(width: FaiSpace.xs),
const SizedBox(width: ChainSpace.xs),
Flexible(
child: Text(
label.toUpperCase(),
@ -245,7 +245,7 @@ class _StatTile extends StatelessWidget {
),
],
),
const SizedBox(height: FaiSpace.sm),
const SizedBox(height: ChainSpace.sm),
Text(
value,
style: theme.textTheme.displaySmall?.copyWith(
@ -282,15 +282,15 @@ class _EventLogPanel extends StatelessWidget {
final theme = Theme.of(context);
final healthy = snapshot.chainHealthy;
final l = AppLocalizations.of(context)!;
return FaiCard(
return ChainCard(
child: Row(
children: [
Icon(
healthy ? Icons.verified_outlined : Icons.gpp_bad_outlined,
size: 24,
color: healthy ? FaiColors.success : theme.colorScheme.error,
color: healthy ? ChainColors.success : theme.colorScheme.error,
),
const SizedBox(width: FaiSpace.lg),
const SizedBox(width: ChainSpace.lg),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -322,10 +322,10 @@ class _EventLogPanel extends StatelessWidget {
icon: const Icon(Icons.fact_check_outlined, size: 16),
label: Text(l.doctorVerifyNow),
),
const SizedBox(width: FaiSpace.sm),
FaiPill(
const SizedBox(width: ChainSpace.sm),
ChainPill(
label: healthy ? l.doctorChainPillOk : l.doctorChainPillTamper,
tone: healthy ? FaiPillTone.success : FaiPillTone.danger,
tone: healthy ? ChainPillTone.success : ChainPillTone.danger,
),
],
),
@ -353,7 +353,7 @@ class _DaemonPathsPanel extends StatelessWidget {
(l.doctorPathLog, paths.logPath, Icons.description_outlined, false),
(
l.doctorPathStudioErrors,
FaiLog.instance.path,
ChainLog.instance.path,
Icons.report_problem_outlined,
false,
),
@ -365,7 +365,7 @@ class _DaemonPathsPanel extends StatelessWidget {
].where((e) => e.$2.isNotEmpty).toList();
if (entries.isEmpty) {
return FaiCard(
return ChainCard(
child: Text(
l.doctorPathsEmpty,
style: Theme.of(context).textTheme.bodySmall,
@ -373,7 +373,7 @@ class _DaemonPathsPanel extends StatelessWidget {
);
}
return FaiCard(
return ChainCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -416,7 +416,7 @@ class _PathRow extends StatelessWidget {
child: Row(
children: [
Icon(icon, size: 16, color: theme.colorScheme.onSurfaceVariant),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
SizedBox(
width: 90,
child: Text(
@ -429,13 +429,13 @@ class _PathRow extends StatelessWidget {
Expanded(
child: SelectableText(
path,
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
color: theme.colorScheme.onSurface,
),
),
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
if (isLog || isConfig) ...[
OutlinedButton.icon(
onPressed: () => isConfig
@ -447,7 +447,7 @@ class _PathRow extends StatelessWidget {
visualDensity: VisualDensity.compact,
),
),
const SizedBox(width: FaiSpace.xs),
const SizedBox(width: ChainSpace.xs),
],
OutlinedButton.icon(
onPressed: () async {
@ -494,7 +494,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
/// True when the last daemon action failed because no `fai`
/// binary could be located. Swaps the raw output line for the
/// actionable [FaiBinaryRecovery] block.
/// actionable [ChainBinaryRecovery] block.
bool _binaryMissing = false;
ChannelStatusSnapshot? _channels;
@ -557,7 +557,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
}
}
}
return FaiCard(
return ChainCard(
// Row + Expanded forces the card to take the full width
// its parent offers. Without this, the card hugs the
// intrinsic width of the longest button row and ends up
@ -575,10 +575,10 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
: Icons.power_off_outlined,
size: 18,
color: active?.running == true
? FaiColors.success
? ChainColors.success
: theme.colorScheme.onSurfaceVariant,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Expanded(
child: Text(
active == null
@ -593,36 +593,36 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
),
),
if (active != null) ...[
const SizedBox(width: FaiSpace.sm),
FaiPill(
const SizedBox(width: ChainSpace.sm),
ChainPill(
label: active.running
? l.doctorPillRunning
: l.doctorPillStopped,
tone: active.running
? FaiPillTone.success
: FaiPillTone.neutral,
? ChainPillTone.success
: ChainPillTone.neutral,
),
],
],
),
const SizedBox(height: FaiSpace.sm),
const SizedBox(height: ChainSpace.sm),
Text(
l.doctorDaemonControlHint,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Wrap(
spacing: FaiSpace.sm,
runSpacing: FaiSpace.sm,
spacing: ChainSpace.sm,
runSpacing: ChainSpace.sm,
children: [
FilledButton.tonalIcon(
onPressed: _busy
? null
: () => _run(
l.daemonActionRestart,
() => SystemActions.faiDaemon(['restart']),
() => SystemActions.chainDaemon(['restart']),
),
icon: const Icon(Icons.restart_alt, size: 16),
label: Text(l.doctorRestart),
@ -633,7 +633,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
? null
: () => _run(
l.daemonActionStart,
() => SystemActions.faiDaemon(['start']),
() => SystemActions.chainDaemon(['start']),
),
icon: const Icon(Icons.play_arrow, size: 16),
label: Text(l.doctorStart),
@ -643,7 +643,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
? null
: () => _run(
l.daemonActionStop,
() => SystemActions.faiDaemon(['stop']),
() => SystemActions.chainDaemon(['stop']),
),
icon: const Icon(Icons.stop_circle_outlined, size: 16),
label: Text(l.doctorStop),
@ -653,7 +653,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
? null
: () => _run(
l.daemonActionStatus,
() => SystemActions.faiDaemon(['status']),
() => SystemActions.chainDaemon(['status']),
),
icon: const Icon(Icons.health_and_safety_outlined, size: 16),
label: Text(l.doctorStatusAction),
@ -661,7 +661,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
],
),
if (_busy) ...[
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Row(
children: [
const SizedBox(
@ -669,7 +669,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
height: 14,
child: CircularProgressIndicator(strokeWidth: 2),
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Text(
l.doctorDaemonControlWorking,
style: theme.textTheme.bodySmall?.copyWith(
@ -680,27 +680,27 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
),
],
if (_binaryMissing) ...[
const SizedBox(height: FaiSpace.md),
FaiBinaryRecovery(
const SizedBox(height: ChainSpace.md),
ChainBinaryRecovery(
onLocated: () => _run(
l.daemonActionStatus,
() => SystemActions.faiDaemon(['status']),
() => SystemActions.chainDaemon(['status']),
),
),
],
if (_output != null) ...[
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Container(
width: double.infinity,
padding: const EdgeInsets.all(FaiSpace.sm),
padding: const EdgeInsets.all(ChainSpace.sm),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(FaiRadius.sm),
borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: SelectableText(
_output!,
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
color: theme.colorScheme.onSurface,
),
@ -732,7 +732,7 @@ class _ModulesPanel extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final l = AppLocalizations.of(context)!;
return FaiCard(
return ChainCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -743,7 +743,7 @@ class _ModulesPanel extends StatelessWidget {
size: 18,
color: theme.colorScheme.onSurfaceVariant,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -767,17 +767,17 @@ class _ModulesPanel extends StatelessWidget {
],
),
),
FaiPill(
ChainPill(
label: snapshot.moduleCount > 0
? l.doctorPillLoaded
: l.doctorPillEmpty,
tone: snapshot.moduleCount > 0
? FaiPillTone.success
: FaiPillTone.neutral,
? ChainPillTone.success
: ChainPillTone.neutral,
),
],
),
const Divider(height: FaiSpace.xl),
const Divider(height: ChainSpace.xl),
Row(
children: [
Icon(
@ -785,7 +785,7 @@ class _ModulesPanel extends StatelessWidget {
size: 18,
color: theme.colorScheme.onSurfaceVariant,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Text(
snapshot.pendingApprovals == 0
? l.doctorApprovalsNone
@ -794,9 +794,9 @@ class _ModulesPanel extends StatelessWidget {
),
const Spacer(),
if (snapshot.pendingApprovals > 0)
FaiPill(
ChainPill(
label: l.doctorApprovalsAttentionPill,
tone: FaiPillTone.warning,
tone: ChainPillTone.warning,
),
],
),
@ -819,10 +819,10 @@ class _ServicesPanel extends StatelessWidget {
// Wrap on narrow windows so the mono-spaced hint
// (`add to ~/.chain/config.yaml under services:`) does not
// overflow horizontally past the card's right edge.
return FaiCard(
return ChainCard(
child: Wrap(
spacing: FaiSpace.sm,
runSpacing: FaiSpace.xs,
spacing: ChainSpace.sm,
runSpacing: ChainSpace.xs,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Row(
@ -833,7 +833,7 @@ class _ServicesPanel extends StatelessWidget {
size: 18,
color: theme.colorScheme.onSurfaceVariant,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Text(
l.doctorServicesEmpty,
style: theme.textTheme.bodyMedium?.copyWith(
@ -844,7 +844,7 @@ class _ServicesPanel extends StatelessWidget {
),
Text(
l.doctorServicesEmptyHint,
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
color: theme.colorScheme.onSurfaceVariant,
),
@ -853,7 +853,7 @@ class _ServicesPanel extends StatelessWidget {
),
);
}
return FaiCard(
return ChainCard(
padding: EdgeInsets.zero,
child: Column(
children: [
@ -861,7 +861,7 @@ class _ServicesPanel extends StatelessWidget {
if (i > 0)
Divider(height: 1, color: theme.colorScheme.outlineVariant),
Padding(
padding: const EdgeInsets.all(FaiSpace.lg),
padding: const EdgeInsets.all(ChainSpace.lg),
child: Row(
children: [
Icon(
@ -869,25 +869,25 @@ class _ServicesPanel extends StatelessWidget {
size: 16,
color: theme.colorScheme.primary,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Text(
snapshot.services[i].name,
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
Text(
snapshot.services[i].endpoint,
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
color: theme.colorScheme.onSurfaceVariant,
),
),
const Spacer(),
for (final tag in snapshot.services[i].tags) ...[
FaiPill(label: tag, tone: FaiPillTone.neutral),
const SizedBox(width: FaiSpace.xs),
ChainPill(label: tag, tone: ChainPillTone.neutral),
const SizedBox(width: ChainSpace.xs),
],
],
),
@ -917,7 +917,7 @@ class _UpdateBannerState extends State<_UpdateBanner> {
_applying = true;
_applyOutput = null;
});
final r = await SystemActions.faiUpdateApply(widget.status.channel);
final r = await SystemActions.chainUpdateApply(widget.status.channel);
if (!mounted) return;
setState(() {
_applying = false;
@ -945,7 +945,7 @@ class _UpdateBannerState extends State<_UpdateBanner> {
final body = available
? l.doctorUpdateBody(status.channel, status.latestVersion)
: (status.reason ?? l.doctorUpdateUnreachableBody(status.channel));
return FaiCard(
return ChainCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -953,7 +953,7 @@ class _UpdateBannerState extends State<_UpdateBanner> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(iconData, size: 20, color: iconColor),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -967,7 +967,7 @@ class _UpdateBannerState extends State<_UpdateBanner> {
const SizedBox(height: 2),
Text(
body,
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
color: theme.colorScheme.onSurfaceVariant,
),
@ -976,7 +976,7 @@ class _UpdateBannerState extends State<_UpdateBanner> {
const SizedBox(height: 4),
Text(
l.doctorReleaseNotes(status.releaseNotesUrl!),
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
color: theme.colorScheme.primary,
),
@ -999,16 +999,16 @@ class _UpdateBannerState extends State<_UpdateBanner> {
_applying ? l.doctorApplying : l.doctorApplyUpdate,
),
),
const SizedBox(width: FaiSpace.sm),
FaiPill(
const SizedBox(width: ChainSpace.sm),
ChainPill(
label: available ? l.doctorPillNew : l.doctorPillOffline,
tone: available ? FaiPillTone.success : FaiPillTone.neutral,
tone: available ? ChainPillTone.success : ChainPillTone.neutral,
),
],
),
if (_applyOutput != null) ...[
const SizedBox(height: FaiSpace.md),
FaiErrorBox(text: _applyOutput!, maxHeight: 240),
const SizedBox(height: ChainSpace.md),
ChainErrorBox(text: _applyOutput!, maxHeight: 240),
],
],
),

View file

@ -103,7 +103,7 @@ class _FederationPageState extends State<FederationPage> {
tooltip: l.federationReloadTooltip,
onPressed: _refresh,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
],
),
floatingActionButton: FloatingActionButton.extended(
@ -118,7 +118,7 @@ class _FederationPageState extends State<FederationPage> {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return FaiEmptyState(
return ChainEmptyState(
icon: Icons.cloud_off_outlined,
iconColor: theme.colorScheme.error,
title: l.hubUnreachable,
@ -131,7 +131,7 @@ class _FederationPageState extends State<FederationPage> {
}
final sats = snapshot.data ?? [];
if (sats.isEmpty) {
return FaiEmptyState(
return ChainEmptyState(
icon: Icons.hub_outlined,
title: l.federationEmptyTitle,
hint: l.federationEmptyHint,
@ -143,9 +143,9 @@ class _FederationPageState extends State<FederationPage> {
);
}
return ListView.separated(
padding: const EdgeInsets.all(FaiSpace.xl),
padding: const EdgeInsets.all(ChainSpace.xl),
itemCount: sats.length,
separatorBuilder: (_, _) => const SizedBox(height: FaiSpace.md),
separatorBuilder: (_, _) => const SizedBox(height: ChainSpace.md),
itemBuilder: (context, i) => _SatelliteCard(satellite: sats[i]),
);
},
@ -162,19 +162,19 @@ class _SatelliteCard extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final l = AppLocalizations.of(context)!;
return FaiCard(
return ChainCard(
accentTop: true,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
FaiPill(
ChainPill(
label: l.federationPillConnected,
tone: FaiPillTone.success,
tone: ChainPillTone.success,
icon: Icons.link,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Expanded(
child: Text(
satellite.displayName,
@ -183,19 +183,19 @@ class _SatelliteCard extends StatelessWidget {
),
),
),
FaiPill(
ChainPill(
label: satellite.region.isEmpty
? l.federationRegionNone
: satellite.region,
tone: FaiPillTone.neutral,
tone: ChainPillTone.neutral,
icon: Icons.public,
),
],
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Wrap(
spacing: FaiSpace.md,
runSpacing: FaiSpace.xs,
spacing: ChainSpace.md,
runSpacing: ChainSpace.xs,
children: [
_meta(theme, 'v${satellite.hubVersion}'),
_meta(theme, 'wire ${satellite.wireVersion}'),
@ -203,7 +203,7 @@ class _SatelliteCard extends StatelessWidget {
],
),
if (satellite.capabilities.isNotEmpty) ...[
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Text(
l.federationCapabilities(satellite.capabilities.length),
style: theme.textTheme.labelSmall?.copyWith(
@ -213,13 +213,13 @@ class _SatelliteCard extends StatelessWidget {
),
const SizedBox(height: 4),
Wrap(
spacing: FaiSpace.xs,
runSpacing: FaiSpace.xs,
spacing: ChainSpace.xs,
runSpacing: ChainSpace.xs,
children: satellite.capabilities
.map(
(c) => FaiPill(
(c) => ChainPill(
label: c,
tone: FaiPillTone.accent,
tone: ChainPillTone.accent,
icon: Icons.extension_outlined,
),
)
@ -233,7 +233,7 @@ class _SatelliteCard extends StatelessWidget {
Widget _meta(ThemeData theme, String text) => Text(
text,
style: FaiTheme.mono(size: 10, color: theme.colorScheme.onSurfaceVariant),
style: ChainTheme.mono(size: 10, color: theme.colorScheme.onSurfaceVariant),
);
}
@ -261,11 +261,11 @@ class _EnrollmentDialog extends StatelessWidget {
Text(l.federationTokenLabel, style: theme.textTheme.labelSmall),
const SizedBox(height: 4),
_CopyableBlock(text: enrollment.token, copiedMsg: l.federationCopied),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Text(l.federationConfigLabel, style: theme.textTheme.labelSmall),
const SizedBox(height: 4),
_CopyableBlock(text: config, copiedMsg: l.federationCopied),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Text(
l.federationEnrollmentHint,
style: theme.textTheme.bodySmall?.copyWith(
@ -299,19 +299,19 @@ class _CopyableBlock extends StatelessWidget {
Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(
FaiSpace.md,
FaiSpace.md,
ChainSpace.md,
ChainSpace.md,
40,
FaiSpace.md,
ChainSpace.md,
),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(FaiRadius.sm),
borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: SelectableText(
text,
style: FaiTheme.mono(size: 11, color: theme.colorScheme.onSurface),
style: ChainTheme.mono(size: 11, color: theme.colorScheme.onSurface),
),
),
Positioned(

View file

@ -48,7 +48,7 @@ class _ModulesPageState extends State<ModulesPage> {
tooltip: AppLocalizations.of(context)!.modulesReloadTooltip,
onPressed: _refresh,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
],
),
body: FutureBuilder<List<ModuleSummary>>(
@ -59,7 +59,7 @@ class _ModulesPageState extends State<ModulesPage> {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return FaiEmptyState(
return ChainEmptyState(
icon: Icons.cloud_off_outlined,
iconColor: Theme.of(context).colorScheme.error,
title: l.hubUnreachable,
@ -72,22 +72,22 @@ class _ModulesPageState extends State<ModulesPage> {
}
final modules = snapshot.data ?? [];
if (modules.isEmpty) {
return FaiEmptyState(
return ChainEmptyState(
icon: Icons.extension_outlined,
title: l.modulesNoneTitle,
hint: l.modulesNoneHint,
);
}
return ListView(
padding: const EdgeInsets.all(FaiSpace.xl),
padding: const EdgeInsets.all(ChainSpace.xl),
children: [
_RecentActivityPanel(future: _historyFuture),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
for (var i = 0; i < modules.length; i++) ...[
if (i > 0) const SizedBox(height: FaiSpace.md),
if (i > 0) const SizedBox(height: ChainSpace.md),
GestureDetector(
onTap: () async {
final uninstalled = await FaiModuleSheet.show(
final uninstalled = await ChainModuleSheet.show(
context,
modules[i].name,
);
@ -116,7 +116,7 @@ class _ModuleCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return FaiCard(
return ChainCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -128,15 +128,15 @@ class _ModuleCard extends StatelessWidget {
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: FaiSpace.sm),
FaiPill(
const SizedBox(width: ChainSpace.sm),
ChainPill(
label: 'v${module.version}',
tone: FaiPillTone.neutral,
tone: ChainPillTone.neutral,
monospace: true,
),
],
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -152,13 +152,13 @@ class _ModuleCard extends StatelessWidget {
),
Expanded(
child: Wrap(
spacing: FaiSpace.xs,
runSpacing: FaiSpace.xs,
spacing: ChainSpace.xs,
runSpacing: ChainSpace.xs,
children: module.capabilities
.map(
(c) => FaiPill(
(c) => ChainPill(
label: c,
tone: FaiPillTone.accent,
tone: ChainPillTone.accent,
monospace: true,
),
)
@ -192,12 +192,12 @@ class _RecentActivityPanel extends StatelessWidget {
if (events.isEmpty) return const SizedBox.shrink();
return Container(
padding: const EdgeInsets.symmetric(
horizontal: FaiSpace.md,
vertical: FaiSpace.sm,
horizontal: ChainSpace.md,
vertical: ChainSpace.sm,
),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(FaiRadius.sm),
borderRadius: BorderRadius.circular(ChainRadius.sm),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: Column(
@ -219,7 +219,7 @@ class _RecentActivityPanel extends StatelessWidget {
fontSize: 10,
),
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Text(
AppLocalizations.of(
context,
@ -230,7 +230,7 @@ class _RecentActivityPanel extends StatelessWidget {
),
],
),
const SizedBox(height: FaiSpace.xs),
const SizedBox(height: ChainSpace.xs),
for (final e in events) _ActivityRow(event: e),
],
),
@ -248,7 +248,7 @@ class _ActivityRow extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final installed = event.type == 'module.installed';
final accent = installed ? FaiColors.success : FaiColors.warning;
final accent = installed ? ChainColors.success : ChainColors.warning;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
@ -263,7 +263,7 @@ class _ActivityRow extends StatelessWidget {
width: 150,
child: Text(
_formatTimestamp(event.timestamp.toLocal()),
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 10,
color: theme.colorScheme.onSurfaceVariant,
),

File diff suppressed because it is too large Load diff

View file

@ -35,14 +35,14 @@ class WelcomePage extends StatelessWidget {
final hubDown = StudioShellState.of(context)?.connected == false;
return Scaffold(
backgroundColor: theme.scaffoldBackgroundColor,
appBar: AppBar(titleSpacing: FaiSpace.xl, title: Text(l.navWelcome)),
appBar: AppBar(titleSpacing: ChainSpace.xl, title: Text(l.navWelcome)),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.fromLTRB(
FaiSpace.xl,
FaiSpace.lg,
FaiSpace.xl,
FaiSpace.xxl,
ChainSpace.xl,
ChainSpace.lg,
ChainSpace.xl,
ChainSpace.xxl,
),
// Center the 960-px content column on wide windows
// so the right margin matches the left rather than
@ -59,24 +59,24 @@ class WelcomePage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _Hero(),
const SizedBox(height: FaiSpace.xxl),
const SizedBox(height: ChainSpace.xxl),
// When the hub is down the onboarding checklist
// would render dead (all-unchecked, nothing to
// probe). Lead with a "start the hub" card
// instead; restore the checklist once connected.
if (hubDown) ...[
const _HubDownHero(),
const SizedBox(height: FaiSpace.xxl),
const SizedBox(height: ChainSpace.xxl),
] else
const _OnboardingChecklist(),
const _PillarRow(),
const SizedBox(height: FaiSpace.xxl),
const SizedBox(height: ChainSpace.xxl),
const _SectionLabel(textKey: _SectionLabelKey.trust),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
const _TrustPosture(),
const SizedBox(height: FaiSpace.xxl),
const SizedBox(height: ChainSpace.xxl),
const _SectionLabel(textKey: _SectionLabelKey.docs),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
const _DocsRow(),
],
),
@ -100,9 +100,9 @@ class _Hero extends StatelessWidget {
final l = AppLocalizations.of(context)!;
return Container(
width: double.infinity,
padding: const EdgeInsets.all(FaiSpace.xxl),
padding: const EdgeInsets.all(ChainSpace.xxl),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
gradient: LinearGradient(
colors: [
theme.colorScheme.primary.withValues(alpha: 0.18),
@ -123,7 +123,7 @@ class _Hero extends StatelessWidget {
Row(
children: [
Container(
padding: const EdgeInsets.all(FaiSpace.md),
padding: const EdgeInsets.all(ChainSpace.md),
decoration: BoxDecoration(
color: theme.colorScheme.primary.withValues(alpha: 0.14),
shape: BoxShape.circle,
@ -134,7 +134,7 @@ class _Hero extends StatelessWidget {
color: theme.colorScheme.primary,
),
),
const SizedBox(width: FaiSpace.lg),
const SizedBox(width: ChainSpace.lg),
Expanded(
child: Text(
l.welcomeHeroTitle,
@ -145,7 +145,7 @@ class _Hero extends StatelessWidget {
),
],
),
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
Text(
l.welcomeHeroSubtitle,
style: theme.textTheme.titleMedium?.copyWith(
@ -189,13 +189,13 @@ class _HubDownHeroState extends State<_HubDownHero> {
final l = AppLocalizations.of(context)!;
// No `fai` binary "Start hub" cannot work; lead the
// operator to the install guide instead.
final canStart = SystemActions.faiBinaryExists();
final canStart = SystemActions.chainBinaryExists();
return Container(
width: double.infinity,
padding: const EdgeInsets.all(FaiSpace.xl),
padding: const EdgeInsets.all(ChainSpace.xl),
decoration: BoxDecoration(
color: theme.colorScheme.errorContainer.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
border: Border.all(
color: theme.colorScheme.error.withValues(alpha: 0.4),
),
@ -210,7 +210,7 @@ class _HubDownHeroState extends State<_HubDownHero> {
size: 24,
color: theme.colorScheme.error,
),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
Expanded(
child: Text(
l.welcomeHubDownTitle,
@ -221,7 +221,7 @@ class _HubDownHeroState extends State<_HubDownHero> {
),
],
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Text(
l.welcomeHubDownBody,
style: theme.textTheme.bodyMedium?.copyWith(
@ -229,15 +229,15 @@ class _HubDownHeroState extends State<_HubDownHero> {
height: 1.45,
),
),
const SizedBox(height: FaiSpace.xs),
const SizedBox(height: ChainSpace.xs),
Text(
l.welcomeHubDownEndpoint(HubService.instance.endpointLabel),
style: FaiTheme.mono(
style: ChainTheme.mono(
size: 11,
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: FaiSpace.lg),
const SizedBox(height: ChainSpace.lg),
if (canStart)
FilledButton.icon(
onPressed: _starting ? null : _start,
@ -253,8 +253,8 @@ class _HubDownHeroState extends State<_HubDownHero> {
),
)
else
const FaiBinaryRecovery(),
const SizedBox(height: FaiSpace.sm),
const ChainBinaryRecovery(),
const SizedBox(height: ChainSpace.sm),
TextButton.icon(
icon: const Icon(Icons.open_in_new, size: 16),
label: Text(l.welcomeHubDownDocsLink),
@ -323,7 +323,7 @@ class _PillarRow extends StatelessWidget {
children: [
for (final p in pillars) ...[
_Pillar(icon: p.$1, title: p.$2, body: p.$3),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
],
],
);
@ -339,7 +339,7 @@ class _PillarRow extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
for (var i = 0; i < pillars.length; i++) ...[
if (i > 0) const SizedBox(width: FaiSpace.md),
if (i > 0) const SizedBox(width: ChainSpace.md),
Expanded(
child: _Pillar(
icon: pillars[i].$1,
@ -367,24 +367,24 @@ class _Pillar extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.all(FaiSpace.lg),
padding: const EdgeInsets.all(ChainSpace.lg),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, size: 28, color: theme.colorScheme.primary),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Text(
title,
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: FaiSpace.sm),
const SizedBox(height: ChainSpace.sm),
Text(
body,
style: theme.textTheme.bodyMedium?.copyWith(
@ -427,7 +427,7 @@ class _TrustPosture extends StatelessWidget {
return Column(
children: [
for (var i = 0; i < rows.length; i++) ...[
if (i > 0) const SizedBox(height: FaiSpace.sm),
if (i > 0) const SizedBox(height: ChainSpace.sm),
_TrustRow(icon: rows[i].$1, title: rows[i].$2, body: rows[i].$3),
],
],
@ -450,17 +450,17 @@ class _TrustRow extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Container(
padding: const EdgeInsets.all(FaiSpace.lg),
padding: const EdgeInsets.all(ChainSpace.lg),
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, size: 22, color: theme.colorScheme.primary),
const SizedBox(width: FaiSpace.lg),
const SizedBox(width: ChainSpace.lg),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -601,13 +601,13 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
_aiOk,
l.welcomeChecklistAi,
l.welcomeChecklistAiHint,
() => FaiSettingsDialog.show(context),
() => ChainSettingsDialog.show(context),
),
(
_mcpOk,
l.welcomeChecklistMcp,
l.welcomeChecklistMcpHint,
() => FaiSettingsDialog.show(context),
() => ChainSettingsDialog.show(context),
),
(
_moduleOk,
@ -623,7 +623,7 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
),
];
return Padding(
padding: const EdgeInsets.only(bottom: FaiSpace.xxl),
padding: const EdgeInsets.only(bottom: ChainSpace.xxl),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -661,11 +661,11 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
Container(
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: Column(
@ -684,7 +684,7 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
),
),
if (allDone) ...[
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
_AllDoneCelebration(onDismiss: _dismiss),
],
],
@ -707,19 +707,19 @@ class _AllDoneCelebration extends StatelessWidget {
final theme = Theme.of(context);
final l = AppLocalizations.of(context)!;
return Container(
padding: const EdgeInsets.all(FaiSpace.lg),
padding: const EdgeInsets.all(ChainSpace.lg),
decoration: BoxDecoration(
color: FaiColors.success.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(FaiRadius.md),
border: Border.all(color: FaiColors.success.withValues(alpha: 0.35)),
color: ChainColors.success.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(ChainRadius.md),
border: Border.all(color: ChainColors.success.withValues(alpha: 0.35)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.celebration_outlined, color: FaiColors.success),
const SizedBox(width: FaiSpace.sm),
Icon(Icons.celebration_outlined, color: ChainColors.success),
const SizedBox(width: ChainSpace.sm),
Expanded(
child: Text(
l.welcomeChecklistAllSetTitle,
@ -741,7 +741,7 @@ class _AllDoneCelebration extends StatelessWidget {
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
_NextStepRow(
icon: Icons.timeline_outlined,
title: l.welcomeChecklistNextAuditTitle,
@ -797,12 +797,12 @@ class _NextStepRow extends StatelessWidget {
final theme = Theme.of(context);
final canActivate = doc != null && onOpenDoc != null;
return Padding(
padding: const EdgeInsets.only(bottom: FaiSpace.sm),
padding: const EdgeInsets.only(bottom: ChainSpace.sm),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, size: 18, color: theme.colorScheme.primary),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -823,7 +823,7 @@ class _NextStepRow extends StatelessWidget {
],
),
),
const SizedBox(width: FaiSpace.md),
const SizedBox(width: ChainSpace.md),
OutlinedButton(
onPressed: canActivate ? () => onOpenDoc!(doc!) : null,
child: Text(buttonLabel),
@ -856,16 +856,16 @@ class _ChecklistRow extends StatelessWidget {
final theme = Theme.of(context);
final l = AppLocalizations.of(context)!;
final row = Padding(
padding: const EdgeInsets.all(FaiSpace.lg),
padding: const EdgeInsets.all(ChainSpace.lg),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
done ? Icons.check_circle : Icons.radio_button_unchecked,
size: 20,
color: done ? FaiColors.success : theme.colorScheme.outline,
color: done ? ChainColors.success : theme.colorScheme.outline,
),
const SizedBox(width: FaiSpace.lg),
const SizedBox(width: ChainSpace.lg),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -890,10 +890,10 @@ class _ChecklistRow extends StatelessWidget {
],
),
),
const SizedBox(width: FaiSpace.md),
FaiPill(
const SizedBox(width: ChainSpace.md),
ChainPill(
label: done ? l.welcomeChecklistDone : l.welcomeChecklistTodo,
tone: done ? FaiPillTone.success : FaiPillTone.neutral,
tone: done ? ChainPillTone.success : ChainPillTone.neutral,
),
],
),
@ -985,7 +985,7 @@ class _DocsRow extends StatelessWidget {
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
LayoutBuilder(
builder: (context, constraints) {
final twoCols = constraints.maxWidth >= 640;
@ -997,16 +997,16 @@ class _DocsRow extends StatelessWidget {
return Column(
children: [
for (var i = 0; i < _kDocs.length; i++) ...[
if (i > 0) const SizedBox(height: FaiSpace.md),
if (i > 0) const SizedBox(height: ChainSpace.md),
_DocCard(entry: _kDocs[i]),
],
],
);
}
final cardWidth = (constraints.maxWidth - FaiSpace.md) / 2;
final cardWidth = (constraints.maxWidth - ChainSpace.md) / 2;
return Wrap(
spacing: FaiSpace.md,
runSpacing: FaiSpace.md,
spacing: ChainSpace.md,
runSpacing: ChainSpace.md,
children: [
for (final d in _kDocs)
SizedBox(
@ -1045,30 +1045,30 @@ class _DocCardState extends State<_DocCard> {
onEnter: (_) => setState(() => _hovered = true),
onExit: (_) => setState(() => _hovered = false),
child: AnimatedContainer(
duration: FaiMotion.base,
curve: FaiMotion.easing,
duration: ChainMotion.base,
curve: ChainMotion.easing,
transform: Matrix4.translationValues(0, _hovered ? -2 : 0, 0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(FaiRadius.md),
boxShadow: _hovered ? FaiElevation.medium(theme.brightness) : null,
borderRadius: BorderRadius.circular(ChainRadius.md),
boxShadow: _hovered ? ChainElevation.medium(theme.brightness) : null,
),
child: Material(
color: theme.colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
child: InkWell(
onTap: () => _DocReaderSheet.show(context, entry),
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
child: Container(
padding: const EdgeInsets.all(FaiSpace.lg),
padding: const EdgeInsets.all(ChainSpace.lg),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(FaiRadius.md),
borderRadius: BorderRadius.circular(ChainRadius.md),
border: Border.all(color: theme.colorScheme.outlineVariant),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(entry.icon, size: 22, color: theme.colorScheme.primary),
const SizedBox(width: FaiSpace.lg),
const SizedBox(width: ChainSpace.lg),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -1159,7 +1159,7 @@ class _DocReaderSheet extends StatefulWidget {
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
elevation: 8,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(FaiRadius.md)),
borderRadius: BorderRadius.vertical(top: Radius.circular(ChainRadius.md)),
),
builder: (_) => _DocReaderSheet(entry: entry),
);
@ -1191,7 +1191,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> {
// the .dart_tool asset manifest can be inconsistent with
// the compiled binary surface "rebuild Studio" as a
// first-class hint rather than just dumping the raw
// PlatformException string into FaiErrorBox.
// PlatformException string into ChainErrorBox.
try {
return await rootBundle.loadString(localised);
} catch (firstErr) {
@ -1219,7 +1219,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> {
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: FaiSpace.sm),
padding: const EdgeInsets.symmetric(vertical: ChainSpace.sm),
child: Container(
width: 40,
height: 4,
@ -1231,10 +1231,10 @@ class _DocReaderSheetState extends State<_DocReaderSheet> {
),
Padding(
padding: const EdgeInsets.fromLTRB(
FaiSpace.xxl,
FaiSpace.sm,
FaiSpace.lg,
FaiSpace.sm,
ChainSpace.xxl,
ChainSpace.sm,
ChainSpace.lg,
ChainSpace.sm,
),
child: Row(
children: [
@ -1243,7 +1243,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> {
size: 20,
color: theme.colorScheme.primary,
),
const SizedBox(width: FaiSpace.sm),
const SizedBox(width: ChainSpace.sm),
Expanded(
child: Text(
widget.entry.title(l),
@ -1268,7 +1268,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> {
builder: (context, snap) {
if (snap.connectionState == ConnectionState.waiting) {
return const Padding(
padding: EdgeInsets.all(FaiSpace.xxl),
padding: EdgeInsets.all(ChainSpace.xxl),
child: Center(child: CircularProgressIndicator()),
);
}
@ -1276,7 +1276,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> {
final err = snap.error;
final structured = err is _DocReaderError ? err : null;
return Padding(
padding: const EdgeInsets.all(FaiSpace.xxl),
padding: const EdgeInsets.all(ChainSpace.xxl),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1286,21 +1286,21 @@ class _DocReaderSheetState extends State<_DocReaderSheet> {
color: theme.colorScheme.error,
),
),
const SizedBox(height: FaiSpace.sm),
const SizedBox(height: ChainSpace.sm),
if (structured != null)
FaiErrorBox(
ChainErrorBox(
text: structured.localizedBody(l),
isError: true,
maxHeight: 200,
)
else
FaiErrorBox(
ChainErrorBox(
error: err,
isError: true,
maxHeight: 200,
),
if (structured != null) ...[
const SizedBox(height: FaiSpace.md),
const SizedBox(height: ChainSpace.md),
TextButton.icon(
icon: const Icon(Icons.open_in_new, size: 16),
label: Text(structured.forgejoUrl),
@ -1314,14 +1314,14 @@ class _DocReaderSheetState extends State<_DocReaderSheet> {
}
return Markdown(
data: snap.data ?? '',
padding: const EdgeInsets.all(FaiSpace.xxl),
padding: const EdgeInsets.all(ChainSpace.xxl),
selectable: true,
onTapLink: (text, href, title) async {
if (href != null && href.isNotEmpty) {
await SystemActions.openInOs(href);
}
},
styleSheet: FaiTheme.markdownStyle(theme),
styleSheet: ChainTheme.markdownStyle(theme),
);
},
),