refactor: rename internal Fai* design system + fai_ helpers to chain
Some checks failed
Security / Security check (push) Failing after 2s
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:
parent
68d23ab7dd
commit
891acd2ba2
52 changed files with 1225 additions and 1225 deletions
|
|
@ -1,6 +1,6 @@
|
|||
// Recovery affordance shown when Studio cannot locate the `fai`
|
||||
// binary on the machine. Replaces the old CLI-jargon dead-end
|
||||
// ("set FAI_BIN to its full path") with two acts a non-CLI
|
||||
// ("set CHAIN_BIN to its full path") with two acts a non-CLI
|
||||
// operator can actually take:
|
||||
//
|
||||
// 1. Locate the binary with a native file picker, persisting
|
||||
|
|
@ -14,26 +14,26 @@ import '../data/system_actions.dart';
|
|||
import '../l10n/app_localizations.dart';
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
class FaiBinaryRecovery extends StatelessWidget {
|
||||
class ChainBinaryRecovery extends StatelessWidget {
|
||||
/// Fired after the operator successfully picks a `fai` binary,
|
||||
/// so the host can retry whatever action hit the missing
|
||||
/// binary (e.g. re-run the daemon start / status probe).
|
||||
final VoidCallback? onLocated;
|
||||
|
||||
const FaiBinaryRecovery({super.key, this.onLocated});
|
||||
const ChainBinaryRecovery({super.key, this.onLocated});
|
||||
|
||||
Future<void> _locate(BuildContext context) async {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
final picked = await FilePicker.pickFiles(
|
||||
dialogTitle: l.faiBinaryPickerTitle,
|
||||
dialogTitle: l.chainBinaryPickerTitle,
|
||||
);
|
||||
final path = picked?.files.single.path;
|
||||
if (path == null) return;
|
||||
final ok = await SystemActions.setFaiBinaryPath(path);
|
||||
messenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(ok ? l.faiBinarySetOk(path) : l.faiBinaryNotFound),
|
||||
content: Text(ok ? l.chainBinarySetOk(path) : l.chainBinaryNotFound),
|
||||
),
|
||||
);
|
||||
if (ok) onLocated?.call();
|
||||
|
|
@ -45,10 +45,10 @@ class FaiBinaryRecovery extends StatelessWidget {
|
|||
final l = AppLocalizations.of(context)!;
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(FaiSpace.md),
|
||||
padding: const EdgeInsets.all(ChainSpace.md),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.errorContainer.withValues(alpha: 0.4),
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
border: Border.all(
|
||||
color: theme.colorScheme.error.withValues(alpha: 0.4),
|
||||
),
|
||||
|
|
@ -57,34 +57,34 @@ class FaiBinaryRecovery extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l.faiBinaryNotFound,
|
||||
l.chainBinaryNotFound,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: theme.colorScheme.error,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.xs),
|
||||
const SizedBox(height: ChainSpace.xs),
|
||||
Text(
|
||||
l.faiBinaryNotFoundHint,
|
||||
l.chainBinaryNotFoundHint,
|
||||
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: () => _locate(context),
|
||||
icon: const Icon(Icons.folder_open, size: 16),
|
||||
label: Text(l.faiBinaryLocateButton),
|
||||
label: Text(l.chainBinaryLocateButton),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () =>
|
||||
SystemActions.openInOs(kFaiInstallDocsUrl),
|
||||
icon: const Icon(Icons.open_in_new, size: 16),
|
||||
label: Text(l.faiBinaryInstallDocsButton),
|
||||
label: Text(l.chainBinaryInstallDocsButton),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
// FaiCard — flat card with subtle accent gradient on the top
|
||||
// ChainCard — flat card with subtle accent gradient on the top
|
||||
// border. Replaces Material's default Card for the Ch∆In look.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
class FaiCard extends StatelessWidget {
|
||||
class ChainCard extends StatelessWidget {
|
||||
/// Card body. Padding is applied internally; pass plain content.
|
||||
final Widget child;
|
||||
|
||||
|
|
@ -18,15 +18,15 @@ class FaiCard extends StatelessWidget {
|
|||
/// Used by status rows (live event, healthy service).
|
||||
final Color? accentLeft;
|
||||
|
||||
/// Internal padding. Defaults to [FaiSpace.lg].
|
||||
/// Internal padding. Defaults to [ChainSpace.lg].
|
||||
final EdgeInsetsGeometry padding;
|
||||
|
||||
const FaiCard({
|
||||
const ChainCard({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.accentTop = false,
|
||||
this.accentLeft,
|
||||
this.padding = const EdgeInsets.all(FaiSpace.lg),
|
||||
this.padding = const EdgeInsets.all(ChainSpace.lg),
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -35,7 +35,7 @@ class FaiCard extends StatelessWidget {
|
|||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.md),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.md),
|
||||
border: Border.all(color: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
|
|
@ -61,8 +61,8 @@ class FaiCard extends StatelessWidget {
|
|||
),
|
||||
if (accentLeft != null)
|
||||
Positioned(
|
||||
top: FaiSpace.md,
|
||||
bottom: FaiSpace.md,
|
||||
top: ChainSpace.md,
|
||||
bottom: ChainSpace.md,
|
||||
left: 0,
|
||||
width: 3,
|
||||
child: DecoratedBox(
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiDataRow — Linear-style dense list row used by the audit
|
||||
// ChainDataRow — Linear-style dense list row used by the audit
|
||||
// stream. Hover-elevation, accent-coloured leading stripe, mono
|
||||
// timestamp, expressive type badge.
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
|
|||
import '../theme/theme.dart';
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
class FaiDataRow extends StatefulWidget {
|
||||
class ChainDataRow extends StatefulWidget {
|
||||
/// Coloured leading stripe (4px). Used to encode event type
|
||||
/// (success / warning / failure) at a glance.
|
||||
final Color accent;
|
||||
|
|
@ -27,7 +27,7 @@ class FaiDataRow extends StatefulWidget {
|
|||
/// Optional tap handler.
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const FaiDataRow({
|
||||
const ChainDataRow({
|
||||
super.key,
|
||||
required this.accent,
|
||||
required this.leading,
|
||||
|
|
@ -38,10 +38,10 @@ class FaiDataRow extends StatefulWidget {
|
|||
});
|
||||
|
||||
@override
|
||||
State<FaiDataRow> createState() => _FaiDataRowState();
|
||||
State<ChainDataRow> createState() => _FaiDataRowState();
|
||||
}
|
||||
|
||||
class _FaiDataRowState extends State<FaiDataRow> {
|
||||
class _FaiDataRowState extends State<ChainDataRow> {
|
||||
bool _hovered = false;
|
||||
|
||||
@override
|
||||
|
|
@ -57,12 +57,12 @@ class _FaiDataRowState extends State<FaiDataRow> {
|
|||
onTap: widget.onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: AnimatedContainer(
|
||||
duration: FaiMotion.fast,
|
||||
duration: ChainMotion.fast,
|
||||
decoration: BoxDecoration(
|
||||
color: _hovered
|
||||
? theme.colorScheme.surfaceContainerHigh
|
||||
: theme.colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
border: Border.all(
|
||||
color: _hovered
|
||||
? theme.colorScheme.outline
|
||||
|
|
@ -70,8 +70,8 @@ class _FaiDataRowState extends State<FaiDataRow> {
|
|||
),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: FaiSpace.md,
|
||||
vertical: FaiSpace.sm,
|
||||
horizontal: ChainSpace.md,
|
||||
vertical: ChainSpace.sm,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
@ -83,12 +83,12 @@ class _FaiDataRowState extends State<FaiDataRow> {
|
|||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.md),
|
||||
const SizedBox(width: ChainSpace.md),
|
||||
SizedBox(
|
||||
width: 88,
|
||||
child: Text(
|
||||
widget.leading,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
@ -98,7 +98,7 @@ class _FaiDataRowState extends State<FaiDataRow> {
|
|||
width: 200,
|
||||
child: Text(
|
||||
widget.title,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 12,
|
||||
weight: FontWeight.w500,
|
||||
color: theme.colorScheme.onSurface,
|
||||
|
|
@ -116,10 +116,10 @@ class _FaiDataRowState extends State<FaiDataRow> {
|
|||
),
|
||||
if (widget.trailing != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: FaiSpace.md),
|
||||
padding: const EdgeInsets.only(left: ChainSpace.md),
|
||||
child: Text(
|
||||
widget.trailing!,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiDeltaMark — the Ch∆In signature element. A precise triangle
|
||||
// ChainDeltaMark — the Ch∆In signature element. A precise triangle
|
||||
// (∆) drawn from primitives, not a font glyph. Three states:
|
||||
//
|
||||
// - idle: static, accent colour, soft glow
|
||||
|
|
@ -12,25 +12,25 @@ import 'dart:math';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum FaiDeltaMode { idle, live, busy }
|
||||
enum ChainDeltaMode { idle, live, busy }
|
||||
|
||||
class FaiDeltaMark extends StatefulWidget {
|
||||
final FaiDeltaMode mode;
|
||||
class ChainDeltaMark extends StatefulWidget {
|
||||
final ChainDeltaMode mode;
|
||||
final double size;
|
||||
final Color color;
|
||||
|
||||
const FaiDeltaMark({
|
||||
const ChainDeltaMark({
|
||||
super.key,
|
||||
required this.color,
|
||||
this.mode = FaiDeltaMode.idle,
|
||||
this.mode = ChainDeltaMode.idle,
|
||||
this.size = 36,
|
||||
});
|
||||
|
||||
@override
|
||||
State<FaiDeltaMark> createState() => _FaiDeltaMarkState();
|
||||
State<ChainDeltaMark> createState() => _FaiDeltaMarkState();
|
||||
}
|
||||
|
||||
class _FaiDeltaMarkState extends State<FaiDeltaMark>
|
||||
class _FaiDeltaMarkState extends State<ChainDeltaMark>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _ctrl;
|
||||
|
||||
|
|
@ -44,13 +44,13 @@ class _FaiDeltaMarkState extends State<FaiDeltaMark>
|
|||
_restart();
|
||||
}
|
||||
|
||||
Duration _durationFor(FaiDeltaMode m) {
|
||||
Duration _durationFor(ChainDeltaMode m) {
|
||||
switch (m) {
|
||||
case FaiDeltaMode.idle:
|
||||
case ChainDeltaMode.idle:
|
||||
return const Duration(seconds: 1);
|
||||
case FaiDeltaMode.live:
|
||||
case ChainDeltaMode.live:
|
||||
return const Duration(milliseconds: 1600);
|
||||
case FaiDeltaMode.busy:
|
||||
case ChainDeltaMode.busy:
|
||||
return const Duration(seconds: 4);
|
||||
}
|
||||
}
|
||||
|
|
@ -58,21 +58,21 @@ class _FaiDeltaMarkState extends State<FaiDeltaMark>
|
|||
void _restart() {
|
||||
_ctrl.duration = _durationFor(widget.mode);
|
||||
switch (widget.mode) {
|
||||
case FaiDeltaMode.idle:
|
||||
case ChainDeltaMode.idle:
|
||||
_ctrl.stop();
|
||||
_ctrl.value = 0;
|
||||
break;
|
||||
case FaiDeltaMode.live:
|
||||
case ChainDeltaMode.live:
|
||||
_ctrl.repeat(reverse: true);
|
||||
break;
|
||||
case FaiDeltaMode.busy:
|
||||
case ChainDeltaMode.busy:
|
||||
_ctrl.repeat();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant FaiDeltaMark old) {
|
||||
void didUpdateWidget(covariant ChainDeltaMark old) {
|
||||
super.didUpdateWidget(old);
|
||||
if (old.mode != widget.mode) _restart();
|
||||
}
|
||||
|
|
@ -103,7 +103,7 @@ class _FaiDeltaMarkState extends State<FaiDeltaMark>
|
|||
|
||||
class _DeltaPainter extends CustomPainter {
|
||||
final Color color;
|
||||
final FaiDeltaMode mode;
|
||||
final ChainDeltaMode mode;
|
||||
final double t;
|
||||
|
||||
_DeltaPainter({required this.color, required this.mode, required this.t});
|
||||
|
|
@ -115,12 +115,12 @@ class _DeltaPainter extends CustomPainter {
|
|||
|
||||
// In live mode: scale the whole mark between 0.85 and 1.15
|
||||
// so the pulse is visible without staring. Idle stays at 1.0.
|
||||
final scale = mode == FaiDeltaMode.live ? 0.85 + 0.30 * t : 1.0;
|
||||
final scale = mode == ChainDeltaMode.live ? 0.85 + 0.30 * t : 1.0;
|
||||
final r = size.width * 0.36 * scale;
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(cx, cy);
|
||||
if (mode == FaiDeltaMode.busy) {
|
||||
if (mode == ChainDeltaMode.busy) {
|
||||
canvas.rotate(t * 2 * pi);
|
||||
}
|
||||
|
||||
|
|
@ -133,8 +133,8 @@ class _DeltaPainter extends CustomPainter {
|
|||
|
||||
// Glow halo. In live mode the glow ring grows with the pulse
|
||||
// and the alpha breathes harder.
|
||||
if (mode != FaiDeltaMode.idle) {
|
||||
final pulseStrength = mode == FaiDeltaMode.live ? t : 1.0;
|
||||
if (mode != ChainDeltaMode.idle) {
|
||||
final pulseStrength = mode == ChainDeltaMode.live ? t : 1.0;
|
||||
final glow = Paint()
|
||||
..color = color.withValues(alpha: 0.30 + 0.40 * pulseStrength)
|
||||
..maskFilter = MaskFilter.blur(BlurStyle.normal, 8 + 14 * pulseStrength)
|
||||
|
|
@ -143,7 +143,7 @@ class _DeltaPainter extends CustomPainter {
|
|||
}
|
||||
|
||||
// Filled triangle, brighter on the up-beat for live mode.
|
||||
final fillAlpha = mode == FaiDeltaMode.live ? 0.15 + 0.20 * t : 0.18;
|
||||
final fillAlpha = mode == ChainDeltaMode.live ? 0.15 + 0.20 * t : 0.18;
|
||||
final fill = Paint()
|
||||
..color = color.withValues(alpha: fillAlpha)
|
||||
..style = PaintingStyle.fill;
|
||||
|
|
@ -158,7 +158,7 @@ class _DeltaPainter extends CustomPainter {
|
|||
canvas.drawPath(path, stroke);
|
||||
|
||||
// Inner accent dot.
|
||||
final dotAlpha = mode == FaiDeltaMode.live ? 0.55 + 0.45 * t : 1.0;
|
||||
final dotAlpha = mode == ChainDeltaMode.live ? 0.55 + 0.45 * t : 1.0;
|
||||
final dot = Paint()
|
||||
..color = color.withValues(alpha: dotAlpha)
|
||||
..style = PaintingStyle.fill;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiEmptyState — gracious empty / loading / error placeholder.
|
||||
// ChainEmptyState — gracious empty / loading / error placeholder.
|
||||
// Replaces "(no data)" strings with one tone, one icon, one tip.
|
||||
//
|
||||
// The blank surface is where an app most easily feels unfinished,
|
||||
|
|
@ -11,9 +11,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../theme/tokens.dart';
|
||||
import 'fai_delta_mark.dart';
|
||||
import 'chain_delta_mark.dart';
|
||||
|
||||
class FaiEmptyState extends StatelessWidget {
|
||||
class ChainEmptyState extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String title;
|
||||
final String? hint;
|
||||
|
|
@ -23,7 +23,7 @@ class FaiEmptyState extends StatelessWidget {
|
|||
/// for connection-failure variants.
|
||||
final Color? iconColor;
|
||||
|
||||
const FaiEmptyState({
|
||||
const ChainEmptyState({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
|
|
@ -40,7 +40,7 @@ class FaiEmptyState extends StatelessWidget {
|
|||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 440),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(FaiSpace.xxl),
|
||||
padding: const EdgeInsets.all(ChainSpace.xxl),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
@ -57,7 +57,7 @@ class FaiEmptyState extends StatelessWidget {
|
|||
children: [
|
||||
Opacity(
|
||||
opacity: 0.10,
|
||||
child: FaiDeltaMark(
|
||||
child: ChainDeltaMark(
|
||||
color: theme.colorScheme.primary,
|
||||
size: 112,
|
||||
),
|
||||
|
|
@ -71,14 +71,14 @@ class FaiEmptyState extends StatelessWidget {
|
|||
border: Border.all(
|
||||
color: color.withValues(alpha: 0.18),
|
||||
),
|
||||
boxShadow: FaiElevation.low(theme.brightness),
|
||||
boxShadow: ChainElevation.low(theme.brightness),
|
||||
),
|
||||
child: Icon(icon, size: 26, color: color),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.xl),
|
||||
const SizedBox(height: ChainSpace.xl),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
|
|
@ -87,7 +87,7 @@ class FaiEmptyState extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
if (hint != null) ...[
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Text(
|
||||
hint!,
|
||||
textAlign: TextAlign.center,
|
||||
|
|
@ -98,7 +98,7 @@ class FaiEmptyState extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
if (action != null) ...[
|
||||
const SizedBox(height: FaiSpace.xl),
|
||||
const SizedBox(height: ChainSpace.xl),
|
||||
action!,
|
||||
],
|
||||
],
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiEnBadge — small "[EN]" pill shown next to text that came
|
||||
// ChainEnBadge — small "[EN]" pill shown next to text that came
|
||||
// from outside Studio's localization pipeline (MCP server tool
|
||||
// names, n8n endpoint descriptions, native LLM responses).
|
||||
//
|
||||
|
|
@ -14,21 +14,21 @@ import 'package:flutter/material.dart';
|
|||
import '../l10n/app_localizations.dart';
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
class FaiEnBadge extends StatelessWidget {
|
||||
class ChainEnBadge extends StatelessWidget {
|
||||
/// When true, the badge renders. When false (e.g. the active
|
||||
/// locale already is English), it returns
|
||||
/// `SizedBox.shrink()` so callers can drop it inline without
|
||||
/// guarding visibility themselves.
|
||||
final bool visible;
|
||||
|
||||
const FaiEnBadge({super.key, required this.visible});
|
||||
const ChainEnBadge({super.key, required this.visible});
|
||||
|
||||
/// Convenience constructor: derives `visible` from the active
|
||||
/// Localizations locale. Use this from inside a Build method
|
||||
/// where you already have a BuildContext.
|
||||
factory FaiEnBadge.forContext(BuildContext context) {
|
||||
factory ChainEnBadge.forContext(BuildContext context) {
|
||||
final lang = Localizations.localeOf(context).languageCode;
|
||||
return FaiEnBadge(visible: lang != 'en');
|
||||
return ChainEnBadge(visible: lang != 'en');
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -42,7 +42,7 @@ class FaiEnBadge extends StatelessWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
border: Border.all(color: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
child: Text(
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiErrorBox — selectable monospace error / output block with
|
||||
// ChainErrorBox — selectable monospace error / output block with
|
||||
// a small copy-to-clipboard button in the top-right corner.
|
||||
//
|
||||
// Used wherever the operator might want to paste daemon stderr
|
||||
|
|
@ -15,7 +15,7 @@ import '../l10n/app_localizations.dart';
|
|||
import '../theme/theme.dart';
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
class FaiErrorBox extends StatefulWidget {
|
||||
class ChainErrorBox extends StatefulWidget {
|
||||
/// The text the operator wants to read (and copy). Rendered
|
||||
/// monospace, selectable, multi-line. Ignored when [error] is
|
||||
/// supplied.
|
||||
|
|
@ -41,7 +41,7 @@ class FaiErrorBox extends StatefulWidget {
|
|||
/// trailers otherwise.
|
||||
final Object? error;
|
||||
|
||||
const FaiErrorBox({
|
||||
const ChainErrorBox({
|
||||
super.key,
|
||||
this.text = '',
|
||||
this.isError = false,
|
||||
|
|
@ -49,14 +49,14 @@ class FaiErrorBox extends StatefulWidget {
|
|||
this.error,
|
||||
}) : assert(
|
||||
text != '' || error != null,
|
||||
'FaiErrorBox needs either text or error',
|
||||
'ChainErrorBox needs either text or error',
|
||||
);
|
||||
|
||||
@override
|
||||
State<FaiErrorBox> createState() => _FaiErrorBoxState();
|
||||
State<ChainErrorBox> createState() => _FaiErrorBoxState();
|
||||
}
|
||||
|
||||
class _FaiErrorBoxState extends State<FaiErrorBox> {
|
||||
class _FaiErrorBoxState extends State<ChainErrorBox> {
|
||||
bool _justCopied = false;
|
||||
bool _detailExpanded = false;
|
||||
|
||||
|
|
@ -86,14 +86,14 @@ class _FaiErrorBoxState extends State<FaiErrorBox> {
|
|||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
FaiSpace.sm,
|
||||
FaiSpace.xs,
|
||||
FaiSpace.xs,
|
||||
FaiSpace.sm,
|
||||
ChainSpace.sm,
|
||||
ChainSpace.xs,
|
||||
ChainSpace.xs,
|
||||
ChainSpace.sm,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
border: Border.all(color: accent.withValues(alpha: 0.4)),
|
||||
),
|
||||
child: Column(
|
||||
|
|
@ -133,7 +133,7 @@ class _FaiErrorBoxState extends State<FaiErrorBox> {
|
|||
),
|
||||
],
|
||||
if (friendly.detail.isNotEmpty) ...[
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
InkWell(
|
||||
onTap: () => setState(() {
|
||||
_detailExpanded = !_detailExpanded;
|
||||
|
|
@ -159,7 +159,7 @@ class _FaiErrorBoxState extends State<FaiErrorBox> {
|
|||
),
|
||||
),
|
||||
if (_detailExpanded) ...[
|
||||
const SizedBox(height: FaiSpace.xs),
|
||||
const SizedBox(height: ChainSpace.xs),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: widget.maxHeight ?? double.infinity,
|
||||
|
|
@ -168,7 +168,7 @@ class _FaiErrorBoxState extends State<FaiErrorBox> {
|
|||
child: SingleChildScrollView(
|
||||
child: SelectableText(
|
||||
friendly.detail,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
@ -187,7 +187,7 @@ class _FaiErrorBoxState extends State<FaiErrorBox> {
|
|||
child: SingleChildScrollView(
|
||||
child: SelectableText(
|
||||
widget.text,
|
||||
style: FaiTheme.mono(size: 11, color: fg),
|
||||
style: ChainTheme.mono(size: 11, color: fg),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiFlowOutput — type-aware renderer for one entry in
|
||||
// ChainFlowOutput — type-aware renderer for one entry in
|
||||
// `runSavedFlow`'s output map. Reads `FlowOutput` and dispatches
|
||||
// to the right widget:
|
||||
//
|
||||
|
|
@ -27,10 +27,10 @@ import '../l10n/app_localizations.dart';
|
|||
import '../theme/theme.dart';
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
class FaiFlowOutput extends StatelessWidget {
|
||||
class ChainFlowOutput extends StatelessWidget {
|
||||
final FlowOutput output;
|
||||
|
||||
const FaiFlowOutput({super.key, required this.output});
|
||||
const ChainFlowOutput({super.key, required this.output});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -75,10 +75,10 @@ class _TextView extends StatelessWidget {
|
|||
if (_looksLikeMarkdown) {
|
||||
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: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
child: MarkdownBody(
|
||||
|
|
@ -89,16 +89,16 @@ class _TextView extends StatelessWidget {
|
|||
await SystemActions.openInOs(href);
|
||||
}
|
||||
},
|
||||
styleSheet: FaiTheme.markdownStyle(theme),
|
||||
styleSheet: ChainTheme.markdownStyle(theme),
|
||||
),
|
||||
);
|
||||
}
|
||||
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: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
child: SelectableText(
|
||||
|
|
@ -119,15 +119,15 @@ class _CodeBlock extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
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: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
child: SelectableText(
|
||||
text,
|
||||
style: FaiTheme.mono(size: 12, color: theme.colorScheme.onSurface),
|
||||
style: ChainTheme.mono(size: 12, color: theme.colorScheme.onSurface),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -183,10 +183,10 @@ class _BytesView extends StatelessWidget {
|
|||
final l = AppLocalizations.of(context)!;
|
||||
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: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
child: Column(
|
||||
|
|
@ -196,11 +196,11 @@ class _BytesView extends StatelessWidget {
|
|||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: 240),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
child: Image.memory(bytes, fit: BoxFit.contain),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
],
|
||||
Text(
|
||||
'${mimeType.isEmpty ? l.flowsOutputBytesUnknownMime : mimeType}'
|
||||
|
|
@ -209,7 +209,7 @@ class _BytesView extends StatelessWidget {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
OutlinedButton.icon(
|
||||
icon: const Icon(Icons.save_alt, size: 16),
|
||||
label: Text(l.flowsOutputSaveAs),
|
||||
|
|
@ -247,10 +247,10 @@ class _FileView extends StatelessWidget {
|
|||
final l = AppLocalizations.of(context)!;
|
||||
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: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
child: Column(
|
||||
|
|
@ -258,7 +258,7 @@ class _FileView extends StatelessWidget {
|
|||
children: [
|
||||
SelectableText(
|
||||
uri,
|
||||
style: FaiTheme.mono(size: 11, color: theme.colorScheme.primary),
|
||||
style: ChainTheme.mono(size: 11, color: theme.colorScheme.primary),
|
||||
),
|
||||
if (mimeType.isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
|
|
@ -269,7 +269,7 @@ class _FileView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
OutlinedButton.icon(
|
||||
icon: const Icon(Icons.open_in_new, size: 16),
|
||||
label: Text(l.flowsOutputOpen),
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiLogViewer — modal sheet that renders a log file inline with
|
||||
// ChainLogViewer — modal sheet that renders a log file inline with
|
||||
// line numbers, basic syntax colouring and a Copy-all button.
|
||||
//
|
||||
// Use [showFaiLogViewer] to open it. Two log shapes are supported
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
// anything else renders neutral.
|
||||
// - tracing text (the daemon's `~/.chain/run/<channel>.log`):
|
||||
// `[INFO]`, `[WARN]`, `[ERROR]`, `[DEBUG]` substrings get the
|
||||
// matching tone from the FaiTheme palette.
|
||||
// matching tone from the ChainTheme palette.
|
||||
//
|
||||
// The viewer reads the *last* N lines (default 500) to keep
|
||||
// rendering snappy on huge log files. A Refresh button re-reads
|
||||
|
|
@ -42,7 +42,7 @@ Future<void> showFaiLogViewer(
|
|||
return showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (_) => FaiLogViewer(
|
||||
builder: (_) => ChainLogViewer(
|
||||
path: path,
|
||||
title: title ?? path.split(Platform.pathSeparator).last,
|
||||
tailLines: tailLines,
|
||||
|
|
@ -72,7 +72,7 @@ Future<void> showFaiConfigViewer(
|
|||
);
|
||||
}
|
||||
|
||||
class FaiLogViewer extends StatefulWidget {
|
||||
class ChainLogViewer extends StatefulWidget {
|
||||
final String path;
|
||||
final String title;
|
||||
final int tailLines;
|
||||
|
|
@ -84,7 +84,7 @@ class FaiLogViewer extends StatefulWidget {
|
|||
/// (for config / plain-text files).
|
||||
final bool plain;
|
||||
|
||||
const FaiLogViewer({
|
||||
const ChainLogViewer({
|
||||
super.key,
|
||||
required this.path,
|
||||
required this.title,
|
||||
|
|
@ -94,10 +94,10 @@ class FaiLogViewer extends StatefulWidget {
|
|||
});
|
||||
|
||||
@override
|
||||
State<FaiLogViewer> createState() => _FaiLogViewerState();
|
||||
State<ChainLogViewer> createState() => _FaiLogViewerState();
|
||||
}
|
||||
|
||||
class _FaiLogViewerState extends State<FaiLogViewer> {
|
||||
class _FaiLogViewerState extends State<ChainLogViewer> {
|
||||
List<String> _lines = const <String>[];
|
||||
bool _loading = true;
|
||||
bool _justCopied = false;
|
||||
|
|
@ -141,8 +141,8 @@ class _FaiLogViewerState extends State<FaiLogViewer> {
|
|||
final l = AppLocalizations.of(context)!;
|
||||
return Dialog(
|
||||
insetPadding: const EdgeInsets.symmetric(
|
||||
horizontal: FaiSpace.xl,
|
||||
vertical: FaiSpace.xl,
|
||||
horizontal: ChainSpace.xl,
|
||||
vertical: ChainSpace.xl,
|
||||
),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 980, maxHeight: 720),
|
||||
|
|
@ -169,7 +169,7 @@ class _FaiLogViewerState extends State<FaiLogViewer> {
|
|||
: _lines.isEmpty
|
||||
? Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(FaiSpace.xl),
|
||||
padding: const EdgeInsets.all(ChainSpace.xl),
|
||||
child: Text(
|
||||
l.logViewerEmpty,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
|
|
@ -218,10 +218,10 @@ class _Header extends StatelessWidget {
|
|||
final l = AppLocalizations.of(context)!;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
FaiSpace.lg,
|
||||
FaiSpace.md,
|
||||
FaiSpace.sm,
|
||||
FaiSpace.md,
|
||||
ChainSpace.lg,
|
||||
ChainSpace.md,
|
||||
ChainSpace.sm,
|
||||
ChainSpace.md,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
@ -230,7 +230,7 @@ class _Header extends StatelessWidget {
|
|||
size: 18,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -246,7 +246,7 @@ class _Header extends StatelessWidget {
|
|||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'$path · ${l.logViewerLineCount(lineCount)}',
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 10,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
@ -296,7 +296,7 @@ class _LogBody extends StatelessWidget {
|
|||
final gutterWidth = 12.0 + 8.0 * lines.length.toString().length;
|
||||
return Scrollbar(
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(vertical: FaiSpace.sm),
|
||||
padding: const EdgeInsets.symmetric(vertical: ChainSpace.sm),
|
||||
itemCount: lines.length,
|
||||
itemBuilder: (context, i) => _LogLine(
|
||||
lineNumber: i + 1,
|
||||
|
|
@ -328,7 +328,7 @@ class _LogLine extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: FaiSpace.sm, vertical: 1),
|
||||
padding: const EdgeInsets.symmetric(horizontal: ChainSpace.sm, vertical: 1),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
@ -337,7 +337,7 @@ class _LogLine extends StatelessWidget {
|
|||
child: Text(
|
||||
'$lineNumber',
|
||||
textAlign: TextAlign.right,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant.withValues(
|
||||
alpha: 0.6,
|
||||
|
|
@ -345,7 +345,7 @@ class _LogLine extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
Expanded(
|
||||
child: SelectableText.rich(
|
||||
plain
|
||||
|
|
@ -354,7 +354,7 @@ class _LogLine extends StatelessWidget {
|
|||
style: TextStyle(color: theme.colorScheme.onSurface),
|
||||
)
|
||||
: _highlightLine(text, theme),
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
|
|
@ -388,7 +388,7 @@ TextSpan _highlightJsonLine(String line, ThemeData theme) {
|
|||
color: theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
final stringStyle = TextStyle(color: FaiColors.success);
|
||||
final stringStyle = TextStyle(color: ChainColors.success);
|
||||
final errorStringStyle = TextStyle(color: theme.colorScheme.error);
|
||||
|
||||
final keyRe = RegExp(r'"(ts|level|source|error|context)"\s*:');
|
||||
|
|
@ -440,7 +440,7 @@ TextSpan _highlightTraceLine(String line, ThemeData theme) {
|
|||
break;
|
||||
case 'WARN':
|
||||
case 'WARNING':
|
||||
tone = FaiColors.warning;
|
||||
tone = ChainColors.warning;
|
||||
break;
|
||||
case 'INFO':
|
||||
tone = theme.colorScheme.primary;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiModuleSheet — modal bottom-sheet showing detailed module
|
||||
// ChainModuleSheet — modal bottom-sheet showing detailed module
|
||||
// info: full capability list, declared permissions, on-disk
|
||||
// directory. Opened by tapping a module card on the Modules
|
||||
// page.
|
||||
|
|
@ -11,12 +11,12 @@ import '../l10n/app_localizations.dart';
|
|||
import '../pages/welcome.dart' show showFaiDoc;
|
||||
import '../theme/theme.dart';
|
||||
import '../theme/tokens.dart';
|
||||
import 'fai_pill.dart';
|
||||
import 'chain_pill.dart';
|
||||
|
||||
class FaiModuleSheet extends StatefulWidget {
|
||||
class ChainModuleSheet extends StatefulWidget {
|
||||
final String moduleName;
|
||||
|
||||
const FaiModuleSheet({super.key, required this.moduleName});
|
||||
const ChainModuleSheet({super.key, required this.moduleName});
|
||||
|
||||
/// Convenience launcher used from the Modules list. Resolves
|
||||
/// to `true` when the operator uninstalled the module — the
|
||||
|
|
@ -28,18 +28,18 @@ class FaiModuleSheet extends StatefulWidget {
|
|||
isScrollControlled: true,
|
||||
elevation: 8,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(FaiRadius.md)),
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(ChainRadius.md)),
|
||||
),
|
||||
builder: (_) => FaiModuleSheet(moduleName: name),
|
||||
builder: (_) => ChainModuleSheet(moduleName: name),
|
||||
);
|
||||
return r ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
State<FaiModuleSheet> createState() => _FaiModuleSheetState();
|
||||
State<ChainModuleSheet> createState() => _FaiModuleSheetState();
|
||||
}
|
||||
|
||||
class _FaiModuleSheetState extends State<FaiModuleSheet> {
|
||||
class _FaiModuleSheetState extends State<ChainModuleSheet> {
|
||||
late final Future<ModuleDetail> _future;
|
||||
bool _uninstalling = false;
|
||||
|
||||
|
|
@ -127,12 +127,12 @@ class _FaiModuleSheetState extends State<FaiModuleSheet> {
|
|||
_Handle(),
|
||||
if (snapshot.connectionState == ConnectionState.waiting)
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(FaiSpace.xxxl),
|
||||
padding: EdgeInsets.all(ChainSpace.xxxl),
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
else if (snapshot.hasError)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(FaiSpace.xl),
|
||||
padding: const EdgeInsets.all(ChainSpace.xl),
|
||||
child: Text(
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
|
|
@ -163,7 +163,7 @@ class _Handle extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: FaiSpace.sm),
|
||||
padding: const EdgeInsets.symmetric(vertical: ChainSpace.sm),
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
|
|
@ -193,10 +193,10 @@ class _Body extends StatelessWidget {
|
|||
final l = AppLocalizations.of(context)!;
|
||||
return ListView(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
FaiSpace.xxl,
|
||||
FaiSpace.md,
|
||||
FaiSpace.xxl,
|
||||
FaiSpace.xxl,
|
||||
ChainSpace.xxl,
|
||||
ChainSpace.md,
|
||||
ChainSpace.xxl,
|
||||
ChainSpace.xxl,
|
||||
),
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
|
|
@ -208,39 +208,39 @@ class _Body extends StatelessWidget {
|
|||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.md),
|
||||
FaiPill(
|
||||
const SizedBox(width: ChainSpace.md),
|
||||
ChainPill(
|
||||
label: 'v${detail.version}',
|
||||
tone: FaiPillTone.accent,
|
||||
tone: ChainPillTone.accent,
|
||||
monospace: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: FaiSpace.xs),
|
||||
const SizedBox(height: ChainSpace.xs),
|
||||
SelectableText(
|
||||
detail.directory,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.xl),
|
||||
const SizedBox(height: ChainSpace.xl),
|
||||
_SectionHeader(l.moduleSheetCapabilities),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Wrap(
|
||||
spacing: FaiSpace.xs,
|
||||
runSpacing: FaiSpace.xs,
|
||||
spacing: ChainSpace.xs,
|
||||
runSpacing: ChainSpace.xs,
|
||||
children: detail.capabilities
|
||||
.map(
|
||||
(c) => FaiPill(
|
||||
(c) => ChainPill(
|
||||
label: c,
|
||||
tone: FaiPillTone.accent,
|
||||
tone: ChainPillTone.accent,
|
||||
monospace: true,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.xl),
|
||||
const SizedBox(height: ChainSpace.xl),
|
||||
Row(
|
||||
children: [
|
||||
_SectionHeader(l.moduleSheetPermissions),
|
||||
|
|
@ -259,7 +259,7 @@ class _Body extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
if (detail.permissions.isEmpty)
|
||||
Text(
|
||||
l.moduleSheetNoPermissions,
|
||||
|
|
@ -282,10 +282,10 @@ class _Body extends StatelessWidget {
|
|||
size: 14,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
SelectableText(
|
||||
p,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 12,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
|
|
@ -296,7 +296,7 @@ class _Body extends StatelessWidget {
|
|||
)
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.xxl),
|
||||
const SizedBox(height: ChainSpace.xxl),
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
|
|
@ -378,7 +378,7 @@ class _UninstallVersionPickerDialogState
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(l.uninstallVersionPickerBody(widget.moduleName)),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
RadioGroup<String>(
|
||||
groupValue: _picked,
|
||||
onChanged: (val) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiPill — small inline label. Used for capabilities, versions,
|
||||
// ChainPill — small inline label. Used for capabilities, versions,
|
||||
// permission scopes, status text. Replaces ad-hoc Container chips.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -6,18 +6,18 @@ import 'package:flutter/material.dart';
|
|||
import '../theme/theme.dart';
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
enum FaiPillTone { neutral, accent, success, warning, danger }
|
||||
enum ChainPillTone { neutral, accent, success, warning, danger }
|
||||
|
||||
class FaiPill extends StatelessWidget {
|
||||
class ChainPill extends StatelessWidget {
|
||||
final String label;
|
||||
final FaiPillTone tone;
|
||||
final ChainPillTone tone;
|
||||
final IconData? icon;
|
||||
final bool monospace;
|
||||
|
||||
const FaiPill({
|
||||
const ChainPill({
|
||||
super.key,
|
||||
required this.label,
|
||||
this.tone = FaiPillTone.neutral,
|
||||
this.tone = ChainPillTone.neutral,
|
||||
this.icon,
|
||||
this.monospace = false,
|
||||
});
|
||||
|
|
@ -27,13 +27,13 @@ class FaiPill extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
final (bg, fg) = _colors(theme.colorScheme);
|
||||
final textStyle = monospace
|
||||
? FaiTheme.mono(size: 11, weight: FontWeight.w500, color: fg)
|
||||
? ChainTheme.mono(size: 11, weight: FontWeight.w500, color: fg)
|
||||
: theme.textTheme.labelSmall?.copyWith(color: fg);
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: FaiSpace.sm, vertical: 3),
|
||||
padding: const EdgeInsets.symmetric(horizontal: ChainSpace.sm, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: bg,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
@ -50,25 +50,25 @@ class FaiPill extends StatelessWidget {
|
|||
|
||||
(Color, Color) _colors(ColorScheme scheme) {
|
||||
switch (tone) {
|
||||
case FaiPillTone.neutral:
|
||||
case ChainPillTone.neutral:
|
||||
return (scheme.surfaceContainerHighest, scheme.onSurfaceVariant);
|
||||
case FaiPillTone.accent:
|
||||
case ChainPillTone.accent:
|
||||
return (scheme.primary.withValues(alpha: 0.15), scheme.primary);
|
||||
case FaiPillTone.success:
|
||||
case ChainPillTone.success:
|
||||
return (
|
||||
FaiColors.success.withValues(alpha: 0.15),
|
||||
ChainColors.success.withValues(alpha: 0.15),
|
||||
scheme.brightness == Brightness.dark
|
||||
? FaiColors.success
|
||||
? ChainColors.success
|
||||
: const Color(0xFF15803D),
|
||||
);
|
||||
case FaiPillTone.warning:
|
||||
case ChainPillTone.warning:
|
||||
return (
|
||||
FaiColors.warning.withValues(alpha: 0.15),
|
||||
ChainColors.warning.withValues(alpha: 0.15),
|
||||
scheme.brightness == Brightness.dark
|
||||
? FaiColors.warning
|
||||
? ChainColors.warning
|
||||
: const Color(0xFFB45309),
|
||||
);
|
||||
case FaiPillTone.danger:
|
||||
case ChainPillTone.danger:
|
||||
return (scheme.errorContainer, scheme.onErrorContainer);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiSearchPalette — global Cmd+K command bar.
|
||||
// ChainSearchPalette — global Cmd+K command bar.
|
||||
//
|
||||
// Indexes: nav destinations, installed modules, store entries,
|
||||
// saved flows. Filters client-side as the operator types.
|
||||
|
|
@ -12,12 +12,12 @@ import 'package:flutter/services.dart';
|
|||
|
||||
import '../data/hub.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import 'fai_module_sheet.dart';
|
||||
import 'chain_module_sheet.dart';
|
||||
|
||||
/// One row in the palette. Carries everything needed to render
|
||||
/// + dispatch — keep it serializable-shaped so we can persist
|
||||
/// recent picks later.
|
||||
class FaiSearchHit {
|
||||
class ChainSearchHit {
|
||||
final String label;
|
||||
final String hint;
|
||||
final IconData icon;
|
||||
|
|
@ -33,7 +33,7 @@ class FaiSearchHit {
|
|||
/// Action to run when this hit is selected.
|
||||
final VoidCallback onSelect;
|
||||
|
||||
FaiSearchHit({
|
||||
ChainSearchHit({
|
||||
required this.label,
|
||||
required this.hint,
|
||||
required this.icon,
|
||||
|
|
@ -42,33 +42,33 @@ class FaiSearchHit {
|
|||
}) : haystack = '$label $hint $group'.toLowerCase();
|
||||
}
|
||||
|
||||
class FaiSearchPalette extends StatefulWidget {
|
||||
class ChainSearchPalette extends StatefulWidget {
|
||||
/// Pages the operator can navigate to. Each entry maps to a
|
||||
/// callback that selects the right tab back in the shell.
|
||||
final List<FaiSearchHit> staticHits;
|
||||
final List<ChainSearchHit> staticHits;
|
||||
|
||||
const FaiSearchPalette({super.key, required this.staticHits});
|
||||
const ChainSearchPalette({super.key, required this.staticHits});
|
||||
|
||||
/// Convenience launcher — used from the Cmd+K shortcut.
|
||||
static Future<void> show(
|
||||
BuildContext context, {
|
||||
required List<FaiSearchHit> staticHits,
|
||||
required List<ChainSearchHit> staticHits,
|
||||
}) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
barrierColor: Colors.black54,
|
||||
builder: (_) => FaiSearchPalette(staticHits: staticHits),
|
||||
builder: (_) => ChainSearchPalette(staticHits: staticHits),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
State<FaiSearchPalette> createState() => _FaiSearchPaletteState();
|
||||
State<ChainSearchPalette> createState() => _FaiSearchPaletteState();
|
||||
}
|
||||
|
||||
class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
||||
class _FaiSearchPaletteState extends State<ChainSearchPalette> {
|
||||
final _query = TextEditingController();
|
||||
final _focus = FocusNode();
|
||||
Future<List<FaiSearchHit>>? _dynamicFuture;
|
||||
Future<List<ChainSearchHit>>? _dynamicFuture;
|
||||
int _highlight = 0;
|
||||
|
||||
@override
|
||||
|
|
@ -92,7 +92,7 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
/// Pull modules / store / flows in parallel so the palette
|
||||
/// is searchable as soon as the operator finishes typing the
|
||||
/// first character.
|
||||
Future<List<FaiSearchHit>> _loadDynamic() async {
|
||||
Future<List<ChainSearchHit>> _loadDynamic() async {
|
||||
final svc = HubService.instance;
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final results = await Future.wait([
|
||||
|
|
@ -104,17 +104,17 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
final store = results[1] as List<StoreItem>;
|
||||
final flows = results[2] as List<SavedFlow>;
|
||||
|
||||
final hits = <FaiSearchHit>[];
|
||||
final hits = <ChainSearchHit>[];
|
||||
for (final m in modules) {
|
||||
hits.add(
|
||||
FaiSearchHit(
|
||||
ChainSearchHit(
|
||||
label: m.name,
|
||||
hint: l.searchInstalledModuleHint(m.version),
|
||||
icon: Icons.extension,
|
||||
group: l.searchGroupModules,
|
||||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
FaiModuleSheet.show(context, m.name);
|
||||
ChainModuleSheet.show(context, m.name);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
@ -123,7 +123,7 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
final pagesGroup = l.searchGroupPages;
|
||||
for (final s in store) {
|
||||
hits.add(
|
||||
FaiSearchHit(
|
||||
ChainSearchHit(
|
||||
label: s.name,
|
||||
hint: s.taglineEn.isEmpty
|
||||
? l.searchStoreHintWithCategory(
|
||||
|
|
@ -151,7 +151,7 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
final flowsGroup = l.searchGroupFlows;
|
||||
for (final f in flows) {
|
||||
hits.add(
|
||||
FaiSearchHit(
|
||||
ChainSearchHit(
|
||||
label: f.name,
|
||||
hint: l.searchSavedFlowHint,
|
||||
icon: Icons.account_tree_outlined,
|
||||
|
|
@ -174,11 +174,11 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
/// Filtered + grouped result list. Empty query returns the
|
||||
/// pages first then everything else, capped to 50 rows so the
|
||||
/// dialog stays scrollable.
|
||||
List<FaiSearchHit> _resultsFor(List<FaiSearchHit> dynamic_, String raw) {
|
||||
List<ChainSearchHit> _resultsFor(List<ChainSearchHit> dynamic_, String raw) {
|
||||
final query = raw.trim().toLowerCase();
|
||||
final all = [...widget.staticHits, ...dynamic_];
|
||||
if (query.isEmpty) return all.take(50).toList();
|
||||
final scored = <(int, FaiSearchHit)>[];
|
||||
final scored = <(int, ChainSearchHit)>[];
|
||||
for (final h in all) {
|
||||
final i = h.haystack.indexOf(query);
|
||||
if (i < 0) continue;
|
||||
|
|
@ -224,10 +224,10 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 640, maxHeight: 540),
|
||||
child: FutureBuilder<List<FaiSearchHit>>(
|
||||
child: FutureBuilder<List<ChainSearchHit>>(
|
||||
future: _dynamicFuture,
|
||||
builder: (context, snap) {
|
||||
final dyn = snap.data ?? const <FaiSearchHit>[];
|
||||
final dyn = snap.data ?? const <ChainSearchHit>[];
|
||||
return ValueListenableBuilder<TextEditingValue>(
|
||||
valueListenable: _query,
|
||||
builder: (_, value, _) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiSettingsDialog — modal for changing the hub endpoint.
|
||||
// ChainSettingsDialog — modal for changing the hub endpoint.
|
||||
// Reads current values from HubService, persists on save via
|
||||
// HubService.reconnect.
|
||||
|
||||
|
|
@ -14,25 +14,25 @@ import '../l10n/app_localizations.dart';
|
|||
import '../pages/welcome.dart' show showFaiDoc;
|
||||
import '../theme/theme.dart';
|
||||
import '../theme/tokens.dart';
|
||||
import 'fai_error_box.dart';
|
||||
import 'fai_pill.dart';
|
||||
import 'fai_system_ai_editor.dart';
|
||||
import 'chain_error_box.dart';
|
||||
import 'chain_pill.dart';
|
||||
import 'chain_system_ai_editor.dart';
|
||||
import 'theme_picker_grid.dart';
|
||||
|
||||
class FaiSettingsDialog extends StatefulWidget {
|
||||
const FaiSettingsDialog({super.key});
|
||||
class ChainSettingsDialog extends StatefulWidget {
|
||||
const ChainSettingsDialog({super.key});
|
||||
|
||||
/// Convenience launcher used from the sidebar gear icon.
|
||||
static Future<bool> show(BuildContext context) async {
|
||||
final ok = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (_) => const FaiSettingsDialog(),
|
||||
builder: (_) => const ChainSettingsDialog(),
|
||||
);
|
||||
return ok ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
State<FaiSettingsDialog> createState() => _FaiSettingsDialogState();
|
||||
State<ChainSettingsDialog> createState() => _FaiSettingsDialogState();
|
||||
}
|
||||
|
||||
/// Sidebar categories — every Settings panel belongs to exactly
|
||||
|
|
@ -40,7 +40,7 @@ class FaiSettingsDialog extends StatefulWidget {
|
|||
/// first-opened category.
|
||||
enum _Category { general, appearance, ai, integrations, security, maintenance }
|
||||
|
||||
class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
||||
class _FaiSettingsDialogState extends State<ChainSettingsDialog> {
|
||||
late final TextEditingController _host;
|
||||
late final TextEditingController _port;
|
||||
bool _secure = false;
|
||||
|
|
@ -291,7 +291,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
_saving = true;
|
||||
_channelToast = null;
|
||||
});
|
||||
final r = await SystemActions.faiChannelSwitch(name);
|
||||
final r = await SystemActions.chainChannelSwitch(name);
|
||||
if (!mounted) return;
|
||||
final l = AppLocalizations.of(context)!;
|
||||
setState(() {
|
||||
|
|
@ -384,10 +384,10 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
};
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
FaiSpace.xl,
|
||||
FaiSpace.xl,
|
||||
FaiSpace.xl,
|
||||
FaiSpace.lg,
|
||||
ChainSpace.xl,
|
||||
ChainSpace.xl,
|
||||
ChainSpace.xl,
|
||||
ChainSpace.lg,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -403,7 +403,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
String? docSlug,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: FaiSpace.lg),
|
||||
padding: const EdgeInsets.only(bottom: ChainSpace.lg),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
@ -455,7 +455,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
),
|
||||
autofocus: true,
|
||||
),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
|
|
@ -470,7 +470,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
keyboardType: TextInputType.number,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.md),
|
||||
const SizedBox(width: ChainSpace.md),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: SwitchListTile(
|
||||
|
|
@ -490,7 +490,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
],
|
||||
),
|
||||
if (_error != null) ...[
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
Text(
|
||||
_error!,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
|
|
@ -498,12 +498,12 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
Container(
|
||||
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),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
@ -512,10 +512,10 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
size: 14,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
Text(
|
||||
_previewUrl(),
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
|
|
@ -524,7 +524,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
),
|
||||
),
|
||||
if (_channels != null) ...[
|
||||
const SizedBox(height: FaiSpace.xl),
|
||||
const SizedBox(height: ChainSpace.xl),
|
||||
Text(
|
||||
l.channelsHeader,
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
|
|
@ -540,7 +540,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
for (final ch in _channels!.channels)
|
||||
_ChannelRow(
|
||||
channel: ch,
|
||||
|
|
@ -551,21 +551,21 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
? null
|
||||
: () => _runDaemon(
|
||||
l.daemonActionEnableAutostart,
|
||||
() => SystemActions.faiDaemonEnable(ch.name),
|
||||
() => SystemActions.chainDaemonEnable(ch.name),
|
||||
),
|
||||
onDisableAutostart: _saving
|
||||
? null
|
||||
: () => _runDaemon(
|
||||
l.daemonActionDisableAutostart,
|
||||
() => SystemActions.faiDaemonDisable(ch.name),
|
||||
() => SystemActions.chainDaemonDisable(ch.name),
|
||||
),
|
||||
),
|
||||
if (_channelToast != null) ...[
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
FaiErrorBox(text: _channelToast!, maxHeight: 200),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
ChainErrorBox(text: _channelToast!, maxHeight: 200),
|
||||
],
|
||||
],
|
||||
const SizedBox(height: FaiSpace.xl),
|
||||
const SizedBox(height: ChainSpace.xl),
|
||||
const _DefaultScopePanel(),
|
||||
];
|
||||
}
|
||||
|
|
@ -594,7 +594,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
_SystemAiPanel(
|
||||
status: _aiStatus!,
|
||||
onEdit: () async {
|
||||
final updated = await FaiSystemAiEditor.show(context, _aiStatus!);
|
||||
final updated = await ChainSystemAiEditor.show(context, _aiStatus!);
|
||||
if (updated != null && mounted) {
|
||||
setState(() => _aiStatus = updated);
|
||||
}
|
||||
|
|
@ -612,7 +612,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
),
|
||||
if (_mcpClients == null)
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: FaiSpace.md),
|
||||
padding: EdgeInsets.symmetric(vertical: ChainSpace.md),
|
||||
child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
|
||||
)
|
||||
else
|
||||
|
|
@ -622,7 +622,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
onRemove: _removeMcpClient,
|
||||
onRefresh: _refreshMcpClients,
|
||||
),
|
||||
const SizedBox(height: FaiSpace.xl),
|
||||
const SizedBox(height: ChainSpace.xl),
|
||||
if (_n8nEndpoints != null)
|
||||
_N8nEndpointsPanel(
|
||||
endpoints: _n8nEndpoints!,
|
||||
|
|
@ -646,7 +646,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
onSave: _saveRegistryToken,
|
||||
onClear: _clearRegistryToken,
|
||||
),
|
||||
const SizedBox(height: FaiSpace.xl),
|
||||
const SizedBox(height: ChainSpace.xl),
|
||||
_HubAuthTokenPanel(
|
||||
configuredChars: _hubAuthTokenChars,
|
||||
onSave: _saveHubAuthToken,
|
||||
|
|
@ -686,7 +686,7 @@ class _FaiSettingsDialogState extends State<FaiSettingsDialog> {
|
|||
return AlertDialog(
|
||||
title: Text(l.settingsTitle),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(FaiRadius.md),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.md),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: ConstrainedBox(
|
||||
|
|
@ -770,15 +770,15 @@ class _ChannelRow extends StatelessWidget {
|
|||
channel.running ? Icons.circle : Icons.circle_outlined,
|
||||
size: 10,
|
||||
color: channel.running
|
||||
? FaiColors.success
|
||||
? ChainColors.success
|
||||
: theme.colorScheme.outline,
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
SizedBox(
|
||||
width: 88,
|
||||
child: Text(
|
||||
channel.name,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 11,
|
||||
weight: active ? FontWeight.w600 : FontWeight.w400,
|
||||
color: theme.colorScheme.onSurface,
|
||||
|
|
@ -789,7 +789,7 @@ class _ChannelRow extends StatelessWidget {
|
|||
width: 64,
|
||||
child: Text(
|
||||
':${channel.port}',
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
@ -805,10 +805,10 @@ class _ChannelRow extends StatelessWidget {
|
|||
),
|
||||
if (active)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: FaiSpace.sm),
|
||||
child: FaiPill(
|
||||
padding: const EdgeInsets.only(right: ChainSpace.sm),
|
||||
child: ChainPill(
|
||||
label: l.channelsActive,
|
||||
tone: FaiPillTone.success,
|
||||
tone: ChainPillTone.success,
|
||||
),
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
|
|
@ -876,7 +876,7 @@ class _MenuRow extends StatelessWidget {
|
|||
return Row(
|
||||
children: [
|
||||
Icon(icon, size: 14),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
Text(text),
|
||||
],
|
||||
);
|
||||
|
|
@ -906,18 +906,18 @@ class _SystemAiPanel extends StatelessWidget {
|
|||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
FaiPill(
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
ChainPill(
|
||||
label: status.enabled ? l.systemAiEnabled : l.systemAiOff,
|
||||
tone: status.enabled ? FaiPillTone.success : FaiPillTone.neutral,
|
||||
tone: status.enabled ? ChainPillTone.success : ChainPillTone.neutral,
|
||||
),
|
||||
if (status.enabled) ...[
|
||||
const SizedBox(width: FaiSpace.xs),
|
||||
FaiPill(
|
||||
const SizedBox(width: ChainSpace.xs),
|
||||
ChainPill(
|
||||
label: status.privacyMode,
|
||||
tone: status.privacyMode == 'full'
|
||||
? FaiPillTone.warning
|
||||
: FaiPillTone.neutral,
|
||||
? ChainPillTone.warning
|
||||
: ChainPillTone.neutral,
|
||||
),
|
||||
],
|
||||
const Spacer(),
|
||||
|
|
@ -985,7 +985,7 @@ class _StatRow extends StatelessWidget {
|
|||
child: Text(
|
||||
value.isEmpty ? '—' : value,
|
||||
style: mono
|
||||
? FaiTheme.mono(size: 11, color: theme.colorScheme.onSurface)
|
||||
? ChainTheme.mono(size: 11, color: theme.colorScheme.onSurface)
|
||||
: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
|
|
@ -1045,10 +1045,10 @@ class _McpClientsPanel extends StatelessWidget {
|
|||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
FaiPill(
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
ChainPill(
|
||||
label: l.mcpServersCount(clients.length),
|
||||
tone: FaiPillTone.neutral,
|
||||
tone: ChainPillTone.neutral,
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
|
|
@ -1075,10 +1075,10 @@ class _McpClientsPanel extends StatelessWidget {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
if (clients.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: FaiSpace.sm),
|
||||
padding: const EdgeInsets.symmetric(vertical: ChainSpace.sm),
|
||||
child: Text(
|
||||
l.mcpEmpty,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
|
|
@ -1105,7 +1105,7 @@ class _McpClientRow extends StatelessWidget {
|
|||
final l = AppLocalizations.of(context)!;
|
||||
final ok = client.healthy;
|
||||
final dotColor = client.errorKind.isEmpty
|
||||
? (ok ? FaiColors.success : FaiColors.muted)
|
||||
? (ok ? ChainColors.success : ChainColors.muted)
|
||||
: theme.colorScheme.error;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
|
|
@ -1116,7 +1116,7 @@ class _McpClientRow extends StatelessWidget {
|
|||
padding: const EdgeInsets.only(top: 6),
|
||||
child: Icon(Icons.circle, size: 8, color: dotColor),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -1125,28 +1125,28 @@ class _McpClientRow extends StatelessWidget {
|
|||
children: [
|
||||
Text(
|
||||
client.name,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 12,
|
||||
weight: FontWeight.w600,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
if (client.toolCount > 0)
|
||||
FaiPill(
|
||||
ChainPill(
|
||||
label: l.mcpToolsCount(client.toolCount),
|
||||
tone: FaiPillTone.success,
|
||||
tone: ChainPillTone.success,
|
||||
)
|
||||
else if (client.errorKind.isNotEmpty)
|
||||
FaiPill(
|
||||
ChainPill(
|
||||
label: client.errorKind,
|
||||
tone: FaiPillTone.danger,
|
||||
tone: ChainPillTone.danger,
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
client.endpoint,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 10,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
@ -1234,7 +1234,7 @@ class _AddMcpClientDialogState extends State<_AddMcpClientDialog> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
// Curated suggestions from the official Anthropic
|
||||
// MCP servers + a few community staples. Click a
|
||||
// chip to pre-fill the form. The operator still
|
||||
|
|
@ -1249,8 +1249,8 @@ class _AddMcpClientDialogState extends State<_AddMcpClientDialog> {
|
|||
),
|
||||
const SizedBox(height: 6),
|
||||
Wrap(
|
||||
spacing: FaiSpace.xs,
|
||||
runSpacing: FaiSpace.xs,
|
||||
spacing: ChainSpace.xs,
|
||||
runSpacing: ChainSpace.xs,
|
||||
children: [
|
||||
for (final s in _kMcpSuggestions)
|
||||
ActionChip(
|
||||
|
|
@ -1260,7 +1260,7 @@ class _AddMcpClientDialogState extends State<_AddMcpClientDialog> {
|
|||
),
|
||||
],
|
||||
),
|
||||
const Divider(height: FaiSpace.xl),
|
||||
const Divider(height: ChainSpace.xl),
|
||||
TextField(
|
||||
controller: _name,
|
||||
autofocus: true,
|
||||
|
|
@ -1271,7 +1271,7 @@ class _AddMcpClientDialogState extends State<_AddMcpClientDialog> {
|
|||
isDense: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
TextField(
|
||||
controller: _endpoint,
|
||||
decoration: InputDecoration(
|
||||
|
|
@ -1281,7 +1281,7 @@ class _AddMcpClientDialogState extends State<_AddMcpClientDialog> {
|
|||
isDense: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
TextField(
|
||||
controller: _apiKey,
|
||||
decoration: InputDecoration(
|
||||
|
|
@ -1292,7 +1292,7 @@ class _AddMcpClientDialogState extends State<_AddMcpClientDialog> {
|
|||
isDense: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
TextField(
|
||||
controller: _desc,
|
||||
decoration: InputDecoration(
|
||||
|
|
@ -1377,10 +1377,10 @@ class _N8nEndpointsPanel extends StatelessWidget {
|
|||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
FaiPill(
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
ChainPill(
|
||||
label: l.n8nEndpointsCount(endpoints.length),
|
||||
tone: FaiPillTone.neutral,
|
||||
tone: ChainPillTone.neutral,
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
|
|
@ -1407,10 +1407,10 @@ class _N8nEndpointsPanel extends StatelessWidget {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
if (endpoints.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: FaiSpace.sm),
|
||||
padding: const EdgeInsets.symmetric(vertical: ChainSpace.sm),
|
||||
child: Text(
|
||||
l.n8nEmpty,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
|
|
@ -1437,7 +1437,7 @@ class _N8nEndpointRow extends StatelessWidget {
|
|||
final l = AppLocalizations.of(context)!;
|
||||
final ok = endpoint.healthy;
|
||||
final dotColor = endpoint.errorKind.isEmpty
|
||||
? (ok ? FaiColors.success : FaiColors.muted)
|
||||
? (ok ? ChainColors.success : ChainColors.muted)
|
||||
: theme.colorScheme.error;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
|
|
@ -1448,7 +1448,7 @@ class _N8nEndpointRow extends StatelessWidget {
|
|||
padding: const EdgeInsets.only(top: 6),
|
||||
child: Icon(Icons.circle, size: 8, color: dotColor),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -1457,28 +1457,28 @@ class _N8nEndpointRow extends StatelessWidget {
|
|||
children: [
|
||||
Text(
|
||||
endpoint.name,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 12,
|
||||
weight: FontWeight.w600,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
if (endpoint.workflowCount > 0)
|
||||
FaiPill(
|
||||
ChainPill(
|
||||
label: l.n8nWorkflowsCount(endpoint.workflowCount),
|
||||
tone: FaiPillTone.success,
|
||||
tone: ChainPillTone.success,
|
||||
)
|
||||
else if (endpoint.errorKind.isNotEmpty)
|
||||
FaiPill(
|
||||
ChainPill(
|
||||
label: endpoint.errorKind,
|
||||
tone: FaiPillTone.danger,
|
||||
tone: ChainPillTone.danger,
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
endpoint.baseUrl,
|
||||
style: FaiTheme.mono(
|
||||
style: ChainTheme.mono(
|
||||
size: 10,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
@ -1554,7 +1554,7 @@ class _AddN8nEndpointDialogState extends State<_AddN8nEndpointDialog> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
TextField(
|
||||
controller: _name,
|
||||
autofocus: true,
|
||||
|
|
@ -1565,7 +1565,7 @@ class _AddN8nEndpointDialogState extends State<_AddN8nEndpointDialog> {
|
|||
isDense: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
TextField(
|
||||
controller: _baseUrl,
|
||||
decoration: InputDecoration(
|
||||
|
|
@ -1575,7 +1575,7 @@ class _AddN8nEndpointDialogState extends State<_AddN8nEndpointDialog> {
|
|||
isDense: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
TextField(
|
||||
controller: _apiKey,
|
||||
decoration: InputDecoration(
|
||||
|
|
@ -1586,7 +1586,7 @@ class _AddN8nEndpointDialogState extends State<_AddN8nEndpointDialog> {
|
|||
isDense: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
TextField(
|
||||
controller: _desc,
|
||||
decoration: InputDecoration(
|
||||
|
|
@ -1718,7 +1718,7 @@ class _MaintenancePanelState extends State<_MaintenancePanel> {
|
|||
_resetting = true;
|
||||
_resultText = null;
|
||||
});
|
||||
final r = await SystemActions.faiReset(
|
||||
final r = await SystemActions.chainReset(
|
||||
keepModules: _keepModules,
|
||||
keepData: _keepData,
|
||||
);
|
||||
|
|
@ -1755,7 +1755,7 @@ class _MaintenancePanelState extends State<_MaintenancePanel> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
dense: true,
|
||||
|
|
@ -1788,7 +1788,7 @@ class _MaintenancePanelState extends State<_MaintenancePanel> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Row(
|
||||
children: [
|
||||
FilledButton.icon(
|
||||
|
|
@ -1813,8 +1813,8 @@ class _MaintenancePanelState extends State<_MaintenancePanel> {
|
|||
],
|
||||
),
|
||||
if (_resultText != null) ...[
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
FaiErrorBox(text: _resultText!, isError: !_resultOk, maxHeight: 240),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
ChainErrorBox(text: _resultText!, isError: !_resultOk, maxHeight: 240),
|
||||
],
|
||||
],
|
||||
);
|
||||
|
|
@ -1891,7 +1891,7 @@ class _RegistryCredentialsPanelState extends State<_RegistryCredentialsPanel> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
|
|
@ -1901,7 +1901,7 @@ class _RegistryCredentialsPanelState extends State<_RegistryCredentialsPanel> {
|
|||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: FaiSpace.xs),
|
||||
const SizedBox(width: ChainSpace.xs),
|
||||
Text(
|
||||
isConfigured
|
||||
? l.registryTokenStatusConfigured(widget.configuredChars!)
|
||||
|
|
@ -1925,7 +1925,7 @@ class _RegistryCredentialsPanelState extends State<_RegistryCredentialsPanel> {
|
|||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
|
|
@ -1947,7 +1947,7 @@ class _RegistryCredentialsPanelState extends State<_RegistryCredentialsPanel> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(Icons.save_outlined, size: 14),
|
||||
label: Text(l.registryTokenSaveButton),
|
||||
|
|
@ -2040,7 +2040,7 @@ class _HubAuthTokenPanelState extends State<_HubAuthTokenPanel> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
|
|
@ -2050,7 +2050,7 @@ class _HubAuthTokenPanelState extends State<_HubAuthTokenPanel> {
|
|||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: FaiSpace.xs),
|
||||
const SizedBox(width: ChainSpace.xs),
|
||||
Text(
|
||||
isConfigured
|
||||
? l.hubAuthTokenStatusConfigured(widget.configuredChars!)
|
||||
|
|
@ -2074,7 +2074,7 @@ class _HubAuthTokenPanelState extends State<_HubAuthTokenPanel> {
|
|||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
|
|
@ -2096,7 +2096,7 @@ class _HubAuthTokenPanelState extends State<_HubAuthTokenPanel> {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(Icons.save_outlined, size: 14),
|
||||
label: Text(l.hubAuthTokenSaveButton),
|
||||
|
|
@ -2410,10 +2410,10 @@ class _DefaultScopePanelState extends State<_DefaultScopePanel> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
if (_loading)
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: FaiSpace.sm),
|
||||
padding: EdgeInsets.symmetric(vertical: ChainSpace.sm),
|
||||
child: SizedBox(
|
||||
height: 16,
|
||||
width: 16,
|
||||
|
|
@ -2438,7 +2438,7 @@ class _DefaultScopePanelState extends State<_DefaultScopePanel> {
|
|||
onDown: () => _moveDown(i),
|
||||
onRemove: () => _removeAt(i),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
|
|
@ -2453,7 +2453,7 @@ class _DefaultScopePanelState extends State<_DefaultScopePanel> {
|
|||
onSubmitted: _saving ? null : _addEntry,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(Icons.add, size: 14),
|
||||
label: Text(l.defaultScopeAddButton),
|
||||
|
|
@ -2464,7 +2464,7 @@ class _DefaultScopePanelState extends State<_DefaultScopePanel> {
|
|||
],
|
||||
),
|
||||
if (unusedSuggestions.isNotEmpty) ...[
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Text(
|
||||
l.defaultScopeSuggestions,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
|
|
@ -2474,7 +2474,7 @@ class _DefaultScopePanelState extends State<_DefaultScopePanel> {
|
|||
),
|
||||
const SizedBox(height: 4),
|
||||
Wrap(
|
||||
spacing: FaiSpace.xs,
|
||||
spacing: ChainSpace.xs,
|
||||
runSpacing: 4,
|
||||
children: [
|
||||
for (final s in unusedSuggestions)
|
||||
|
|
@ -2489,7 +2489,7 @@ class _DefaultScopePanelState extends State<_DefaultScopePanel> {
|
|||
),
|
||||
],
|
||||
if (_error != null) ...[
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Text(
|
||||
_error!,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
|
|
@ -2603,7 +2603,7 @@ class _Sidebar extends StatelessWidget {
|
|||
child: Container(
|
||||
color: theme.colorScheme.surfaceContainer,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(vertical: FaiSpace.lg),
|
||||
padding: const EdgeInsets.symmetric(vertical: ChainSpace.lg),
|
||||
children: [
|
||||
for (final (cat, icon, label) in items)
|
||||
_SidebarRow(
|
||||
|
|
@ -2639,24 +2639,24 @@ class _SidebarRow extends StatelessWidget {
|
|||
? theme.colorScheme.onSurface
|
||||
: theme.colorScheme.onSurfaceVariant;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: FaiSpace.sm, vertical: 2),
|
||||
padding: const EdgeInsets.symmetric(horizontal: ChainSpace.sm, vertical: 2),
|
||||
child: Material(
|
||||
color: selected
|
||||
? theme.colorScheme.primary.withValues(alpha: 0.14)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: FaiSpace.md,
|
||||
horizontal: ChainSpace.md,
|
||||
vertical: 8,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, size: 16, color: fg),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
Text(
|
||||
label,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
// FaiStatusDot — small pulsing dot used as a "live" indicator.
|
||||
// ChainStatusDot — small pulsing dot used as a "live" indicator.
|
||||
// Goes still when [pulsing] is false, breathes gently when true.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
class FaiStatusDot extends StatefulWidget {
|
||||
class ChainStatusDot extends StatefulWidget {
|
||||
final Color color;
|
||||
|
||||
/// When true, the dot fades between full and 30% opacity.
|
||||
|
|
@ -14,7 +14,7 @@ class FaiStatusDot extends StatefulWidget {
|
|||
|
||||
final double size;
|
||||
|
||||
const FaiStatusDot({
|
||||
const ChainStatusDot({
|
||||
super.key,
|
||||
required this.color,
|
||||
this.pulsing = false,
|
||||
|
|
@ -22,10 +22,10 @@ class FaiStatusDot extends StatefulWidget {
|
|||
});
|
||||
|
||||
@override
|
||||
State<FaiStatusDot> createState() => _FaiStatusDotState();
|
||||
State<ChainStatusDot> createState() => _FaiStatusDotState();
|
||||
}
|
||||
|
||||
class _FaiStatusDotState extends State<FaiStatusDot>
|
||||
class _FaiStatusDotState extends State<ChainStatusDot>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _ctrl;
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ class _FaiStatusDotState extends State<FaiStatusDot>
|
|||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant FaiStatusDot old) {
|
||||
void didUpdateWidget(covariant ChainStatusDot old) {
|
||||
super.didUpdateWidget(old);
|
||||
if (widget.pulsing && !_ctrl.isAnimating) {
|
||||
_ctrl.repeat(reverse: true);
|
||||
|
|
@ -86,10 +86,10 @@ class _FaiStatusDotState extends State<FaiStatusDot>
|
|||
}
|
||||
|
||||
/// Convenience presets.
|
||||
class FaiStatusDots {
|
||||
FaiStatusDots._();
|
||||
class ChainStatusDots {
|
||||
ChainStatusDots._();
|
||||
static Widget live() =>
|
||||
const FaiStatusDot(color: FaiColors.success, pulsing: true);
|
||||
static Widget idle() => const FaiStatusDot(color: FaiColors.muted);
|
||||
static Widget down() => const FaiStatusDot(color: FaiColors.danger);
|
||||
const ChainStatusDot(color: ChainColors.success, pulsing: true);
|
||||
static Widget idle() => const ChainStatusDot(color: ChainColors.muted);
|
||||
static Widget down() => const ChainStatusDot(color: ChainColors.danger);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// FaiSystemAiEditor — modal for configuring the hub-internal
|
||||
// ChainSystemAiEditor — modal for configuring the hub-internal
|
||||
// System AI from inside Studio. Mirrors the YAML block but with
|
||||
// provider presets, in-place explainers, and a "Test connection"
|
||||
// button. On save, calls HubAdmin.UpdateSystemAi which both
|
||||
|
|
@ -11,10 +11,10 @@ import '../data/hub.dart';
|
|||
import '../l10n/app_localizations.dart';
|
||||
import '../theme/theme.dart';
|
||||
import '../theme/tokens.dart';
|
||||
import 'fai_pill.dart';
|
||||
import 'chain_pill.dart';
|
||||
|
||||
/// Provider preset metadata kept in sync with
|
||||
/// `fai_hub::operator_config::SystemLlmProvider`. Anything that
|
||||
/// `chain_hub::operator_config::SystemLlmProvider`. Anything that
|
||||
/// is operator-visible (label, description, default endpoint,
|
||||
/// suggested env-var) lives here so the dropdown self-explains
|
||||
/// without needing to read the source.
|
||||
|
|
@ -108,10 +108,10 @@ class _ProviderPreset {
|
|||
}
|
||||
}
|
||||
|
||||
class FaiSystemAiEditor extends StatefulWidget {
|
||||
class ChainSystemAiEditor extends StatefulWidget {
|
||||
final SystemAiStatus initial;
|
||||
|
||||
const FaiSystemAiEditor({super.key, required this.initial});
|
||||
const ChainSystemAiEditor({super.key, required this.initial});
|
||||
|
||||
/// Convenience launcher. Returns the new status when the
|
||||
/// operator saved, null on cancel.
|
||||
|
|
@ -121,15 +121,15 @@ class FaiSystemAiEditor extends StatefulWidget {
|
|||
) {
|
||||
return showDialog<SystemAiStatus>(
|
||||
context: context,
|
||||
builder: (_) => FaiSystemAiEditor(initial: initial),
|
||||
builder: (_) => ChainSystemAiEditor(initial: initial),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
State<FaiSystemAiEditor> createState() => _FaiSystemAiEditorState();
|
||||
State<ChainSystemAiEditor> createState() => _FaiSystemAiEditorState();
|
||||
}
|
||||
|
||||
class _FaiSystemAiEditorState extends State<FaiSystemAiEditor> {
|
||||
class _FaiSystemAiEditorState extends State<ChainSystemAiEditor> {
|
||||
late _ProviderPreset _preset;
|
||||
late final TextEditingController _endpoint;
|
||||
late final TextEditingController _model;
|
||||
|
|
@ -365,11 +365,11 @@ class _FaiSystemAiEditorState extends State<FaiSystemAiEditor> {
|
|||
return AlertDialog(
|
||||
title: Text(l.systemAiTitle),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(FaiRadius.md),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.md),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: FaiSpace.xl,
|
||||
vertical: FaiSpace.lg,
|
||||
horizontal: ChainSpace.xl,
|
||||
vertical: ChainSpace.lg,
|
||||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 520, maxHeight: 600),
|
||||
|
|
@ -385,10 +385,10 @@ class _FaiSystemAiEditorState extends State<FaiSystemAiEditor> {
|
|||
),
|
||||
),
|
||||
if (_hw != null) ...[
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
_HardwareBanner(hw: _hw!, lastReviewed: _curated?.lastReviewed),
|
||||
],
|
||||
const SizedBox(height: FaiSpace.lg),
|
||||
const SizedBox(height: ChainSpace.lg),
|
||||
_ProviderDropdown(value: _preset, onChanged: _onProviderChanged),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
|
|
@ -397,17 +397,17 @@ class _FaiSystemAiEditorState extends State<FaiSystemAiEditor> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
TextField(
|
||||
controller: _endpoint,
|
||||
style: FaiTheme.mono(size: 12),
|
||||
style: ChainTheme.mono(size: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: l.systemAiEndpointLabel,
|
||||
border: const OutlineInputBorder(),
|
||||
isDense: true,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
_ModelPicker(
|
||||
controller: _model,
|
||||
preset: _preset,
|
||||
|
|
@ -424,10 +424,10 @@ class _FaiSystemAiEditorState extends State<FaiSystemAiEditor> {
|
|||
? null
|
||||
: _pullModel,
|
||||
),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
TextField(
|
||||
controller: _apiKeyEnv,
|
||||
style: FaiTheme.mono(size: 12),
|
||||
style: ChainTheme.mono(size: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: _preset.wire == 'openai'
|
||||
? l.systemAiApiKeyRequired
|
||||
|
|
@ -445,20 +445,20 @@ class _FaiSystemAiEditorState extends State<FaiSystemAiEditor> {
|
|||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: FaiSpace.lg),
|
||||
const SizedBox(height: ChainSpace.lg),
|
||||
_PrivacyModeChips(
|
||||
value: _privacyMode,
|
||||
onChanged: (v) => setState(() => _privacyMode = v),
|
||||
),
|
||||
if (widget.initial.cacheCount > 0) ...[
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
_CacheStatusRow(
|
||||
cacheCount: widget.initial.cacheCount,
|
||||
onClear: _saving || _testing ? null : _clearCache,
|
||||
),
|
||||
],
|
||||
if (_error != null) ...[
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
Text(
|
||||
_error!,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
|
|
@ -467,7 +467,7 @@ class _FaiSystemAiEditorState extends State<FaiSystemAiEditor> {
|
|||
),
|
||||
],
|
||||
if (_testResult != null) ...[
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
_TestResultPanel(result: _testResult!),
|
||||
],
|
||||
],
|
||||
|
|
@ -573,11 +573,11 @@ class _PrivacyModeChips extends StatelessWidget {
|
|||
for (final (wire, label, desc) in modes)
|
||||
InkWell(
|
||||
onTap: () => onChanged(wire),
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: FaiSpace.sm,
|
||||
horizontal: FaiSpace.sm,
|
||||
vertical: ChainSpace.sm,
|
||||
horizontal: ChainSpace.sm,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
@ -616,13 +616,13 @@ class _TestResultPanel extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final ok = result.isSuccess;
|
||||
final color = ok ? FaiColors.success : theme.colorScheme.error;
|
||||
final color = ok ? ChainColors.success : 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(
|
||||
|
|
@ -635,16 +635,16 @@ class _TestResultPanel extends StatelessWidget {
|
|||
size: 14,
|
||||
color: color,
|
||||
),
|
||||
const SizedBox(width: FaiSpace.xs),
|
||||
const SizedBox(width: ChainSpace.xs),
|
||||
Text(
|
||||
ok ? l.systemAiConnectionOk : l.systemAiConnectionFailed,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(color: color),
|
||||
),
|
||||
const Spacer(),
|
||||
if (ok && result.latencyMs > 0)
|
||||
FaiPill(
|
||||
ChainPill(
|
||||
label: '${result.latencyMs} ms',
|
||||
tone: FaiPillTone.neutral,
|
||||
tone: ChainPillTone.neutral,
|
||||
monospace: true,
|
||||
),
|
||||
],
|
||||
|
|
@ -652,10 +652,10 @@ class _TestResultPanel extends StatelessWidget {
|
|||
const SizedBox(height: 4),
|
||||
SelectableText(
|
||||
ok ? l.systemAiReplyPrefix(result.text) : result.text,
|
||||
style: FaiTheme.mono(size: 11, color: theme.colorScheme.onSurface),
|
||||
style: ChainTheme.mono(size: 11, color: theme.colorScheme.onSurface),
|
||||
),
|
||||
if (!ok && result.fixHint(l).isNotEmpty) ...[
|
||||
const SizedBox(height: FaiSpace.xs),
|
||||
const SizedBox(height: ChainSpace.xs),
|
||||
Text(
|
||||
result.fixHint(l),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
|
|
@ -720,7 +720,7 @@ class _ModelPicker extends StatelessWidget {
|
|||
valueListenable: controller,
|
||||
builder: (_, _, _) => TextField(
|
||||
controller: controller,
|
||||
style: FaiTheme.mono(size: 12),
|
||||
style: ChainTheme.mono(size: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: l.systemAiModelLabel,
|
||||
hintText: preset.modelHint.isEmpty
|
||||
|
|
@ -738,7 +738,7 @@ class _ModelPicker extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
OutlinedButton.icon(
|
||||
onPressed: onRefresh,
|
||||
icon: loading
|
||||
|
|
@ -751,7 +751,7 @@ class _ModelPicker extends StatelessWidget {
|
|||
label: Text(l.systemAiRefresh),
|
||||
),
|
||||
if (_isOllama) ...[
|
||||
const SizedBox(width: FaiSpace.sm),
|
||||
const SizedBox(width: ChainSpace.sm),
|
||||
OutlinedButton.icon(
|
||||
onPressed: onPull,
|
||||
icon: pulling
|
||||
|
|
@ -776,7 +776,7 @@ class _ModelPicker extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
if (models != null) ...[
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
if (models!.isEmpty)
|
||||
Text(
|
||||
_isOllama ? l.systemAiNoModelsOllama : l.systemAiNoModelsGeneric,
|
||||
|
|
@ -786,8 +786,8 @@ class _ModelPicker extends StatelessWidget {
|
|||
)
|
||||
else ...[
|
||||
Wrap(
|
||||
spacing: FaiSpace.xs,
|
||||
runSpacing: FaiSpace.xs,
|
||||
spacing: ChainSpace.xs,
|
||||
runSpacing: ChainSpace.xs,
|
||||
children: [
|
||||
for (final id in _sortedBySuitability(
|
||||
models!,
|
||||
|
|
@ -850,13 +850,13 @@ extension _SuitabilityCopy on _Suitability {
|
|||
Color color(ColorScheme cs) {
|
||||
switch (this) {
|
||||
case _Suitability.recommended:
|
||||
return FaiColors.success;
|
||||
return ChainColors.success;
|
||||
case _Suitability.balanced:
|
||||
return cs.primary;
|
||||
case _Suitability.small:
|
||||
return FaiColors.warning;
|
||||
return ChainColors.warning;
|
||||
case _Suitability.large:
|
||||
return FaiColors.warning;
|
||||
return ChainColors.warning;
|
||||
case _Suitability.huge:
|
||||
return cs.error;
|
||||
case _Suitability.unknown:
|
||||
|
|
@ -1005,7 +1005,7 @@ class _ModelChip extends StatelessWidget {
|
|||
size: suitability == _Suitability.recommended ? 14 : 9,
|
||||
color: color,
|
||||
),
|
||||
label: Text(id, style: FaiTheme.mono(size: 11)),
|
||||
label: Text(id, style: ChainTheme.mono(size: 11)),
|
||||
side: BorderSide(color: color.withValues(alpha: 0.4)),
|
||||
onPressed: onTap,
|
||||
),
|
||||
|
|
@ -1030,10 +1030,10 @@ class _HardwareBanner extends StatelessWidget {
|
|||
? l.systemAiHwReviewed(lastReviewed!)
|
||||
: '';
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: FaiSpace.sm, vertical: 6),
|
||||
padding: const EdgeInsets.symmetric(horizontal: ChainSpace.sm, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
border: Border.all(color: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
child: Row(
|
||||
|
|
@ -1067,7 +1067,7 @@ class _SuitabilityLegend extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
final l = AppLocalizations.of(context)!;
|
||||
Widget dot(Color c, String label) => Padding(
|
||||
padding: const EdgeInsets.only(right: FaiSpace.md),
|
||||
padding: const EdgeInsets.only(right: ChainSpace.md),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
|
@ -1090,11 +1090,11 @@ class _SuitabilityLegend extends StatelessWidget {
|
|||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: FaiSpace.md),
|
||||
padding: const EdgeInsets.only(right: ChainSpace.md),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.star, size: 11, color: FaiColors.success),
|
||||
Icon(Icons.star, size: 11, color: ChainColors.success),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
l.systemAiLegendRecommended(tierTag),
|
||||
|
|
@ -1107,7 +1107,7 @@ class _SuitabilityLegend extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
dot(theme.colorScheme.primary, l.systemAiLegendBalanced),
|
||||
dot(FaiColors.warning, l.systemAiLegendSmallLarge),
|
||||
dot(ChainColors.warning, l.systemAiLegendSmallLarge),
|
||||
dot(theme.colorScheme.error, l.systemAiLegendHuge),
|
||||
dot(theme.colorScheme.outline, l.systemAiLegendUnknown),
|
||||
],
|
||||
|
|
@ -1131,10 +1131,10 @@ class _CacheStatusRow extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
final l = AppLocalizations.of(context)!;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: FaiSpace.sm, vertical: 6),
|
||||
padding: const EdgeInsets.symmetric(horizontal: ChainSpace.sm, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
border: Border.all(color: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
child: Row(
|
||||
|
|
@ -55,7 +55,7 @@ class _ThemePickerGridState extends State<ThemePickerGrid> {
|
|||
builder: (context, snap) {
|
||||
if (snap.connectionState != ConnectionState.done) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: FaiSpace.sm),
|
||||
padding: EdgeInsets.symmetric(vertical: ChainSpace.sm),
|
||||
child: SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
|
|
@ -85,7 +85,7 @@ class _ThemePickerGridState extends State<ThemePickerGrid> {
|
|||
_customTile(theme, l, active),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: FaiSpace.sm),
|
||||
const SizedBox(height: ChainSpace.sm),
|
||||
Text(
|
||||
l.themePluginHint,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
|
|
@ -248,13 +248,13 @@ class _Tile extends StatelessWidget {
|
|||
height: 76,
|
||||
child: Material(
|
||||
color: theme.colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
borderRadius: BorderRadius.circular(ChainRadius.sm),
|
||||
border: Border.all(
|
||||
color: selected
|
||||
? theme.colorScheme.primary
|
||||
|
|
@ -432,7 +432,7 @@ class _ColorPickerDialogState extends State<_ColorPickerDialog> {
|
|||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
TextField(
|
||||
controller: _hex,
|
||||
decoration: InputDecoration(
|
||||
|
|
@ -444,7 +444,7 @@ class _ColorPickerDialogState extends State<_ColorPickerDialog> {
|
|||
onChanged: _commitHex,
|
||||
onSubmitted: _commitHex,
|
||||
),
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
const SizedBox(height: ChainSpace.md),
|
||||
Text(l.themePluginCustomPreview, style: theme.textTheme.labelSmall),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@
|
|||
// primitives directly — no Material `Card` / generic `Container`
|
||||
// soup in page code.
|
||||
|
||||
export 'fai_binary_recovery.dart';
|
||||
export 'fai_card.dart';
|
||||
export 'fai_data_row.dart';
|
||||
export 'fai_delta_mark.dart';
|
||||
export 'fai_empty_state.dart';
|
||||
export 'fai_en_badge.dart';
|
||||
export 'fai_error_box.dart';
|
||||
export 'fai_flow_output.dart';
|
||||
export 'fai_log_viewer.dart';
|
||||
export 'fai_module_sheet.dart';
|
||||
export 'fai_pill.dart';
|
||||
export 'fai_settings_dialog.dart';
|
||||
export 'fai_status_dot.dart';
|
||||
export 'fai_system_ai_editor.dart';
|
||||
export 'chain_binary_recovery.dart';
|
||||
export 'chain_card.dart';
|
||||
export 'chain_data_row.dart';
|
||||
export 'chain_delta_mark.dart';
|
||||
export 'chain_empty_state.dart';
|
||||
export 'chain_en_badge.dart';
|
||||
export 'chain_error_box.dart';
|
||||
export 'chain_flow_output.dart';
|
||||
export 'chain_log_viewer.dart';
|
||||
export 'chain_module_sheet.dart';
|
||||
export 'chain_pill.dart';
|
||||
export 'chain_settings_dialog.dart';
|
||||
export 'chain_status_dot.dart';
|
||||
export 'chain_system_ai_editor.dart';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue