diff --git a/README.md b/README.md index c2d2355..ed9cda7 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ flutter run -d macos Will be published as `fai/studio` on Forgejo (`git.flemming.ws`). The local directory `chain_studio/` follows the established -`fai_platform/` layout convention. +`fai_chain/` layout convention. ## Why "Studio" and not "Stage" diff --git a/assets/docs/federation.md b/assets/docs/federation.md index 475cdf6..71d02b2 100644 --- a/assets/docs/federation.md +++ b/assets/docs/federation.md @@ -14,7 +14,7 @@ connected and lets you enrol new ones. 2. The hub issues a single-use **bootstrap token** and bundles its **CA certificate**. Copy the shown config into the satellite's `config.yaml` (one paste) and export the token as - `FAI_BOOTSTRAP_TOKEN`. + `CHAIN_BOOTSTRAP_TOKEN`. 3. Start the satellite. It dials the primary, the CA pin makes the first connect tamper-proof, and the primary signs it a short-lived certificate. It then appears in this list. diff --git a/assets/docs/federation_de.md b/assets/docs/federation_de.md index b3f9628..ef8b295 100644 --- a/assets/docs/federation_de.md +++ b/assets/docs/federation_de.md @@ -16,7 +16,7 @@ verbundenen Satelliten und richtet neue ein. 2. Der Hub stellt ein einmaliges **Bootstrap-Token** aus und legt sein **CA-Zertifikat** bei. Kopiere die gezeigte Konfiguration in die `config.yaml` des Satelliten (ein Einfügen) und exportiere das - Token als `FAI_BOOTSTRAP_TOKEN`. + Token als `CHAIN_BOOTSTRAP_TOKEN`. 3. Starte den Satelliten. Er wählt sich beim Primary ein, der CA-Pin macht den ersten Connect manipulationssicher, und der Primary signiert ihm ein kurzlebiges Zertifikat. Danach erscheint er in diff --git a/docs/landing-page-design.md b/docs/landing-page-design.md index 73df811..d80063f 100644 --- a/docs/landing-page-design.md +++ b/docs/landing-page-design.md @@ -102,7 +102,7 @@ Instead a `_DocReaderSheet` (modal bottom-sheet, same shape as existing `flutter_markdown` package — same one the Store uses for module READMEs. The doc source is bundled with Studio as `assets/docs/*.md`, kept in sync via a build step that copies -the relevant files from `fai_platform/docs/`. +the relevant files from `fai_chain/docs/`. Pros: - Air-gap-tauglich (no network call to read docs) @@ -158,7 +158,7 @@ phased PRs: content with hard-coded strings (with i18n). Lands without the embedded-doc reader. 2. **Phase B** — bundled markdown docs + `_DocReaderSheet`. - Pick 4-5 source docs from `fai_platform/docs/architecture/` + Pick 4-5 source docs from `fai_chain/docs/architecture/` that map to operator concerns (security, host-services, flow-experience, compliance-gaps). Add a build script that syncs them into `assets/docs/`. diff --git a/lib/data/fai_log.dart b/lib/data/chain_log.dart similarity index 98% rename from lib/data/fai_log.dart rename to lib/data/chain_log.dart index 6e87647..440f23a 100644 --- a/lib/data/fai_log.dart +++ b/lib/data/chain_log.dart @@ -26,10 +26,10 @@ import 'dart:io'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; -class FaiLog { - FaiLog._(); +class ChainLog { + ChainLog._(); - static final FaiLog instance = FaiLog._(); + static final ChainLog instance = ChainLog._(); // Serialise concurrent writes — Future-based mutex so a burst // of errors (rare but happens during failed install retries) diff --git a/lib/data/error_presentation.dart b/lib/data/error_presentation.dart index 8c815ec..27ee75a 100644 --- a/lib/data/error_presentation.dart +++ b/lib/data/error_presentation.dart @@ -16,9 +16,9 @@ import 'package:flutter/material.dart'; -import '../widgets/fai_error_box.dart'; +import '../widgets/chain_error_box.dart'; import '../theme/tokens.dart'; -import 'fai_log.dart'; +import 'chain_log.dart'; /// Show [error] as a SnackBar with a copy affordance. /// @@ -41,7 +41,7 @@ void showFaiErrorSnack( }) { // Fire-and-forget log write. If the disk's full or the dir // is read-only the UI still surfaces the error. - FaiLog.instance.error(source, error, context: title); + ChainLog.instance.error(source, error, context: title); final messenger = ScaffoldMessenger.maybeOf(context); if (messenger == null) { // No Scaffold above — the caller is likely showing the @@ -60,8 +60,8 @@ void showFaiErrorSnack( padding: EdgeInsets.zero, content: Padding( padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.sm, - vertical: FaiSpace.xs, + horizontal: ChainSpace.sm, + vertical: ChainSpace.xs, ), child: Column( mainAxisSize: MainAxisSize.min, @@ -75,9 +75,9 @@ void showFaiErrorSnack( fontWeight: FontWeight.w600, ), ), - const SizedBox(height: FaiSpace.xs), + const SizedBox(height: ChainSpace.xs), ], - FaiErrorBox(error: error, isError: true), + ChainErrorBox(error: error, isError: true), ], ), ), @@ -94,14 +94,14 @@ Future showFaiErrorDialog( Object error, { String? title, }) async { - FaiLog.instance.error(source, error); + ChainLog.instance.error(source, error); return showDialog( context: context, builder: (ctx) => AlertDialog( title: title == null ? null : Text(title), content: SizedBox( width: 480, - child: FaiErrorBox(error: error, isError: true, maxHeight: 240), + child: ChainErrorBox(error: error, isError: true, maxHeight: 240), ), actions: [ TextButton( diff --git a/lib/data/flow_run_driver.dart b/lib/data/flow_run_driver.dart index 1864f15..43e8c32 100644 --- a/lib/data/flow_run_driver.dart +++ b/lib/data/flow_run_driver.dart @@ -161,7 +161,7 @@ class StudioFlowRunDriver implements editor.FlowRunDriver { /// (text / json / bytes only). File outputs degrade to /// JSON containing the URI string — the editor's run-tab /// renderer is read-only so the operator can still see - /// where the file went; Studio's richer FaiFlowOutput + /// where the file went; Studio's richer ChainFlowOutput /// widget keeps file-open affordances available elsewhere. editor.FlowOutputValue _convertOutput(FlowOutput out) { return switch (out) { diff --git a/lib/data/hub.dart b/lib/data/hub.dart index da95297..559dcaf 100644 --- a/lib/data/hub.dart +++ b/lib/data/hub.dart @@ -1,4 +1,4 @@ -// HubService — singleton wrapper around fai_dart_sdk's HubClient. +// HubService — singleton wrapper around chain_dart_sdk's HubClient. // // Centralises the gRPC connection so each page just imports // `HubService.instance` instead of constructing its own client. @@ -1282,7 +1282,7 @@ class SatelliteEnrollment { return 'federation:\n' ' upstream: https://:\n' ' hub_name: $name\n' - ' bootstrap_token_env: FAI_BOOTSTRAP_TOKEN$ca'; + ' bootstrap_token_env: CHAIN_BOOTSTRAP_TOKEN$ca'; } } @@ -1553,7 +1553,7 @@ class HardwareSnapshot { } /// One curated model with editorial metadata. Wire-faithful -/// with `crates/fai_hub/system-ai/models.yaml`. +/// with `crates/chain_hub/system-ai/models.yaml`. class CuratedModelInfo { final String id; final String family; diff --git a/lib/data/registry_token.dart b/lib/data/registry_token.dart index dde1799..820b9c8 100644 --- a/lib/data/registry_token.dart +++ b/lib/data/registry_token.dart @@ -4,7 +4,7 @@ import 'package:path/path.dart' as p; /// Operator-managed registry auth token, kept at /// `~/.chain/registry-token` (mode 0600 on Unix). The hub reads /// this file at install time when `CHAIN_REGISTRY_TOKEN` is unset -/// — see `download_to_temp` in `crates/fai_hub/src/lib.rs`. +/// — see `download_to_temp` in `crates/chain_hub/src/lib.rs`. /// /// Studio writes the file directly so a fresh install never /// requires the operator to fiddle with shell env vars. diff --git a/lib/data/system_actions.dart b/lib/data/system_actions.dart index 6ef7822..6cd8149 100644 --- a/lib/data/system_actions.dart +++ b/lib/data/system_actions.dart @@ -22,7 +22,7 @@ import 'package:shared_preferences/shared_preferences.dart'; /// Sentinel returned in `_runFai(...).stderr` when no `fai` /// binary could be located. UI layers should not display this -/// verbatim — they check [SystemActions.faiBinaryExists] first +/// verbatim — they check [SystemActions.chainBinaryExists] first /// and render a localized, actionable message instead. const String kFaiBinaryNotFound = 'fai-binary-not-found'; @@ -37,7 +37,7 @@ class SystemActions { /// SharedPreferences key for an operator-chosen `fai` binary /// path (set via the file picker when auto-detection fails). - static const String _kFaiBinaryPrefKey = 'system.fai_binary_path'; + static const String _kFaiBinaryPrefKey = 'system.chain_binary_path'; /// In-memory cache of the operator override, loaded once at /// startup by [loadFaiBinaryOverride]. Null = no override. @@ -71,7 +71,7 @@ class SystemActions { /// the canonical install dir). UI uses this to decide between /// the normal daemon-control affordances and the "locate the /// binary / read the install guide" recovery path. - static bool faiBinaryExists() => _faiExecutable() != null; + static bool chainBinaryExists() => _faiExecutable() != null; /// Ask the OS to open [path] in the default handler. On macOS /// this opens text files in TextEdit, configs in the registered @@ -137,7 +137,7 @@ class SystemActions { /// Run a `chain daemon ...` subcommand and surface the captured /// output. Used by Doctor's "Restart daemon" button. - static Future<({bool ok, String stdout, String stderr})> faiDaemon( + static Future<({bool ok, String stdout, String stderr})> chainDaemon( List args, ) async { return _runFai(['daemon', ...args]); @@ -145,7 +145,7 @@ class SystemActions { /// Run `chain update apply --channel `. Long-running on a slow /// network — caller should show a spinner. - static Future<({bool ok, String stdout, String stderr})> faiUpdateApply( + static Future<({bool ok, String stdout, String stderr})> chainUpdateApply( String channel, ) async { return _runFai(['update', 'apply', '--channel', channel]); @@ -154,7 +154,7 @@ class SystemActions { /// Switch the active channel pointer at `~/.chain/current-channel`. /// The CLI also restarts the daemon for the new channel, so the /// caller does not need a follow-up restart. - static Future<({bool ok, String stdout, String stderr})> faiChannelSwitch( + static Future<({bool ok, String stdout, String stderr})> chainChannelSwitch( String channel, ) async { return _runFai(['channel', 'switch', channel]); @@ -164,14 +164,14 @@ class SystemActions { /// (launchd plist on macOS, systemd-user unit on Linux). On /// Windows the platform CLI returns a "not supported" exit /// status that the caller surfaces as an error message. - static Future<({bool ok, String stdout, String stderr})> faiDaemonEnable( + static Future<({bool ok, String stdout, String stderr})> chainDaemonEnable( String channel, ) async { return _runFai(['daemon', 'enable', '--channel', channel]); } - /// Remove the autostart unit installed by [faiDaemonEnable]. - static Future<({bool ok, String stdout, String stderr})> faiDaemonDisable( + /// Remove the autostart unit installed by [chainDaemonEnable]. + static Future<({bool ok, String stdout, String stderr})> chainDaemonDisable( String channel, ) async { return _runFai(['daemon', 'disable', '--channel', channel]); @@ -186,7 +186,7 @@ class SystemActions { /// /// Studio's gRPC channel goes down for ~1s while the daemon /// restarts — caller should follow up with a reconnect. - static Future<({bool ok, String stdout, String stderr})> faiReset({ + static Future<({bool ok, String stdout, String stderr})> chainReset({ bool keepModules = false, bool keepData = false, }) async { @@ -202,9 +202,9 @@ class SystemActions { final exe = _faiExecutable(); if (exe == null) { // Sentinel, not a user-facing string. Callers detect this - // via `faiBinaryExists()` and render a localized, + // via `chainBinaryExists()` and render a localized, // actionable recovery message (locate binary / install - // guide) instead of CLI jargon like "set FAI_BIN". + // guide) instead of CLI jargon like "set CHAIN_BIN". return (ok: false, stdout: '', stderr: kFaiBinaryNotFound); } try { @@ -226,7 +226,7 @@ class SystemActions { } /// Locate the `fai` binary. Order: operator override (set via - /// the file picker), $FAI_BIN, PATH, fallback to the canonical + /// the file picker), $CHAIN_BIN, PATH, fallback to the canonical /// install location under the user's home dir. static String? _faiExecutable() { final override = _faiBinaryOverride; @@ -236,7 +236,7 @@ class SystemActions { return override; } - final fromEnv = Platform.environment['FAI_BIN']; + final fromEnv = Platform.environment['CHAIN_BIN']; if (fromEnv != null && fromEnv.isNotEmpty && File(fromEnv).existsSync()) { return fromEnv; } diff --git a/lib/data/theme_plugin.dart b/lib/data/theme_plugin.dart index 701ce31..03f1770 100644 --- a/lib/data/theme_plugin.dart +++ b/lib/data/theme_plugin.dart @@ -16,7 +16,7 @@ import 'hub.dart'; /// SharedPreferences key for the operator's preferred theme /// plugin. `null` (or absent) means "use Studio's built-in -/// default" (FaiTheme.light / .dark). +/// default" (ChainTheme.light / .dark). const _kThemePluginKey = 'theme_plugin_capability'; /// One ColorScheme pair as built from a theme-plugin's @@ -96,14 +96,14 @@ Future loadThemePluginSchemes(String capability) async { } /// Build a `ThemeData` from a plugin-supplied `ColorScheme`. -/// Delegates to [FaiTheme.fromColorScheme] so plugin-themed +/// Delegates to [ChainTheme.fromColorScheme] so plugin-themed /// builds and built-in themes share one structural shape — same /// textTheme cascade, same component themes, same `inherit` /// values on every TextStyle. Without that, Flutter's /// AnimatedDefaultTextStyle lerp blows up when swapping between /// the two paths. ThemeData themeDataFromScheme(ColorScheme scheme) => - FaiTheme.fromColorScheme(scheme); + ChainTheme.fromColorScheme(scheme); /// Translate the plugin's 14 ARGB tokens into a `ColorScheme`. /// Order matches the plugin contract: diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index f38d7fc..e0e3ef2 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -1539,13 +1539,13 @@ "detail": {"type": "String"} } }, - "faiBinaryNotFound": "Das Programm `fai` wurde auf diesem Rechner nicht gefunden.", - "faiBinaryNotFoundHint": "Installiere die Ch∆In-Plattform oder verweise Studio auf eine vorhandene `fai`-Datei.", - "faiBinaryLocateButton": "`fai`-Programm suchen …", - "faiBinaryInstallDocsButton": "Installationsanleitung öffnen", - "faiBinaryPickerTitle": "`fai`-Programm auswählen", - "faiBinarySetOk": "Verwende `fai` unter {path}", - "@faiBinarySetOk": { + "chainBinaryNotFound": "Das Programm `chain` wurde auf diesem Rechner nicht gefunden.", + "chainBinaryNotFoundHint": "Installiere die Ch∆In-Plattform oder verweise Studio auf eine vorhandene `chain`-Datei.", + "chainBinaryLocateButton": "`chain`-Programm suchen …", + "chainBinaryInstallDocsButton": "Installationsanleitung öffnen", + "chainBinaryPickerTitle": "`chain`-Programm auswählen", + "chainBinarySetOk": "Verwende `chain` unter {path}", + "@chainBinarySetOk": { "placeholders": { "path": {"type": "String"} } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7b02d9e..90fbe3f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1542,13 +1542,13 @@ "detail": {"type": "String"} } }, - "faiBinaryNotFound": "Could not find the `fai` program on this machine.", - "faiBinaryNotFoundHint": "Install the Ch∆In platform, or point Studio at an existing `fai` binary.", - "faiBinaryLocateButton": "Locate `fai` binary…", - "faiBinaryInstallDocsButton": "Open install guide", - "faiBinaryPickerTitle": "Select the `fai` binary", - "faiBinarySetOk": "Using `fai` binary at {path}", - "@faiBinarySetOk": { + "chainBinaryNotFound": "Could not find the `chain` program on this machine.", + "chainBinaryNotFoundHint": "Install the Ch∆In platform, or point Studio at an existing `chain` binary.", + "chainBinaryLocateButton": "Locate `chain` binary…", + "chainBinaryInstallDocsButton": "Open install guide", + "chainBinaryPickerTitle": "Select the `chain` binary", + "chainBinarySetOk": "Using `chain` binary at {path}", + "@chainBinarySetOk": { "placeholders": { "path": {"type": "String"} } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index d994ad6..2913867 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -4328,41 +4328,41 @@ abstract class AppLocalizations { /// **'Could not start daemon: {detail}'** String daemonStartFailed(String detail); - /// No description provided for @faiBinaryNotFound. + /// No description provided for @chainBinaryNotFound. /// /// In en, this message translates to: - /// **'Could not find the `fai` program on this machine.'** - String get faiBinaryNotFound; + /// **'Could not find the `chain` program on this machine.'** + String get chainBinaryNotFound; - /// No description provided for @faiBinaryNotFoundHint. + /// No description provided for @chainBinaryNotFoundHint. /// /// In en, this message translates to: - /// **'Install the Ch∆In platform, or point Studio at an existing `fai` binary.'** - String get faiBinaryNotFoundHint; + /// **'Install the Ch∆In platform, or point Studio at an existing `chain` binary.'** + String get chainBinaryNotFoundHint; - /// No description provided for @faiBinaryLocateButton. + /// No description provided for @chainBinaryLocateButton. /// /// In en, this message translates to: - /// **'Locate `fai` binary…'** - String get faiBinaryLocateButton; + /// **'Locate `chain` binary…'** + String get chainBinaryLocateButton; - /// No description provided for @faiBinaryInstallDocsButton. + /// No description provided for @chainBinaryInstallDocsButton. /// /// In en, this message translates to: /// **'Open install guide'** - String get faiBinaryInstallDocsButton; + String get chainBinaryInstallDocsButton; - /// No description provided for @faiBinaryPickerTitle. + /// No description provided for @chainBinaryPickerTitle. /// /// In en, this message translates to: - /// **'Select the `fai` binary'** - String get faiBinaryPickerTitle; + /// **'Select the `chain` binary'** + String get chainBinaryPickerTitle; - /// No description provided for @faiBinarySetOk. + /// No description provided for @chainBinarySetOk. /// /// In en, this message translates to: - /// **'Using `fai` binary at {path}'** - String faiBinarySetOk(String path); + /// **'Using `chain` binary at {path}'** + String chainBinarySetOk(String path); /// No description provided for @sysAiFixParse. /// diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 7e8df68..9b0a336 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -2535,25 +2535,25 @@ class AppLocalizationsDe extends AppLocalizations { } @override - String get faiBinaryNotFound => - 'Das Programm `fai` wurde auf diesem Rechner nicht gefunden.'; + String get chainBinaryNotFound => + 'Das Programm `chain` wurde auf diesem Rechner nicht gefunden.'; @override - String get faiBinaryNotFoundHint => - 'Installiere die Ch∆In-Plattform oder verweise Studio auf eine vorhandene `fai`-Datei.'; + String get chainBinaryNotFoundHint => + 'Installiere die Ch∆In-Plattform oder verweise Studio auf eine vorhandene `chain`-Datei.'; @override - String get faiBinaryLocateButton => '`fai`-Programm suchen …'; + String get chainBinaryLocateButton => '`chain`-Programm suchen …'; @override - String get faiBinaryInstallDocsButton => 'Installationsanleitung öffnen'; + String get chainBinaryInstallDocsButton => 'Installationsanleitung öffnen'; @override - String get faiBinaryPickerTitle => '`fai`-Programm auswählen'; + String get chainBinaryPickerTitle => '`chain`-Programm auswählen'; @override - String faiBinarySetOk(String path) { - return 'Verwende `fai` unter $path'; + String chainBinarySetOk(String path) { + return 'Verwende `chain` unter $path'; } @override diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index aa3c396..c6dc667 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -2538,25 +2538,25 @@ class AppLocalizationsEn extends AppLocalizations { } @override - String get faiBinaryNotFound => - 'Could not find the `fai` program on this machine.'; + String get chainBinaryNotFound => + 'Could not find the `chain` program on this machine.'; @override - String get faiBinaryNotFoundHint => - 'Install the Ch∆In platform, or point Studio at an existing `fai` binary.'; + String get chainBinaryNotFoundHint => + 'Install the Ch∆In platform, or point Studio at an existing `chain` binary.'; @override - String get faiBinaryLocateButton => 'Locate `fai` binary…'; + String get chainBinaryLocateButton => 'Locate `chain` binary…'; @override - String get faiBinaryInstallDocsButton => 'Open install guide'; + String get chainBinaryInstallDocsButton => 'Open install guide'; @override - String get faiBinaryPickerTitle => 'Select the `fai` binary'; + String get chainBinaryPickerTitle => 'Select the `chain` binary'; @override - String faiBinarySetOk(String path) { - return 'Using `fai` binary at $path'; + String chainBinarySetOk(String path) { + return 'Using `chain` binary at $path'; } @override diff --git a/lib/main.dart b/lib/main.dart index 021f3be..3046664 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'data/fai_log.dart'; +import 'data/chain_log.dart'; import 'data/hub.dart'; import 'data/system_actions.dart'; import 'data/theme_plugin.dart'; @@ -22,7 +22,7 @@ import 'pages/store.dart'; import 'pages/welcome.dart'; import 'theme/theme.dart'; import 'theme/tokens.dart'; -import 'widgets/fai_search_palette.dart'; +import 'widgets/chain_search_palette.dart'; import 'widgets/widgets.dart'; /// Studio's own build version. Bump on every UI commit so the @@ -55,7 +55,7 @@ class StudioApp extends StatefulWidget { /// Capability name of the active theme plugin (e.g. /// `studio.theme.solarized`) or `null` for the built-in - /// FaiTheme. + /// ChainTheme. final String? initialThemePlugin; const StudioApp({ @@ -89,7 +89,7 @@ class StudioAppState extends State { /// Capability name of the active `studio.theme.*` plugin /// (e.g. `studio.theme.solarized`) or `null` for - /// FaiTheme's built-in palette. Flipping this triggers a + /// ChainTheme's built-in palette. Flipping this triggers a /// re-fetch of the plugin's ColorSchemes; the MaterialApp /// rebuilds with the new themes. late final ValueNotifier themePluginNotifier; @@ -188,7 +188,7 @@ class StudioAppState extends State { // own "applied" snackbar is replaced upstream by the // friendlier failure message via [themePluginFailed]. // ignore: discarded_futures - FaiLog.instance.error( + ChainLog.instance.error( 'theme.plugin.load', e, context: 'capability=$capability', @@ -217,8 +217,8 @@ class StudioAppState extends State { return MaterialApp( title: 'Ch∆In Studio', debugShowCheckedModeBanner: false, - theme: p.light ?? FaiTheme.light(), - darkTheme: p.dark ?? FaiTheme.dark(), + theme: p.light ?? ChainTheme.light(), + darkTheme: p.dark ?? ChainTheme.dark(), themeMode: _flutterMode(mode), locale: locale, supportedLocales: AppLocalizations.supportedLocales, @@ -268,7 +268,7 @@ class StudioShellState extends State { /// row and the WelcomePage hero CTA. Future startDaemon() async { final l = AppLocalizations.of(context)!; - final r = await SystemActions.faiDaemon(['start']); + final r = await SystemActions.chainDaemon(['start']); if (!mounted) return; final detail = r.stderr.trim().isEmpty ? r.stdout.trim() : r.stderr.trim(); ScaffoldMessenger.of(context).showSnackBar( @@ -325,7 +325,7 @@ class StudioShellState extends State { // (permissions, on-disk path, uninstall) now lives inside // the Store's detail sheet, and the Store with the // "Installed" filter covers the listing role. Cmd+K still - // opens FaiModuleSheet for quick info. + // opens ChainModuleSheet for quick info. // Flows destination IS the editor as of 0.52.0 — the // graph / text / run tabs all live behind one icon. The // separate "flow-editor" destination is gone; the Cmd+K @@ -413,9 +413,9 @@ class StudioShellState extends State { void _openSearchPalette() { final l = AppLocalizations.of(context)!; final pagesGroup = l.searchGroupPages; - final hits = [ + final hits = [ for (var i = 0; i < _pages.length; i++) - FaiSearchHit( + ChainSearchHit( label: _pages[i].labelOf(context), hint: l.searchPageHint(i + 1), icon: _pages[i].icon, @@ -425,18 +425,18 @@ class StudioShellState extends State { setState(() => _selectedIndex = i); }, ), - FaiSearchHit( + ChainSearchHit( label: l.searchSettingsLabel, hint: l.searchSettingsHint, icon: Icons.settings_outlined, group: pagesGroup, onSelect: () { Navigator.of(context).pop(); - FaiSettingsDialog.show(context); + ChainSettingsDialog.show(context); }, ), ]; - FaiSearchPalette.show(context, staticHits: hits); + ChainSearchPalette.show(context, staticHits: hits); } @override @@ -481,7 +481,7 @@ class StudioShellState extends State { ), _OpenSettingsIntent: CallbackAction<_OpenSettingsIntent>( onInvoke: (_) { - FaiSettingsDialog.show(context); + ChainSettingsDialog.show(context); return null; }, ), @@ -513,13 +513,13 @@ class StudioShellState extends State { if (_hubUnreachable) _HubUnreachableBanner( endpoint: HubService.instance.endpointLabel, - onOpenSettings: () => FaiSettingsDialog.show(context), + onOpenSettings: () => ChainSettingsDialog.show(context), ), Expanded( child: AnimatedSwitcher( - duration: FaiMotion.base, - switchInCurve: FaiMotion.easing, - switchOutCurve: FaiMotion.easing, + duration: ChainMotion.base, + switchInCurve: ChainMotion.easing, + switchOutCurve: ChainMotion.easing, transitionBuilder: (child, anim) => FadeTransition( opacity: anim, child: SlideTransition( @@ -569,8 +569,8 @@ class _HubUnreachableBanner extends StatelessWidget { color: theme.colorScheme.errorContainer, child: Padding( padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.lg, - vertical: FaiSpace.sm, + horizontal: ChainSpace.lg, + vertical: ChainSpace.sm, ), child: Row( children: [ @@ -579,7 +579,7 @@ class _HubUnreachableBanner extends StatelessWidget { size: 18, color: theme.colorScheme.onErrorContainer, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( child: Text( l.hubUnreachableBanner(endpoint), @@ -588,7 +588,7 @@ class _HubUnreachableBanner extends StatelessWidget { ), ), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), TextButton( onPressed: onOpenSettings, child: Text(l.hubUnreachableOpenSettings), @@ -674,8 +674,8 @@ class _SidebarState extends State<_Sidebar> // reveal is a deliberate, eased gesture, not a flicker. // The collapse runs faster (reverseDuration) so dismissing // the rail feels crisp rather than sluggish. - duration: FaiMotion.slow, - reverseDuration: FaiMotion.base, + duration: ChainMotion.slow, + reverseDuration: ChainMotion.base, value: 0, ); } @@ -688,7 +688,7 @@ class _SidebarState extends State<_Sidebar> Future _startDaemon(BuildContext context) async { final l = AppLocalizations.of(context)!; - final r = await SystemActions.faiDaemon(['start']); + final r = await SystemActions.chainDaemon(['start']); if (!context.mounted) return; final detail = r.stderr.trim().isEmpty ? r.stdout.trim() : r.stderr.trim(); ScaffoldMessenger.of(context).showSnackBar( @@ -704,8 +704,8 @@ class _SidebarState extends State<_Sidebar> Widget build(BuildContext context) { final theme = Theme.of(context); final mode = widget.connected == true - ? FaiDeltaMode.live - : FaiDeltaMode.idle; + ? ChainDeltaMode.live + : ChainDeltaMode.idle; final hasChannel = widget.activeChannel != null && widget.activeChannel!.isNotEmpty; return MouseRegion( @@ -721,7 +721,7 @@ class _SidebarState extends State<_Sidebar> return Container( width: width, color: theme.colorScheme.surfaceContainerLow, - padding: const EdgeInsets.symmetric(vertical: FaiSpace.xl), + padding: const EdgeInsets.symmetric(vertical: ChainSpace.xl), child: ClipRect( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -730,7 +730,7 @@ class _SidebarState extends State<_Sidebar> SizedBox( height: _brandRowH, child: _sidebarRow( - leading: FaiDeltaMark( + leading: ChainDeltaMark( color: theme.colorScheme.primary, mode: mode, ), @@ -796,7 +796,7 @@ class _SidebarState extends State<_Sidebar> ) : const SizedBox.shrink(), ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Expanded( child: ListView( padding: EdgeInsets.zero, @@ -868,7 +868,7 @@ class _SidebarState extends State<_Sidebar> child: Opacity( opacity: t, child: Padding( - padding: const EdgeInsets.only(right: FaiSpace.md), + padding: const EdgeInsets.only(right: ChainSpace.md), child: label, ), ), @@ -889,7 +889,7 @@ class _SidebarState extends State<_Sidebar> } Future _openSettings(BuildContext context) async { - final saved = await FaiSettingsDialog.show(context); + final saved = await ChainSettingsDialog.show(context); if (saved && context.mounted) { // Force a sidebar rebuild so the new endpoint shows in // the connection pill. @@ -911,7 +911,7 @@ class _ChannelPill extends StatelessWidget { case 'production': return cs.error; case 'beta': - return FaiColors.warning; + return ChainColors.warning; case 'dev': return cs.primary; case 'local': @@ -943,17 +943,17 @@ class _ChannelPill extends StatelessWidget { message: l.sidebarChannelTooltip, child: Material( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), child: InkWell( - borderRadius: BorderRadius.circular(FaiRadius.sm), - onTap: () => FaiSettingsDialog.show(context), + borderRadius: BorderRadius.circular(ChainRadius.sm), + onTap: () => ChainSettingsDialog.show(context), child: Container( padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.sm, + horizontal: ChainSpace.sm, vertical: 4, ), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: accent.withValues(alpha: 0.4)), ), child: Row( @@ -963,7 +963,7 @@ class _ChannelPill extends StatelessWidget { const SizedBox(width: 4), Text( channel, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 10, weight: FontWeight.w600, color: accent, @@ -980,7 +980,7 @@ class _ChannelPill extends StatelessWidget { /// Mini-version of [_ConnectionLabel] used when the sidebar is /// collapsed. A 10px dot in the same green/red/amber tonality -/// the FaiDeltaMark uses for live / idle / unknown. +/// the ChainDeltaMark uses for live / idle / unknown. class _CollapsedConnectionDot extends StatelessWidget { final bool? connected; const _CollapsedConnectionDot({required this.connected}); @@ -989,10 +989,10 @@ class _CollapsedConnectionDot extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final color = connected == true - ? FaiColors.success + ? ChainColors.success : connected == false ? theme.colorScheme.error - : FaiColors.warning; + : ChainColors.warning; return Container( width: 10, height: 10, @@ -1025,7 +1025,7 @@ class _CollapsedChannelChip extends StatelessWidget { case 'production': return cs.error; case 'beta': - return FaiColors.warning; + return ChainColors.warning; case 'dev': return cs.primary; case 'local': @@ -1053,7 +1053,7 @@ class _CollapsedChannelChip extends StatelessWidget { alignment: Alignment.center, child: Text( _letter, - style: FaiTheme.mono(size: 10, weight: FontWeight.w700, color: accent), + style: ChainTheme.mono(size: 10, weight: FontWeight.w700, color: accent), ), ); } @@ -1080,7 +1080,7 @@ class _BrandLabel extends StatelessWidget { const SizedBox(height: 2), Text( 'v$kStudioVersion', - style: FaiTheme.mono(size: 10, color: theme.colorScheme.primary), + style: ChainTheme.mono(size: 10, color: theme.colorScheme.primary), maxLines: 1, overflow: TextOverflow.ellipsis, ), @@ -1133,7 +1133,7 @@ class _ConnectionLabel extends StatelessWidget { endpoint, maxLines: 1, overflow: TextOverflow.ellipsis, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 10, color: theme.colorScheme.onSurfaceVariant, ), @@ -1251,7 +1251,7 @@ class _SidebarItemState extends State<_SidebarItem> { child: Opacity( opacity: widget.t, child: Padding( - padding: const EdgeInsets.only(right: FaiSpace.md), + padding: const EdgeInsets.only(right: ChainSpace.md), child: Text( label, overflow: TextOverflow.fade, @@ -1278,19 +1278,19 @@ class _SidebarItemState extends State<_SidebarItem> { behavior: HitTestBehavior.opaque, onTap: widget.onTap, child: AnimatedContainer( - duration: FaiMotion.fast, + duration: ChainMotion.fast, // No horizontal padding — the icon column anchors // the left. The bg highlight spans the rail's // current width (typical nav-rail behaviour). - padding: const EdgeInsets.symmetric(vertical: FaiSpace.md), + padding: const EdgeInsets.symmetric(vertical: ChainSpace.md), decoration: BoxDecoration( color: bg, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), // Lift only the selected item — a whisper of depth // marks "you are here" without competing with the // accent fill. boxShadow: widget.selected - ? FaiElevation.low(theme.brightness) + ? ChainElevation.low(theme.brightness) : null, ), child: content, @@ -1348,7 +1348,7 @@ class _Footer extends StatelessWidget { child: Opacity( opacity: t, child: Padding( - padding: const EdgeInsets.only(right: FaiSpace.sm), + padding: const EdgeInsets.only(right: ChainSpace.sm), child: Row( children: [ _ThemeToggle(), @@ -1515,7 +1515,7 @@ class _SidebarClockState extends State<_SidebarClock> { message: tooltip, child: Text( '$hh:$mm:$ss', - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.85), ), @@ -1553,7 +1553,7 @@ class _LanguageToggle extends StatelessWidget { ), child: Text( isDe ? 'DE' : 'EN', - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, weight: FontWeight.w600, color: Theme.of(context).colorScheme.primary, diff --git a/lib/pages/approvals.dart b/lib/pages/approvals.dart index f622480..e099c58 100644 --- a/lib/pages/approvals.dart +++ b/lib/pages/approvals.dart @@ -224,7 +224,7 @@ class _ApprovalsPageState extends State tooltip: AppLocalizations.of(context)!.approvalsReloadTooltip, onPressed: _refresh, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), ], ), body: TabBarView( @@ -288,7 +288,7 @@ class _PendingList extends StatelessWidget { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.cloud_off_outlined, iconColor: theme.colorScheme.error, title: l.hubUnreachable, @@ -301,7 +301,7 @@ class _PendingList extends StatelessWidget { } final pending = snapshot.data ?? []; if (pending.isEmpty) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.task_alt_outlined, title: l.approvalsInboxZero, hint: l.approvalsInboxHint, @@ -314,15 +314,15 @@ class _PendingList extends StatelessWidget { children: [ ListView.separated( padding: EdgeInsets.fromLTRB( - FaiSpace.xl, - FaiSpace.xl, - FaiSpace.xl, + ChainSpace.xl, + ChainSpace.xl, + ChainSpace.xl, // Keep the last card clear of the floating // batch action bar. - selected.isEmpty ? FaiSpace.xl : 96.0, + selected.isEmpty ? ChainSpace.xl : 96.0, ), itemCount: pending.length, - separatorBuilder: (_, _) => const SizedBox(height: FaiSpace.md), + separatorBuilder: (_, _) => const SizedBox(height: ChainSpace.md), itemBuilder: (context, i) { final a = pending[i]; final isSelected = selectedIds.contains(a.id); @@ -337,9 +337,9 @@ class _PendingList extends StatelessWidget { ), if (selected.isNotEmpty) Positioned( - left: FaiSpace.xl, - right: FaiSpace.xl, - bottom: FaiSpace.lg, + left: ChainSpace.xl, + right: ChainSpace.xl, + bottom: ChainSpace.lg, child: _BatchActionBar( selectedCount: selected.length, totalCount: pending.length, @@ -387,15 +387,15 @@ class _BatchActionBar extends StatelessWidget { final l = AppLocalizations.of(context)!; return Material( elevation: 4, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), color: theme.colorScheme.surfaceContainerHigh, child: Container( padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.lg, - vertical: FaiSpace.md, + horizontal: ChainSpace.lg, + vertical: ChainSpace.md, ), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Row( @@ -405,14 +405,14 @@ class _BatchActionBar extends StatelessWidget { size: 18, color: theme.colorScheme.primary, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( l.approvalsBatchSelected(selectedCount), style: theme.textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.w600, ), ), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), if (selectedCount < totalCount) TextButton( onPressed: inFlight ? null : onSelectAll, @@ -434,7 +434,7 @@ class _BatchActionBar extends StatelessWidget { ), ), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), FilledButton.icon( onPressed: inFlight ? null : onApprove, icon: inFlight @@ -470,7 +470,7 @@ class _HistoryList extends StatelessWidget { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.cloud_off_outlined, iconColor: theme.colorScheme.error, title: l.hubUnreachable, @@ -483,16 +483,16 @@ class _HistoryList extends StatelessWidget { } final decided = snapshot.data ?? []; if (decided.isEmpty) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.history, title: l.approvalsHistoryEmpty, hint: l.approvalsHistoryEmptyHint, ); } return ListView.separated( - padding: const EdgeInsets.all(FaiSpace.xl), + padding: const EdgeInsets.all(ChainSpace.xl), itemCount: decided.length, - separatorBuilder: (_, _) => const SizedBox(height: FaiSpace.xs), + separatorBuilder: (_, _) => const SizedBox(height: ChainSpace.xs), itemBuilder: (context, i) { final a = decided[i]; return _HistoryRow(record: a); @@ -522,7 +522,7 @@ class _ApprovalCard extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final l = AppLocalizations.of(context)!; - return FaiCard( + return ChainCard( accentTop: true, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -534,13 +534,13 @@ class _ApprovalCard extends StatelessWidget { onChanged: (_) => onToggleSelected(), visualDensity: VisualDensity.compact, ), - const SizedBox(width: FaiSpace.xs), - FaiPill( + const SizedBox(width: ChainSpace.xs), + ChainPill( label: l.approvalsPillPending, - tone: FaiPillTone.warning, + tone: ChainPillTone.warning, icon: Icons.pending_outlined, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( child: Text( '${approval.flowName} › ${approval.stepId}', @@ -550,17 +550,17 @@ class _ApprovalCard extends StatelessWidget { ), ), if (approval.expiresAt != null) - FaiPill( + ChainPill( label: _expiresInLabel(context, approval.expiresAt!), - tone: FaiPillTone.neutral, + tone: ChainPillTone.neutral, icon: Icons.schedule, ), ], ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), Text(approval.prompt, style: theme.textTheme.bodyLarge), if (approval.payloadPreview != null) ...[ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Padding( padding: const EdgeInsets.only(bottom: 4), child: Text( @@ -574,27 +574,27 @@ class _ApprovalCard extends StatelessWidget { ), Container( width: double.infinity, - padding: const EdgeInsets.all(FaiSpace.md), + padding: const EdgeInsets.all(ChainSpace.md), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: SelectableText( _prettyPreview(approval.payloadPreview!), - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurface, ), ), ), ], - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), Row( children: [ Text( approval.id, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 10, color: theme.colorScheme.onSurfaceVariant, ), @@ -611,7 +611,7 @@ class _ApprovalCard extends StatelessWidget { ), ), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), FilledButton.icon( onPressed: onApprove, icon: const Icon(Icons.check, size: 16), @@ -663,21 +663,21 @@ class _HistoryRow extends StatelessWidget { context: context, builder: (_) => _HistoryDialog(record: record), ), - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), child: Padding( padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.sm, - vertical: FaiSpace.sm, + horizontal: ChainSpace.sm, + vertical: ChainSpace.sm, ), child: Row( children: [ - FaiPill(label: label, tone: tone, icon: icon), - const SizedBox(width: FaiSpace.md), + ChainPill(label: label, tone: tone, icon: icon), + const SizedBox(width: ChainSpace.md), SizedBox( width: 150, child: Text( timeStr, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurfaceVariant, ), @@ -693,7 +693,7 @@ class _HistoryRow extends StatelessWidget { overflow: TextOverflow.ellipsis, ), ), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), if (record.decidedBy.isNotEmpty) Text( record.decidedBy, @@ -707,24 +707,24 @@ class _HistoryRow extends StatelessWidget { ); } - static (FaiPillTone, IconData, String) _statusPresentation( + static (ChainPillTone, IconData, String) _statusPresentation( BuildContext context, String status, ) { final l = AppLocalizations.of(context)!; switch (status) { case 'approved': - return (FaiPillTone.success, Icons.check, l.approvalsPillApproved); + return (ChainPillTone.success, Icons.check, l.approvalsPillApproved); case 'rejected': - return (FaiPillTone.danger, Icons.close, l.approvalsPillRejected); + return (ChainPillTone.danger, Icons.close, l.approvalsPillRejected); case 'expired': return ( - FaiPillTone.neutral, + ChainPillTone.neutral, Icons.timer_off_outlined, l.approvalsPillExpired, ); default: - return (FaiPillTone.neutral, Icons.help_outline, status); + return (ChainPillTone.neutral, Icons.help_outline, status); } } } @@ -743,8 +743,8 @@ class _HistoryDialog extends StatelessWidget { return AlertDialog( title: Row( children: [ - FaiPill(label: label, tone: tone, icon: icon), - const SizedBox(width: FaiSpace.sm), + ChainPill(label: label, tone: tone, icon: icon), + const SizedBox(width: ChainSpace.sm), Expanded( child: Text( '${record.flowName} › ${record.stepId}', @@ -779,7 +779,7 @@ class _HistoryDialog extends StatelessWidget { AppLocalizations.of(context)!.approvalsDialogReason, record.reason, ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( AppLocalizations.of(context)!.approvalsDialogPrompt, style: theme.textTheme.labelSmall?.copyWith( @@ -790,7 +790,7 @@ class _HistoryDialog extends StatelessWidget { const SizedBox(height: 4), SelectableText(record.prompt, style: theme.textTheme.bodyMedium), if (record.payloadPreview != null) ...[ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( AppLocalizations.of(context)!.approvalsPayloadPreview, style: theme.textTheme.labelSmall?.copyWith( @@ -801,22 +801,22 @@ class _HistoryDialog extends StatelessWidget { const SizedBox(height: 4), Container( width: double.infinity, - padding: const EdgeInsets.all(FaiSpace.sm), + padding: const EdgeInsets.all(ChainSpace.sm), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: SelectableText( record.payloadPreview!, - style: FaiTheme.mono(size: 11), + style: ChainTheme.mono(size: 11), ), ), ], - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( record.id, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 10, color: theme.colorScheme.onSurfaceVariant, ), diff --git a/lib/pages/audit.dart b/lib/pages/audit.dart index b292932..422c0ee 100644 --- a/lib/pages/audit.dart +++ b/lib/pages/audit.dart @@ -97,7 +97,7 @@ class _AuditPageState extends State { title: Text(AppLocalizations.of(context)!.auditTitle), actions: [ Padding( - padding: const EdgeInsets.only(right: FaiSpace.lg), + padding: const EdgeInsets.only(right: ChainSpace.lg), child: _FilterChips( value: _typeFilter, onChanged: (v) => setState(() => _typeFilter = v), @@ -113,7 +113,7 @@ class _AuditPageState extends State { tooltip: AppLocalizations.of(context)!.auditClearLogTooltip, onPressed: _onClearPressed, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), ], ), body: Column( @@ -123,14 +123,14 @@ class _AuditPageState extends State { child: !_initialLoaded ? const Center(child: CircularProgressIndicator()) : _error != null && _events.isEmpty - ? FaiEmptyState( + ? ChainEmptyState( icon: Icons.cloud_off_outlined, iconColor: theme.colorScheme.error, title: AppLocalizations.of(context)!.hubUnreachable, hint: AppLocalizations.of(context)!.hubUnreachableHint, ) : filtered.isEmpty - ? FaiEmptyState( + ? ChainEmptyState( icon: Icons.timeline_outlined, title: AppLocalizations.of(context)!.auditNoEvents, hint: AppLocalizations.of(context)!.auditNoEventsHint, @@ -187,7 +187,7 @@ class _AuditPageState extends State { Color _toneFor(String type, ThemeData theme) { if (type.endsWith('.failed')) return theme.colorScheme.error; if (type.endsWith('.completed')) return theme.colorScheme.primary; - if (type.endsWith('.started')) return FaiColors.warning; + if (type.endsWith('.started')) return ChainColors.warning; return theme.colorScheme.outline; } } @@ -220,15 +220,15 @@ class _GroupedEventList extends StatelessWidget { final l = AppLocalizations.of(context)!; final items = _itemsWithHeaders(events, l); return ListView.builder( - padding: const EdgeInsets.all(FaiSpace.xl), + padding: const EdgeInsets.all(ChainSpace.xl), itemCount: items.length, itemBuilder: (context, i) { final item = items[i]; if (item is _GroupHeader) { return Padding( padding: EdgeInsets.only( - top: i == 0 ? 0 : FaiSpace.lg, - bottom: FaiSpace.sm, + top: i == 0 ? 0 : ChainSpace.lg, + bottom: ChainSpace.sm, ), child: Text( item.label, @@ -242,8 +242,8 @@ class _GroupedEventList extends StatelessWidget { } final e = (item as _EventItem).event; return Padding( - padding: const EdgeInsets.only(bottom: FaiSpace.xs), - child: FaiDataRow( + padding: const EdgeInsets.only(bottom: ChainSpace.xs), + child: ChainDataRow( accent: toneFor(e.type), leading: formatTime(e.timestamp), title: e.type, @@ -328,7 +328,7 @@ class _FilterChips extends StatelessWidget { children: [ for (final (v, label) in items) Padding( - padding: const EdgeInsets.only(left: FaiSpace.xs), + padding: const EdgeInsets.only(left: ChainSpace.xs), child: _ChipButton( label: label, selected: value == v, @@ -378,14 +378,14 @@ class _ChipButtonState extends State<_ChipButton> { child: GestureDetector( onTap: widget.onTap, child: AnimatedContainer( - duration: FaiMotion.fast, + duration: ChainMotion.fast, padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.md, + horizontal: ChainSpace.md, vertical: 6, ), decoration: BoxDecoration( color: bg, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all( color: widget.selected ? theme.colorScheme.primary.withValues(alpha: 0.3) @@ -394,7 +394,7 @@ class _ChipButtonState extends State<_ChipButton> { ), child: Text( widget.label, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, weight: widget.selected ? FontWeight.w500 : FontWeight.w400, color: fg, @@ -420,8 +420,8 @@ class _LiveStatusBar extends StatelessWidget { return Container( width: double.infinity, padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.xl, - vertical: FaiSpace.sm, + horizontal: ChainSpace.xl, + vertical: ChainSpace.sm, ), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, @@ -431,11 +431,11 @@ class _LiveStatusBar extends StatelessWidget { ), child: Row( children: [ - FaiStatusDot( - color: live ? FaiColors.success : FaiColors.danger, + ChainStatusDot( + color: live ? ChainColors.success : ChainColors.danger, pulsing: live, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( live ? l.auditLiveStatus(eventCount) : l.auditDisconnected(error!), style: theme.textTheme.bodySmall?.copyWith( @@ -569,7 +569,7 @@ class _EventDetailDialogState extends State<_EventDetailDialog> { return AlertDialog( title: Text(event.type), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), ), content: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 640, maxHeight: 560), @@ -616,7 +616,7 @@ class _EventDetailDialogState extends State<_EventDetailDialog> { valueColor: theme.colorScheme.error, ), if (detail.isNotEmpty) ...[ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( l.auditDetailHeader, style: theme.textTheme.labelSmall?.copyWith( @@ -627,17 +627,17 @@ class _EventDetailDialogState extends State<_EventDetailDialog> { const SizedBox(height: 4), Container( width: double.infinity, - padding: const EdgeInsets.all(FaiSpace.md), + padding: const EdgeInsets.all(ChainSpace.md), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: theme.colorScheme.outlineVariant), ), - child: SelectableText(detail, style: FaiTheme.mono(size: 11)), + child: SelectableText(detail, style: ChainTheme.mono(size: 11)), ), ], if (_explanation != null || _explaining) ...[ - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), _ExplanationPanel( explaining: _explaining, result: _explanation, @@ -729,7 +729,7 @@ class _FlowRunDialog extends StatelessWidget { return AlertDialog( title: Text(l.auditFlowRunDialogTitle(flowName)), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), ), content: ConstrainedBox( constraints: BoxConstraints(maxWidth: 640, maxHeight: maxHeight), @@ -743,15 +743,15 @@ class _FlowRunDialog extends StatelessWidget { color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Flexible( child: ListView.separated( shrinkWrap: true, itemCount: related.length, - separatorBuilder: (_, _) => const SizedBox(height: FaiSpace.xs), + separatorBuilder: (_, _) => const SizedBox(height: ChainSpace.xs), itemBuilder: (context, i) { final e = related[i]; - return FaiDataRow( + return ChainDataRow( accent: _toneFor(e.type, theme), leading: _formatRelativeTime(e.timestamp), title: e.type, @@ -792,7 +792,7 @@ class _FlowRunDialog extends StatelessWidget { Color _toneFor(String type, ThemeData theme) { if (type.endsWith('.failed')) return theme.colorScheme.error; if (type.endsWith('.completed')) return theme.colorScheme.primary; - if (type.endsWith('.started')) return FaiColors.warning; + if (type.endsWith('.started')) return ChainColors.warning; return theme.colorScheme.outline; } } @@ -831,7 +831,7 @@ class _Field extends StatelessWidget { child: SelectableText( value, style: mono - ? FaiTheme.mono( + ? ChainTheme.mono( size: 11, color: valueColor ?? theme.colorScheme.onSurface, ) @@ -875,10 +875,10 @@ class _ExplanationPanel extends StatelessWidget { : theme.colorScheme.error; return Container( width: double.infinity, - padding: const EdgeInsets.all(FaiSpace.md), + padding: const EdgeInsets.all(ChainSpace.md), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: color.withValues(alpha: 0.3)), ), child: Column( @@ -887,7 +887,7 @@ class _ExplanationPanel extends StatelessWidget { Row( children: [ Icon(Icons.auto_awesome, size: 14, color: color), - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), Text( l.auditSystemAi, style: theme.textTheme.labelSmall?.copyWith( @@ -895,15 +895,15 @@ class _ExplanationPanel extends StatelessWidget { letterSpacing: 0.6, ), ), - const SizedBox(width: FaiSpace.xs), - FaiPill( + const SizedBox(width: ChainSpace.xs), + ChainPill( label: privacyMode, tone: privacyMode == 'full' - ? FaiPillTone.warning - : FaiPillTone.neutral, + ? ChainPillTone.warning + : ChainPillTone.neutral, ), if (result != null && result!.isSuccess && result!.cached) ...[ - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), Tooltip( message: () { final hits = result!.cacheHits > 1 @@ -913,9 +913,9 @@ class _ExplanationPanel extends StatelessWidget { ? l.auditCachedTooltipUnknown(hits) : l.auditCachedTooltipKnown(result!.cachedAt, hits); }(), - child: FaiPill( + child: ChainPill( label: l.auditCachedPill, - tone: FaiPillTone.success, + tone: ChainPillTone.success, icon: Icons.bolt, ), ), @@ -926,7 +926,7 @@ class _ExplanationPanel extends StatelessWidget { result!.cached ? l.auditOriginalLatency(result!.latencyMs) : l.auditLatency(result!.latencyMs), - style: FaiTheme.mono( + style: ChainTheme.mono( size: 10, color: theme.colorScheme.onSurfaceVariant, ), @@ -934,7 +934,7 @@ class _ExplanationPanel extends StatelessWidget { if (result != null && result!.isSuccess && onRegenerate != null) ...[ - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Tooltip( message: l.auditRegenerateTooltip, child: IconButton( @@ -946,7 +946,7 @@ class _ExplanationPanel extends StatelessWidget { ], ], ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), if (explaining) Row( children: [ @@ -955,7 +955,7 @@ class _ExplanationPanel extends StatelessWidget { height: 14, child: CircularProgressIndicator(strokeWidth: 2), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( l.auditAskingFull, style: theme.textTheme.bodySmall?.copyWith( @@ -974,7 +974,7 @@ class _ExplanationPanel extends StatelessWidget { ), ), if (result!.fixHint(l).isNotEmpty) ...[ - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Text( l.auditFixLabel, style: theme.textTheme.labelSmall?.copyWith( @@ -1063,7 +1063,7 @@ class _ClearAuditDialogState extends State<_ClearAuditDialog> { color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), TextField( controller: _reviewer, decoration: InputDecoration( @@ -1072,7 +1072,7 @@ class _ClearAuditDialogState extends State<_ClearAuditDialog> { isDense: true, ), ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), ValueListenableBuilder( valueListenable: _reason, builder: (_, _, _) => TextField( diff --git a/lib/pages/doctor.dart b/lib/pages/doctor.dart index 5f1fc5d..1271a34 100644 --- a/lib/pages/doctor.dart +++ b/lib/pages/doctor.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../data/error_presentation.dart'; -import '../data/fai_log.dart'; +import '../data/chain_log.dart'; import '../data/hub.dart'; import '../data/system_actions.dart'; import '../l10n/app_localizations.dart'; @@ -47,7 +47,7 @@ class _DoctorPageState extends State { tooltip: AppLocalizations.of(context)!.doctorRecheckTooltip, onPressed: _refresh, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), ], ), body: FutureBuilder( @@ -58,7 +58,7 @@ class _DoctorPageState extends State { } if (snapshot.hasError) { final l = AppLocalizations.of(context)!; - return FaiEmptyState( + return ChainEmptyState( icon: Icons.cloud_off_outlined, iconColor: Theme.of(context).colorScheme.error, title: l.hubUnreachable, @@ -74,34 +74,34 @@ class _DoctorPageState extends State { s.update.updateAvailable || (!s.update.manifestReachable && s.update.localVersion.isNotEmpty); return ListView( - padding: const EdgeInsets.all(FaiSpace.xl), + padding: const EdgeInsets.all(ChainSpace.xl), children: [ if (showUpdate) _UpdateBanner(status: s.update), - if (showUpdate) const SizedBox(height: FaiSpace.lg), + if (showUpdate) const SizedBox(height: ChainSpace.lg), _SummaryStrip(snapshot: s), - const SizedBox(height: FaiSpace.xl), + const SizedBox(height: ChainSpace.xl), _Section( title: AppLocalizations.of(context)!.doctorEventLogSection, child: _EventLogPanel(snapshot: s, onRefresh: _refresh), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), _Section( title: AppLocalizations.of( context, )!.doctorModulesApprovalsSection, child: _ModulesPanel(snapshot: s), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), _Section( title: AppLocalizations.of(context)!.doctorHostServicesSection, child: _ServicesPanel(snapshot: s), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), _Section( title: AppLocalizations.of(context)!.doctorDaemonFilesSection, child: _DaemonPathsPanel(paths: s.paths), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), _Section( title: AppLocalizations.of(context)!.doctorDaemonControlSection, child: _DaemonActionsCard(), @@ -128,8 +128,8 @@ class _Section extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only( - left: FaiSpace.xs, - bottom: FaiSpace.sm, + left: ChainSpace.xs, + bottom: ChainSpace.sm, ), child: Text( title.toUpperCase(), @@ -162,17 +162,17 @@ class _SummaryStrip extends StatelessWidget { subtitle: l.doctorSummaryCapabilities(snapshot.capabilityCount), icon: Icons.extension_outlined, ), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), _StatTile( label: l.doctorSummaryApprovals, value: snapshot.pendingApprovals.toString(), subtitle: l.doctorSummaryPending, icon: Icons.inbox_outlined, tone: snapshot.pendingApprovals > 0 - ? FaiPillTone.warning - : FaiPillTone.neutral, + ? ChainPillTone.warning + : ChainPillTone.neutral, ), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), _StatTile( label: l.doctorSummaryAudit, value: snapshot.chainHealthy ? '✓' : '⚠', @@ -182,10 +182,10 @@ class _SummaryStrip extends StatelessWidget { ), icon: Icons.shield_outlined, tone: snapshot.chainHealthy - ? FaiPillTone.success - : FaiPillTone.danger, + ? ChainPillTone.success + : ChainPillTone.danger, ), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), _StatTile( label: l.doctorSummaryServices, value: snapshot.services.length.toString(), @@ -202,36 +202,36 @@ class _StatTile extends StatelessWidget { final String value; final String subtitle; final IconData icon; - final FaiPillTone tone; + final ChainPillTone tone; const _StatTile({ required this.label, required this.value, required this.subtitle, required this.icon, - this.tone = FaiPillTone.neutral, + this.tone = ChainPillTone.neutral, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); final accentColor = switch (tone) { - FaiPillTone.success => FaiColors.success, - FaiPillTone.warning => FaiColors.warning, - FaiPillTone.danger => theme.colorScheme.error, + ChainPillTone.success => ChainColors.success, + ChainPillTone.warning => ChainColors.warning, + ChainPillTone.danger => theme.colorScheme.error, _ => theme.colorScheme.primary, }; return Expanded( - child: FaiCard( + child: ChainCard( accentLeft: accentColor, - padding: const EdgeInsets.all(FaiSpace.lg), + padding: const EdgeInsets.all(ChainSpace.lg), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, size: 14, color: theme.colorScheme.onSurfaceVariant), - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), Flexible( child: Text( label.toUpperCase(), @@ -245,7 +245,7 @@ class _StatTile extends StatelessWidget { ), ], ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Text( value, style: theme.textTheme.displaySmall?.copyWith( @@ -282,15 +282,15 @@ class _EventLogPanel extends StatelessWidget { final theme = Theme.of(context); final healthy = snapshot.chainHealthy; final l = AppLocalizations.of(context)!; - return FaiCard( + return ChainCard( child: Row( children: [ Icon( healthy ? Icons.verified_outlined : Icons.gpp_bad_outlined, size: 24, - color: healthy ? FaiColors.success : theme.colorScheme.error, + color: healthy ? ChainColors.success : theme.colorScheme.error, ), - const SizedBox(width: FaiSpace.lg), + const SizedBox(width: ChainSpace.lg), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -322,10 +322,10 @@ class _EventLogPanel extends StatelessWidget { icon: const Icon(Icons.fact_check_outlined, size: 16), label: Text(l.doctorVerifyNow), ), - const SizedBox(width: FaiSpace.sm), - FaiPill( + const SizedBox(width: ChainSpace.sm), + ChainPill( label: healthy ? l.doctorChainPillOk : l.doctorChainPillTamper, - tone: healthy ? FaiPillTone.success : FaiPillTone.danger, + tone: healthy ? ChainPillTone.success : ChainPillTone.danger, ), ], ), @@ -353,7 +353,7 @@ class _DaemonPathsPanel extends StatelessWidget { (l.doctorPathLog, paths.logPath, Icons.description_outlined, false), ( l.doctorPathStudioErrors, - FaiLog.instance.path, + ChainLog.instance.path, Icons.report_problem_outlined, false, ), @@ -365,7 +365,7 @@ class _DaemonPathsPanel extends StatelessWidget { ].where((e) => e.$2.isNotEmpty).toList(); if (entries.isEmpty) { - return FaiCard( + return ChainCard( child: Text( l.doctorPathsEmpty, style: Theme.of(context).textTheme.bodySmall, @@ -373,7 +373,7 @@ class _DaemonPathsPanel extends StatelessWidget { ); } - return FaiCard( + return ChainCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -416,7 +416,7 @@ class _PathRow extends StatelessWidget { child: Row( children: [ Icon(icon, size: 16, color: theme.colorScheme.onSurfaceVariant), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), SizedBox( width: 90, child: Text( @@ -429,13 +429,13 @@ class _PathRow extends StatelessWidget { Expanded( child: SelectableText( path, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurface, ), ), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), if (isLog || isConfig) ...[ OutlinedButton.icon( onPressed: () => isConfig @@ -447,7 +447,7 @@ class _PathRow extends StatelessWidget { visualDensity: VisualDensity.compact, ), ), - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), ], OutlinedButton.icon( onPressed: () async { @@ -494,7 +494,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { /// True when the last daemon action failed because no `fai` /// binary could be located. Swaps the raw output line for the - /// actionable [FaiBinaryRecovery] block. + /// actionable [ChainBinaryRecovery] block. bool _binaryMissing = false; ChannelStatusSnapshot? _channels; @@ -557,7 +557,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { } } } - return FaiCard( + return ChainCard( // Row + Expanded forces the card to take the full width // its parent offers. Without this, the card hugs the // intrinsic width of the longest button row and ends up @@ -575,10 +575,10 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { : Icons.power_off_outlined, size: 18, color: active?.running == true - ? FaiColors.success + ? ChainColors.success : theme.colorScheme.onSurfaceVariant, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( child: Text( active == null @@ -593,36 +593,36 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { ), ), if (active != null) ...[ - const SizedBox(width: FaiSpace.sm), - FaiPill( + const SizedBox(width: ChainSpace.sm), + ChainPill( label: active.running ? l.doctorPillRunning : l.doctorPillStopped, tone: active.running - ? FaiPillTone.success - : FaiPillTone.neutral, + ? ChainPillTone.success + : ChainPillTone.neutral, ), ], ], ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Text( l.doctorDaemonControlHint, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Wrap( - spacing: FaiSpace.sm, - runSpacing: FaiSpace.sm, + spacing: ChainSpace.sm, + runSpacing: ChainSpace.sm, children: [ FilledButton.tonalIcon( onPressed: _busy ? null : () => _run( l.daemonActionRestart, - () => SystemActions.faiDaemon(['restart']), + () => SystemActions.chainDaemon(['restart']), ), icon: const Icon(Icons.restart_alt, size: 16), label: Text(l.doctorRestart), @@ -633,7 +633,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { ? null : () => _run( l.daemonActionStart, - () => SystemActions.faiDaemon(['start']), + () => SystemActions.chainDaemon(['start']), ), icon: const Icon(Icons.play_arrow, size: 16), label: Text(l.doctorStart), @@ -643,7 +643,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { ? null : () => _run( l.daemonActionStop, - () => SystemActions.faiDaemon(['stop']), + () => SystemActions.chainDaemon(['stop']), ), icon: const Icon(Icons.stop_circle_outlined, size: 16), label: Text(l.doctorStop), @@ -653,7 +653,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { ? null : () => _run( l.daemonActionStatus, - () => SystemActions.faiDaemon(['status']), + () => SystemActions.chainDaemon(['status']), ), icon: const Icon(Icons.health_and_safety_outlined, size: 16), label: Text(l.doctorStatusAction), @@ -661,7 +661,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { ], ), if (_busy) ...[ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Row( children: [ const SizedBox( @@ -669,7 +669,7 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { height: 14, child: CircularProgressIndicator(strokeWidth: 2), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( l.doctorDaemonControlWorking, style: theme.textTheme.bodySmall?.copyWith( @@ -680,27 +680,27 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> { ), ], if (_binaryMissing) ...[ - const SizedBox(height: FaiSpace.md), - FaiBinaryRecovery( + const SizedBox(height: ChainSpace.md), + ChainBinaryRecovery( onLocated: () => _run( l.daemonActionStatus, - () => SystemActions.faiDaemon(['status']), + () => SystemActions.chainDaemon(['status']), ), ), ], if (_output != null) ...[ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Container( width: double.infinity, - padding: const EdgeInsets.all(FaiSpace.sm), + padding: const EdgeInsets.all(ChainSpace.sm), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: SelectableText( _output!, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurface, ), @@ -732,7 +732,7 @@ class _ModulesPanel extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final l = AppLocalizations.of(context)!; - return FaiCard( + return ChainCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -743,7 +743,7 @@ class _ModulesPanel extends StatelessWidget { size: 18, color: theme.colorScheme.onSurfaceVariant, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -767,17 +767,17 @@ class _ModulesPanel extends StatelessWidget { ], ), ), - FaiPill( + ChainPill( label: snapshot.moduleCount > 0 ? l.doctorPillLoaded : l.doctorPillEmpty, tone: snapshot.moduleCount > 0 - ? FaiPillTone.success - : FaiPillTone.neutral, + ? ChainPillTone.success + : ChainPillTone.neutral, ), ], ), - const Divider(height: FaiSpace.xl), + const Divider(height: ChainSpace.xl), Row( children: [ Icon( @@ -785,7 +785,7 @@ class _ModulesPanel extends StatelessWidget { size: 18, color: theme.colorScheme.onSurfaceVariant, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( snapshot.pendingApprovals == 0 ? l.doctorApprovalsNone @@ -794,9 +794,9 @@ class _ModulesPanel extends StatelessWidget { ), const Spacer(), if (snapshot.pendingApprovals > 0) - FaiPill( + ChainPill( label: l.doctorApprovalsAttentionPill, - tone: FaiPillTone.warning, + tone: ChainPillTone.warning, ), ], ), @@ -819,10 +819,10 @@ class _ServicesPanel extends StatelessWidget { // Wrap on narrow windows so the mono-spaced hint // (`add to ~/.chain/config.yaml under services:`) does not // overflow horizontally past the card's right edge. - return FaiCard( + return ChainCard( child: Wrap( - spacing: FaiSpace.sm, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.sm, + runSpacing: ChainSpace.xs, crossAxisAlignment: WrapCrossAlignment.center, children: [ Row( @@ -833,7 +833,7 @@ class _ServicesPanel extends StatelessWidget { size: 18, color: theme.colorScheme.onSurfaceVariant, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( l.doctorServicesEmpty, style: theme.textTheme.bodyMedium?.copyWith( @@ -844,7 +844,7 @@ class _ServicesPanel extends StatelessWidget { ), Text( l.doctorServicesEmptyHint, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurfaceVariant, ), @@ -853,7 +853,7 @@ class _ServicesPanel extends StatelessWidget { ), ); } - return FaiCard( + return ChainCard( padding: EdgeInsets.zero, child: Column( children: [ @@ -861,7 +861,7 @@ class _ServicesPanel extends StatelessWidget { if (i > 0) Divider(height: 1, color: theme.colorScheme.outlineVariant), Padding( - padding: const EdgeInsets.all(FaiSpace.lg), + padding: const EdgeInsets.all(ChainSpace.lg), child: Row( children: [ Icon( @@ -869,25 +869,25 @@ class _ServicesPanel extends StatelessWidget { size: 16, color: theme.colorScheme.primary, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( snapshot.services[i].name, style: theme.textTheme.titleSmall?.copyWith( fontWeight: FontWeight.w600, ), ), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), Text( snapshot.services[i].endpoint, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurfaceVariant, ), ), const Spacer(), for (final tag in snapshot.services[i].tags) ...[ - FaiPill(label: tag, tone: FaiPillTone.neutral), - const SizedBox(width: FaiSpace.xs), + ChainPill(label: tag, tone: ChainPillTone.neutral), + const SizedBox(width: ChainSpace.xs), ], ], ), @@ -917,7 +917,7 @@ class _UpdateBannerState extends State<_UpdateBanner> { _applying = true; _applyOutput = null; }); - final r = await SystemActions.faiUpdateApply(widget.status.channel); + final r = await SystemActions.chainUpdateApply(widget.status.channel); if (!mounted) return; setState(() { _applying = false; @@ -945,7 +945,7 @@ class _UpdateBannerState extends State<_UpdateBanner> { final body = available ? l.doctorUpdateBody(status.channel, status.latestVersion) : (status.reason ?? l.doctorUpdateUnreachableBody(status.channel)); - return FaiCard( + return ChainCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -953,7 +953,7 @@ class _UpdateBannerState extends State<_UpdateBanner> { crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(iconData, size: 20, color: iconColor), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -967,7 +967,7 @@ class _UpdateBannerState extends State<_UpdateBanner> { const SizedBox(height: 2), Text( body, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurfaceVariant, ), @@ -976,7 +976,7 @@ class _UpdateBannerState extends State<_UpdateBanner> { const SizedBox(height: 4), Text( l.doctorReleaseNotes(status.releaseNotesUrl!), - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.primary, ), @@ -999,16 +999,16 @@ class _UpdateBannerState extends State<_UpdateBanner> { _applying ? l.doctorApplying : l.doctorApplyUpdate, ), ), - const SizedBox(width: FaiSpace.sm), - FaiPill( + const SizedBox(width: ChainSpace.sm), + ChainPill( label: available ? l.doctorPillNew : l.doctorPillOffline, - tone: available ? FaiPillTone.success : FaiPillTone.neutral, + tone: available ? ChainPillTone.success : ChainPillTone.neutral, ), ], ), if (_applyOutput != null) ...[ - const SizedBox(height: FaiSpace.md), - FaiErrorBox(text: _applyOutput!, maxHeight: 240), + const SizedBox(height: ChainSpace.md), + ChainErrorBox(text: _applyOutput!, maxHeight: 240), ], ], ), diff --git a/lib/pages/federation.dart b/lib/pages/federation.dart index 3f744aa..b82baaf 100644 --- a/lib/pages/federation.dart +++ b/lib/pages/federation.dart @@ -103,7 +103,7 @@ class _FederationPageState extends State { tooltip: l.federationReloadTooltip, onPressed: _refresh, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), ], ), floatingActionButton: FloatingActionButton.extended( @@ -118,7 +118,7 @@ class _FederationPageState extends State { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.cloud_off_outlined, iconColor: theme.colorScheme.error, title: l.hubUnreachable, @@ -131,7 +131,7 @@ class _FederationPageState extends State { } final sats = snapshot.data ?? []; if (sats.isEmpty) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.hub_outlined, title: l.federationEmptyTitle, hint: l.federationEmptyHint, @@ -143,9 +143,9 @@ class _FederationPageState extends State { ); } return ListView.separated( - padding: const EdgeInsets.all(FaiSpace.xl), + padding: const EdgeInsets.all(ChainSpace.xl), itemCount: sats.length, - separatorBuilder: (_, _) => const SizedBox(height: FaiSpace.md), + separatorBuilder: (_, _) => const SizedBox(height: ChainSpace.md), itemBuilder: (context, i) => _SatelliteCard(satellite: sats[i]), ); }, @@ -162,19 +162,19 @@ class _SatelliteCard extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final l = AppLocalizations.of(context)!; - return FaiCard( + return ChainCard( accentTop: true, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - FaiPill( + ChainPill( label: l.federationPillConnected, - tone: FaiPillTone.success, + tone: ChainPillTone.success, icon: Icons.link, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( child: Text( satellite.displayName, @@ -183,19 +183,19 @@ class _SatelliteCard extends StatelessWidget { ), ), ), - FaiPill( + ChainPill( label: satellite.region.isEmpty ? l.federationRegionNone : satellite.region, - tone: FaiPillTone.neutral, + tone: ChainPillTone.neutral, icon: Icons.public, ), ], ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Wrap( - spacing: FaiSpace.md, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.md, + runSpacing: ChainSpace.xs, children: [ _meta(theme, 'v${satellite.hubVersion}'), _meta(theme, 'wire ${satellite.wireVersion}'), @@ -203,7 +203,7 @@ class _SatelliteCard extends StatelessWidget { ], ), if (satellite.capabilities.isNotEmpty) ...[ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( l.federationCapabilities(satellite.capabilities.length), style: theme.textTheme.labelSmall?.copyWith( @@ -213,13 +213,13 @@ class _SatelliteCard extends StatelessWidget { ), const SizedBox(height: 4), Wrap( - spacing: FaiSpace.xs, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.xs, + runSpacing: ChainSpace.xs, children: satellite.capabilities .map( - (c) => FaiPill( + (c) => ChainPill( label: c, - tone: FaiPillTone.accent, + tone: ChainPillTone.accent, icon: Icons.extension_outlined, ), ) @@ -233,7 +233,7 @@ class _SatelliteCard extends StatelessWidget { Widget _meta(ThemeData theme, String text) => Text( text, - style: FaiTheme.mono(size: 10, color: theme.colorScheme.onSurfaceVariant), + style: ChainTheme.mono(size: 10, color: theme.colorScheme.onSurfaceVariant), ); } @@ -261,11 +261,11 @@ class _EnrollmentDialog extends StatelessWidget { Text(l.federationTokenLabel, style: theme.textTheme.labelSmall), const SizedBox(height: 4), _CopyableBlock(text: enrollment.token, copiedMsg: l.federationCopied), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text(l.federationConfigLabel, style: theme.textTheme.labelSmall), const SizedBox(height: 4), _CopyableBlock(text: config, copiedMsg: l.federationCopied), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( l.federationEnrollmentHint, style: theme.textTheme.bodySmall?.copyWith( @@ -299,19 +299,19 @@ class _CopyableBlock extends StatelessWidget { Container( width: double.infinity, padding: const EdgeInsets.fromLTRB( - FaiSpace.md, - FaiSpace.md, + ChainSpace.md, + ChainSpace.md, 40, - FaiSpace.md, + ChainSpace.md, ), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: SelectableText( text, - style: FaiTheme.mono(size: 11, color: theme.colorScheme.onSurface), + style: ChainTheme.mono(size: 11, color: theme.colorScheme.onSurface), ), ), Positioned( diff --git a/lib/pages/modules.dart b/lib/pages/modules.dart index 88628db..24ccf40 100644 --- a/lib/pages/modules.dart +++ b/lib/pages/modules.dart @@ -48,7 +48,7 @@ class _ModulesPageState extends State { tooltip: AppLocalizations.of(context)!.modulesReloadTooltip, onPressed: _refresh, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), ], ), body: FutureBuilder>( @@ -59,7 +59,7 @@ class _ModulesPageState extends State { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.cloud_off_outlined, iconColor: Theme.of(context).colorScheme.error, title: l.hubUnreachable, @@ -72,22 +72,22 @@ class _ModulesPageState extends State { } final modules = snapshot.data ?? []; if (modules.isEmpty) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.extension_outlined, title: l.modulesNoneTitle, hint: l.modulesNoneHint, ); } return ListView( - padding: const EdgeInsets.all(FaiSpace.xl), + padding: const EdgeInsets.all(ChainSpace.xl), children: [ _RecentActivityPanel(future: _historyFuture), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), for (var i = 0; i < modules.length; i++) ...[ - if (i > 0) const SizedBox(height: FaiSpace.md), + if (i > 0) const SizedBox(height: ChainSpace.md), GestureDetector( onTap: () async { - final uninstalled = await FaiModuleSheet.show( + final uninstalled = await ChainModuleSheet.show( context, modules[i].name, ); @@ -116,7 +116,7 @@ class _ModuleCard extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - return FaiCard( + return ChainCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -128,15 +128,15 @@ class _ModuleCard extends StatelessWidget { fontWeight: FontWeight.w600, ), ), - const SizedBox(width: FaiSpace.sm), - FaiPill( + const SizedBox(width: ChainSpace.sm), + ChainPill( label: 'v${module.version}', - tone: FaiPillTone.neutral, + tone: ChainPillTone.neutral, monospace: true, ), ], ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -152,13 +152,13 @@ class _ModuleCard extends StatelessWidget { ), Expanded( child: Wrap( - spacing: FaiSpace.xs, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.xs, + runSpacing: ChainSpace.xs, children: module.capabilities .map( - (c) => FaiPill( + (c) => ChainPill( label: c, - tone: FaiPillTone.accent, + tone: ChainPillTone.accent, monospace: true, ), ) @@ -192,12 +192,12 @@ class _RecentActivityPanel extends StatelessWidget { if (events.isEmpty) return const SizedBox.shrink(); return Container( padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.md, - vertical: FaiSpace.sm, + horizontal: ChainSpace.md, + vertical: ChainSpace.sm, ), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Column( @@ -219,7 +219,7 @@ class _RecentActivityPanel extends StatelessWidget { fontSize: 10, ), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( AppLocalizations.of( context, @@ -230,7 +230,7 @@ class _RecentActivityPanel extends StatelessWidget { ), ], ), - const SizedBox(height: FaiSpace.xs), + const SizedBox(height: ChainSpace.xs), for (final e in events) _ActivityRow(event: e), ], ), @@ -248,7 +248,7 @@ class _ActivityRow extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final installed = event.type == 'module.installed'; - final accent = installed ? FaiColors.success : FaiColors.warning; + final accent = installed ? ChainColors.success : ChainColors.warning; return Padding( padding: const EdgeInsets.symmetric(vertical: 2), child: Row( @@ -263,7 +263,7 @@ class _ActivityRow extends StatelessWidget { width: 150, child: Text( _formatTimestamp(event.timestamp.toLocal()), - style: FaiTheme.mono( + style: ChainTheme.mono( size: 10, color: theme.colorScheme.onSurfaceVariant, ), diff --git a/lib/pages/store.dart b/lib/pages/store.dart index 935d75b..2614c35 100644 --- a/lib/pages/store.dart +++ b/lib/pages/store.dart @@ -203,10 +203,10 @@ class _StorePageState extends State { backgroundColor: theme.scaffoldBackgroundColor, appBar: AppBar( // titleSpacing matches the body's horizontal padding - // (FaiSpace.xl == 24) so the title's left edge sits + // (ChainSpace.xl == 24) so the title's left edge sits // flush with the cards below. Default 16 left a 8-px // gap that read as a misaligned heading. - titleSpacing: FaiSpace.xl, + titleSpacing: ChainSpace.xl, title: Text(l.searchGroupStore), actions: [ FutureBuilder>( @@ -215,7 +215,7 @@ class _StorePageState extends State { final raw = snap.data ?? const []; final categories = _categoriesIn(raw); return Padding( - padding: const EdgeInsets.only(right: FaiSpace.sm), + padding: const EdgeInsets.only(right: ChainSpace.sm), child: _CategoryDropdown( categories: categories, selected: _category, @@ -228,7 +228,7 @@ class _StorePageState extends State { }, ), Padding( - padding: const EdgeInsets.only(right: FaiSpace.sm), + padding: const EdgeInsets.only(right: ChainSpace.sm), child: _FilterButton( activeCount: _activeFilterCount(), onPressed: _openFilterDialog, @@ -243,7 +243,7 @@ class _StorePageState extends State { // to match the body's right-side padding. The icon // glyph itself ends up flush with the rightmost grid // card, not floating in space. - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), ], ), body: Column( @@ -255,9 +255,9 @@ class _StorePageState extends State { Expanded( child: Padding( padding: const EdgeInsets.fromLTRB( - FaiSpace.xl, - FaiSpace.lg, - FaiSpace.xl, + ChainSpace.xl, + ChainSpace.lg, + ChainSpace.xl, 0, ), child: FutureBuilder>( @@ -267,7 +267,7 @@ class _StorePageState extends State { return const Center(child: CircularProgressIndicator()); } if (snap.hasError) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.cloud_off_outlined, iconColor: theme.colorScheme.error, title: l.hubUnreachable, @@ -281,7 +281,7 @@ class _StorePageState extends State { final raw = snap.data ?? const []; final items = _applyAllFilters(raw); if (items.isEmpty) { - return FaiEmptyState( + return ChainEmptyState( icon: Icons.search_off, title: l.storeNoMatches, hint: l.storeNoMatchesHint, @@ -355,7 +355,7 @@ class _StorePageState extends State { error: _aiError, onClear: _clearQuery, ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (showToday) ...[ _StoreTodayHero( @@ -404,7 +404,7 @@ class _StorePageState extends State { recommendedAdding: _addingSources, onAddRecommended: _addRecommendedSource, ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (showStandaloneRecommended) ...[ _RecommendedSourcesStrip( @@ -412,7 +412,7 @@ class _StorePageState extends State { locale: _locale, onAdd: _addRecommendedSource, ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (showFeatured) ...[ _FeaturedStrip( @@ -421,7 +421,7 @@ class _StorePageState extends State { onTap: _openDetail, onInstall: _install, ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], _StoreGrid( items: items, @@ -430,7 +430,7 @@ class _StorePageState extends State { onTap: _openDetail, onInstall: _install, ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], ), ); @@ -450,8 +450,8 @@ class _StorePageState extends State { ), ), padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.xl, - vertical: FaiSpace.md, + horizontal: ChainSpace.xl, + vertical: ChainSpace.md, ), child: _AskBar( controller: _queryCtrl, @@ -564,7 +564,7 @@ class _StorePageState extends State { case TodayCta.none: return null; case TodayCta.openSettings: - return () => FaiSettingsDialog.show(context); + return () => ChainSettingsDialog.show(context); case TodayCta.filterCategory: if (story.ctaPayload.isEmpty) return null; return () { @@ -792,7 +792,7 @@ String _categoryDisplayName(BuildContext ctx, String wire) { /// Map a permission string (`net:api.example.com`, /// `fs.read:/etc/fai`, `env:OPENAI_API_KEY`, …) to a glyph the /// operator can read at a glance. Mirrors the table that used -/// to live in `fai_module_sheet.dart` so the Store detail-sheet +/// to live in `chain_module_sheet.dart` so the Store detail-sheet /// can render permissions in the same shape after the Modules /// page was dropped. IconData _iconForPermission(String permission) { @@ -850,12 +850,12 @@ class _AskBar extends StatelessWidget { final empty = value.text.trim().isEmpty; return Container( padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.md, - vertical: FaiSpace.sm, + horizontal: ChainSpace.md, + vertical: ChainSpace.sm, ), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Row( @@ -869,7 +869,7 @@ class _AskBar extends StatelessWidget { color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( // Cmd+Enter (or Ctrl+Enter on Linux) submits; // bare Enter inserts a newline so the operator @@ -928,7 +928,7 @@ class _AskBar extends StatelessWidget { tooltip: l.storeAskClear, onPressed: onClear, ), - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), FilledButton.icon( onPressed: thinking ? null : onSubmit, icon: thinking @@ -979,7 +979,7 @@ class _FilterButton extends StatelessWidget { children: [ Text(l.storeFilterLabel), if (activeCount > 0) ...[ - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1), decoration: BoxDecoration( @@ -1065,8 +1065,8 @@ class _FilterDialogState extends State<_FilterDialog> { String selected, void Function(String) onSelect, ) => Wrap( - spacing: FaiSpace.xs, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.xs, + runSpacing: ChainSpace.xs, children: [ for (final i in items) _ChoiceChip( @@ -1079,7 +1079,7 @@ class _FilterDialogState extends State<_FilterDialog> { return AlertDialog( title: Text(l.storeFilterLabel), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), ), content: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 460), @@ -1098,7 +1098,7 @@ class _FilterDialogState extends State<_FilterDialog> { _status, (v) => setState(() => _status = v), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), header(l.storeFilterSourceGroup), chipRow( [ @@ -1111,11 +1111,11 @@ class _FilterDialogState extends State<_FilterDialog> { _source, (v) => setState(() => _source = v), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), header(l.storeFilterInstalledGroup), Wrap( - spacing: FaiSpace.xs, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.xs, + runSpacing: ChainSpace.xs, children: [ FilterChip( label: Text(l.storeInstallableOnly), @@ -1208,10 +1208,10 @@ class _AiAnswerCard extends StatelessWidget { : theme.colorScheme.primary; 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.md), + borderRadius: BorderRadius.circular(ChainRadius.md), border: Border.all(color: accent.withValues(alpha: 0.3)), ), child: Column( @@ -1220,7 +1220,7 @@ class _AiAnswerCard extends StatelessWidget { Row( children: [ Icon(Icons.auto_awesome, size: 14, color: accent), - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), Text( l.storeAskAnswerLabel, style: theme.textTheme.labelSmall?.copyWith( @@ -1238,7 +1238,7 @@ class _AiAnswerCard extends StatelessWidget { ), ], ), - const SizedBox(height: FaiSpace.xs), + const SizedBox(height: ChainSpace.xs), if (thinking) Row( children: [ @@ -1247,7 +1247,7 @@ class _AiAnswerCard extends StatelessWidget { height: 14, child: CircularProgressIndicator(strokeWidth: 2), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( l.storeAskAiThinking, style: theme.textTheme.bodyMedium?.copyWith( @@ -1269,7 +1269,7 @@ class _AiAnswerCard extends StatelessWidget { style: theme.textTheme.bodyMedium, ), if (matches.isNotEmpty) ...[ - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Text( l.storeAskAnswerWhy, style: theme.textTheme.labelSmall?.copyWith( @@ -1296,12 +1296,12 @@ class _AiAnswerCard extends StatelessWidget { ), Expanded( child: Wrap( - spacing: FaiSpace.xs, + spacing: ChainSpace.xs, crossAxisAlignment: WrapCrossAlignment.center, children: [ Text( m.name, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 12, weight: FontWeight.w600, color: theme.colorScheme.onSurface, @@ -1365,11 +1365,11 @@ class _CategoryDropdown extends StatelessWidget { ], child: Container( padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.sm, + horizontal: ChainSpace.sm, vertical: 6, ), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Row( @@ -1475,7 +1475,7 @@ class _ChoiceChip extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.only(right: FaiSpace.xs), + padding: const EdgeInsets.only(right: ChainSpace.xs), child: ChoiceChip( label: Text(label), selected: selected, @@ -1526,8 +1526,8 @@ class _StoreGrid extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: cols, - mainAxisSpacing: FaiSpace.md, - crossAxisSpacing: FaiSpace.md, + mainAxisSpacing: ChainSpace.md, + crossAxisSpacing: ChainSpace.md, mainAxisExtent: 168, ), itemCount: items.length, @@ -1572,7 +1572,7 @@ class _FeaturedStrip extends StatelessWidget { children: [ Row( children: [ - Icon(Icons.star, size: 14, color: FaiColors.success), + Icon(Icons.star, size: 14, color: ChainColors.success), const SizedBox(width: 6), Text( l.storeFeatured, @@ -1582,7 +1582,7 @@ class _FeaturedStrip extends StatelessWidget { fontSize: 10, ), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( l.storeFeaturedHint, style: theme.textTheme.bodySmall?.copyWith( @@ -1591,7 +1591,7 @@ class _FeaturedStrip extends StatelessWidget { ), ], ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), SizedBox( // Tiles render larger than regular grid cards // (which are 168 × ~360) so the editorial picks @@ -1601,7 +1601,7 @@ class _FeaturedStrip extends StatelessWidget { child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: items.length, - separatorBuilder: (_, _) => const SizedBox(width: FaiSpace.md), + separatorBuilder: (_, _) => const SizedBox(width: ChainSpace.md), itemBuilder: (context, i) { final item = items[i]; return SizedBox( @@ -1644,14 +1644,14 @@ class _FeaturedTile extends StatelessWidget { final installable = item.status == 'published' || item.status == 'alpha'; return Material( color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), child: InkWell( onTap: onTap, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), child: Container( - padding: const EdgeInsets.all(FaiSpace.lg), + padding: const EdgeInsets.all(ChainSpace.lg), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), gradient: LinearGradient( colors: [ theme.colorScheme.primary.withValues(alpha: 0.14), @@ -1677,7 +1677,7 @@ class _FeaturedTile extends StatelessWidget { color: theme.colorScheme.primary, ), ), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1702,10 +1702,10 @@ class _FeaturedTile extends StatelessWidget { ], ), ), - Icon(Icons.star, size: 18, color: FaiColors.success), + Icon(Icons.star, size: 18, color: ChainColors.success), ], ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1719,7 +1719,7 @@ class _FeaturedTile extends StatelessWidget { // rewrites it on the fly. Padding( padding: const EdgeInsets.only(bottom: 4), - child: FaiEnBadge.forContext(context), + child: ChainEnBadge.forContext(context), ), Expanded( child: Text( @@ -1732,7 +1732,7 @@ class _FeaturedTile extends StatelessWidget { ], ), ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Row( children: [ if (item.bestVersion.isNotEmpty) @@ -1745,25 +1745,25 @@ class _FeaturedTile extends StatelessWidget { message: item.isFederated ? l.storeAdvisoryVersionTooltip : '', - child: FaiPill( + child: ChainPill( label: item.isFederated ? '~v${item.bestVersion}' : 'v${item.bestVersion}', - tone: FaiPillTone.neutral, + tone: ChainPillTone.neutral, monospace: true, ), ), - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), if (item.status.isNotEmpty) - FaiPill( + ChainPill( label: _statusDisplayName(context, item.status), tone: _toneForStatus(item.status), ), const Spacer(), if (item.installed) - FaiPill( + ChainPill( label: l.storePillInstalled, - tone: FaiPillTone.success, + tone: ChainPillTone.success, icon: Icons.check, ) else if (installable) @@ -1777,9 +1777,9 @@ class _FeaturedTile extends StatelessWidget { // wire value). Without an explicit pill here the // featured tile would just go silent, leaving the // operator wondering why the card has no action. - FaiPill( + ChainPill( label: l.storePillComingSoon, - tone: FaiPillTone.neutral, + tone: ChainPillTone.neutral, icon: Icons.schedule, ), ], @@ -1856,23 +1856,23 @@ class _StoreCardState extends State<_StoreCard> { onEnter: (_) => setState(() => _hovered = true), onExit: (_) => setState(() => _hovered = false), child: AnimatedContainer( - duration: FaiMotion.base, - curve: FaiMotion.easing, + duration: ChainMotion.base, + curve: ChainMotion.easing, transform: Matrix4.translationValues(0, _hovered ? -2 : 0, 0), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), boxShadow: _hovered - ? FaiElevation.medium(theme.brightness) - : FaiElevation.low(theme.brightness), + ? ChainElevation.medium(theme.brightness) + : ChainElevation.low(theme.brightness), ), child: Material( color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), child: InkWell( onTap: onTap, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), child: Padding( - padding: const EdgeInsets.all(FaiSpace.md), + padding: const EdgeInsets.all(ChainSpace.md), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1889,7 +1889,7 @@ class _StoreCardState extends State<_StoreCard> { color: theme.colorScheme.primary, ), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1924,26 +1924,26 @@ class _StoreCardState extends State<_StoreCard> { installedVersion ?? '?', item.bestVersion, ), - child: FaiPill( + child: ChainPill( label: l.storePillUpdate, - tone: FaiPillTone.warning, + tone: ChainPillTone.warning, icon: Icons.system_update_alt, ), ) else if (item.installed) - FaiPill( + ChainPill( label: l.storePillInstalled, - tone: FaiPillTone.success, + tone: ChainPillTone.success, icon: Icons.check, ) else if (item.status.isNotEmpty) - FaiPill( + ChainPill( label: _statusDisplayName(context, item.status), tone: _toneForStatus(item.status), ), ], ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Expanded( child: Text( tagline.isEmpty ? l.storeNoTagline : tagline, @@ -1952,7 +1952,7 @@ class _StoreCardState extends State<_StoreCard> { overflow: TextOverflow.ellipsis, ), ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Row( children: [ if (item.bestVersion.isNotEmpty) ...[ @@ -1960,15 +1960,15 @@ class _StoreCardState extends State<_StoreCard> { message: item.isFederated ? l.storeAdvisoryVersionTooltip : '', - child: FaiPill( + child: ChainPill( label: item.isFederated ? '~v${item.bestVersion}' : 'v${item.bestVersion}', - tone: FaiPillTone.neutral, + tone: ChainPillTone.neutral, monospace: true, ), ), - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), ], _ProvenancePill(item: item), const Spacer(), @@ -2039,7 +2039,7 @@ class _StoreDetailSheet extends StatefulWidget { backgroundColor: Theme.of(context).colorScheme.surfaceContainer, elevation: 8, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(FaiRadius.md)), + borderRadius: BorderRadius.vertical(top: Radius.circular(ChainRadius.md)), ), builder: (_) => _StoreDetailSheet(item: item, locale: locale), ); @@ -2248,7 +2248,7 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { mainAxisSize: MainAxisSize.min, children: [ Padding( - padding: const EdgeInsets.symmetric(vertical: FaiSpace.sm), + padding: const EdgeInsets.symmetric(vertical: ChainSpace.sm), child: Container( width: 40, height: 4, @@ -2261,10 +2261,10 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { Flexible( child: SingleChildScrollView( padding: const EdgeInsets.fromLTRB( - FaiSpace.xxl, - FaiSpace.md, - FaiSpace.xxl, - FaiSpace.xxl, + ChainSpace.xxl, + ChainSpace.md, + ChainSpace.xxl, + ChainSpace.xxl, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -2278,7 +2278,7 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { category: item.category, radius: 28, ), - const SizedBox(width: FaiSpace.lg), + const SizedBox(width: ChainSpace.lg), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -2291,7 +2291,7 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { ), const SizedBox(height: 2), Wrap( - spacing: FaiSpace.xs, + spacing: ChainSpace.xs, runSpacing: 4, children: [ if (item.bestVersion.isNotEmpty) @@ -2299,16 +2299,16 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { message: item.isFederated ? l.storeAdvisoryVersionTooltip : '', - child: FaiPill( + child: ChainPill( label: item.isFederated ? '~v${item.bestVersion}' : 'v${item.bestVersion}', - tone: FaiPillTone.neutral, + tone: ChainPillTone.neutral, monospace: true, ), ), if (item.status.isNotEmpty) - FaiPill( + ChainPill( label: _statusDisplayName( context, item.status, @@ -2316,23 +2316,23 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { tone: _toneForStatus(item.status), ), if (item.category.isNotEmpty) - FaiPill( + ChainPill( label: _categoryDisplayName( context, item.category, ), - tone: FaiPillTone.neutral, + tone: ChainPillTone.neutral, ), if (item.installed) - FaiPill( + ChainPill( label: l.storePillInstalled, - tone: FaiPillTone.success, + tone: ChainPillTone.success, icon: Icons.check, ), if (item.license.isNotEmpty) - FaiPill( + ChainPill( label: item.license, - tone: FaiPillTone.neutral, + tone: ChainPillTone.neutral, ), ], ), @@ -2344,7 +2344,7 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { // does not host its own toggle anymore. ], ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), if (tagline.isNotEmpty) ...[ Text( tagline, @@ -2352,7 +2352,7 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (description.isNotEmpty) ...[ if (item.isFederated) ...[ @@ -2361,61 +2361,61 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { // localisation pipeline — flag it. Padding( padding: const EdgeInsets.only(bottom: 6), - child: FaiEnBadge.forContext(context), + child: ChainEnBadge.forContext(context), ), ], SelectableText( description, style: theme.textTheme.bodyMedium, ), - const SizedBox(height: FaiSpace.xl), + const SizedBox(height: ChainSpace.xl), ], if (item.tags.isNotEmpty) ...[ _SectionHeader(l.storeSectionTags), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Wrap( - spacing: FaiSpace.xs, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.xs, + runSpacing: ChainSpace.xs, children: [ for (final t in item.tags) - FaiPill(label: t, tone: FaiPillTone.neutral), + ChainPill(label: t, tone: ChainPillTone.neutral), ], ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (item.requiresCapabilities.isNotEmpty) ...[ _SectionHeader(l.storeSectionRequiredCapabilities), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Wrap( - spacing: FaiSpace.xs, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.xs, + runSpacing: ChainSpace.xs, children: [ for (final c in item.requiresCapabilities) - FaiPill( + ChainPill( label: c, - tone: FaiPillTone.accent, + tone: ChainPillTone.accent, monospace: true, ), ], ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (item.requiresServices.isNotEmpty) ...[ _SectionHeader(l.storeSectionRequiredHostServices), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Wrap( - spacing: FaiSpace.xs, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.xs, + runSpacing: ChainSpace.xs, children: [ for (final s in item.requiresServices) - FaiPill( + ChainPill( label: s, - tone: FaiPillTone.warning, + tone: ChainPillTone.warning, monospace: true, ), ], ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], // Live-from-hub sections for installed // modules. Folded in here in v0.35.0 when the @@ -2426,7 +2426,7 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { // about a module. if (item.installed && _moduleDetail != null) ...[ _SectionHeader(l.storeSectionPermissions), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), if (_moduleDetail!.permissions.isEmpty) Text( l.storeSectionPermissionsNone, @@ -2449,10 +2449,10 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { 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, ), @@ -2462,53 +2462,53 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { ), ], ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), _SectionHeader(l.storeSectionDirectory), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), SelectableText( _moduleDetail!.directory, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (item.screenshotUrls.isNotEmpty) ...[ _SectionHeader(l.storeSectionScreenshots), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), SizedBox( height: 220, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: item.screenshotUrls.length, separatorBuilder: (_, _) => - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), itemBuilder: (context, i) => _Screenshot(url: item.screenshotUrls[i]), ), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (_docsResult?.state == 'found') ...[ _SectionHeader(l.storeSectionDocumentation), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), _DocsPanel(text: _docsResult!.text), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (item.repository.isNotEmpty) ...[ _SectionHeader(l.storeSectionSource), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), InkWell( onTap: _openRepository, child: Row( children: [ const Icon(Icons.open_in_new, size: 14), - const SizedBox(width: FaiSpace.xs), + const SizedBox(width: ChainSpace.xs), Flexible( child: Text( item.repository, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.primary, ), @@ -2518,24 +2518,24 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { ], ), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), ], if (_toast != null) ...[ 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), border: Border.all( color: theme.colorScheme.outlineVariant, ), ), child: SelectableText( _toast!, - style: FaiTheme.mono(size: 11), + style: ChainTheme.mono(size: 11), ), ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), ], Row( children: [ @@ -2585,12 +2585,12 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { Expanded( child: Container( padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.md, - vertical: FaiSpace.sm, + horizontal: ChainSpace.md, + vertical: ChainSpace.sm, ), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all( color: theme.colorScheme.outlineVariant, ), @@ -2602,7 +2602,7 @@ class _StoreDetailSheetState extends State<_StoreDetailSheet> { size: 16, color: theme.colorScheme.onSurfaceVariant, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( child: Text( l.storeNotInstallableInline(item.status), @@ -2674,7 +2674,7 @@ class _InstallProgressDialogState extends State<_InstallProgressDialog> { return AlertDialog( title: Text(l.storeInstallProgressTitle(widget.item.name)), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), ), content: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 480, maxHeight: 360), @@ -2685,9 +2685,9 @@ class _InstallProgressDialogState extends State<_InstallProgressDialog> { return Column( mainAxisSize: MainAxisSize.min, children: [ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), const CircularProgressIndicator(), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( l.storeInstallProgressBody, style: theme.textTheme.bodySmall?.copyWith( @@ -2708,8 +2708,8 @@ class _InstallProgressDialogState extends State<_InstallProgressDialog> { color: theme.colorScheme.error, size: 32, ), - const SizedBox(height: FaiSpace.md), - FaiErrorBox( + const SizedBox(height: ChainSpace.md), + ChainErrorBox( error: snap.error, isError: true, maxHeight: 200, @@ -2725,9 +2725,9 @@ class _InstallProgressDialogState extends State<_InstallProgressDialog> { Icon( Icons.check_circle_outline, size: 32, - color: FaiColors.success, + color: ChainColors.success, ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( l.storeInstalledToast(r.name, r.version), style: theme.textTheme.titleMedium, @@ -2747,16 +2747,16 @@ class _InstallProgressDialogState extends State<_InstallProgressDialog> { } } -FaiPillTone _toneForStatus(String s) { +ChainPillTone _toneForStatus(String s) { switch (s) { case 'published': - return FaiPillTone.success; + return ChainPillTone.success; case 'alpha': - return FaiPillTone.warning; + return ChainPillTone.warning; case 'planned': - return FaiPillTone.neutral; + return ChainPillTone.neutral; default: - return FaiPillTone.neutral; + return ChainPillTone.neutral; } } @@ -2807,10 +2807,10 @@ class _DocsPanel extends StatelessWidget { // scrollbars fighting for the wheel. 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: Markdown( @@ -2822,7 +2822,7 @@ class _DocsPanel extends StatelessWidget { if (href == null || href.isEmpty) return; await SystemActions.openInOs(href); }, - styleSheet: FaiTheme.markdownStyle(theme), + styleSheet: ChainTheme.markdownStyle(theme), ), ); } @@ -2912,12 +2912,12 @@ class _Screenshot extends StatelessWidget { final theme = Theme.of(context); return Material( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), child: InkWell( onTap: () => SystemActions.openInOs(url), - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), child: ClipRRect( - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), child: SizedBox( width: 320, child: Image.network( @@ -2954,7 +2954,7 @@ class _ScreenshotPlaceholder extends StatelessWidget { color: theme.colorScheme.surfaceContainer, child: Center( child: Padding( - padding: const EdgeInsets.all(FaiSpace.md), + padding: const EdgeInsets.all(ChainSpace.md), child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -2974,7 +2974,7 @@ class _ScreenshotPlaceholder extends StatelessWidget { const SizedBox(height: 2), Text( detail, - style: FaiTheme.mono( + style: ChainTheme.mono( size: 9, color: theme.colorScheme.onSurfaceVariant, ), @@ -3186,13 +3186,13 @@ class _StoreTodayHero extends StatelessWidget { // row) without leaving empty space on shorter slides. constraints: const BoxConstraints(minHeight: 240), padding: const EdgeInsets.fromLTRB( - FaiSpace.xl, - FaiSpace.lg, - FaiSpace.md, - FaiSpace.lg, + ChainSpace.xl, + ChainSpace.lg, + ChainSpace.md, + ChainSpace.lg, ), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), gradient: LinearGradient( colors: [ theme.colorScheme.primary.withValues(alpha: 0.16), @@ -3211,24 +3211,24 @@ class _StoreTodayHero extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - padding: const EdgeInsets.all(FaiSpace.md), + padding: const EdgeInsets.all(ChainSpace.md), decoration: BoxDecoration( color: theme.colorScheme.primary.withValues(alpha: 0.14), shape: BoxShape.circle, ), child: Icon(story.icon, size: 24, color: theme.colorScheme.primary), ), - const SizedBox(width: FaiSpace.lg), + const SizedBox(width: ChainSpace.lg), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Wrap( - spacing: FaiSpace.sm, + spacing: ChainSpace.sm, runSpacing: 4, crossAxisAlignment: WrapCrossAlignment.center, children: [ - FaiPill(label: l.storeTodayBadge, tone: FaiPillTone.accent), + ChainPill(label: l.storeTodayBadge, tone: ChainPillTone.accent), Text( badge, style: theme.textTheme.labelSmall?.copyWith( @@ -3247,7 +3247,7 @@ class _StoreTodayHero extends StatelessWidget { ), ], ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Text( title, maxLines: 3, @@ -3257,7 +3257,7 @@ class _StoreTodayHero extends StatelessWidget { height: 1.15, ), ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Text( body, maxLines: 6, @@ -3268,10 +3268,10 @@ class _StoreTodayHero extends StatelessWidget { ), ), if (onCta != null || recommendedSources.isNotEmpty) ...[ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Wrap( - spacing: FaiSpace.sm, - runSpacing: FaiSpace.xs, + spacing: ChainSpace.sm, + runSpacing: ChainSpace.xs, crossAxisAlignment: WrapCrossAlignment.center, children: [ if (onCta != null) @@ -3321,7 +3321,7 @@ class _StoreTodayHero extends StatelessWidget { onPressed: onDismiss, ), if (totalCount > 1) ...[ - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Row( mainAxisSize: MainAxisSize.min, children: [ @@ -3443,10 +3443,10 @@ class _RecommendedSourcesStrip 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.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Column( @@ -3459,7 +3459,7 @@ class _RecommendedSourcesStrip extends StatelessWidget { size: 18, color: theme.colorScheme.primary, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Text( l.storeRecommendedTitle, style: theme.textTheme.titleSmall?.copyWith( @@ -3475,10 +3475,10 @@ class _RecommendedSourcesStrip extends StatelessWidget { color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Wrap( - spacing: FaiSpace.md, - runSpacing: FaiSpace.sm, + spacing: ChainSpace.md, + runSpacing: ChainSpace.sm, children: [ for (final s in _kRecommendedSources) _RecommendedSourceCard( @@ -3516,16 +3516,16 @@ class _RecommendedSourceCard extends StatelessWidget { return SizedBox( width: 320, child: 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), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Row( children: [ Icon(source.icon, size: 18, color: theme.colorScheme.primary), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -3549,7 +3549,7 @@ class _RecommendedSourceCard extends StatelessWidget { ], ), ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Tooltip( message: busy ? l.storeRecommendedAdding @@ -3589,23 +3589,23 @@ class _ProvenancePill extends StatelessWidget { final source = _sourceForItem(item); switch (source) { case 'mcp': - return FaiPill( + return ChainPill( label: l.storeProvenanceMcp(item.provider), - tone: FaiPillTone.warning, + tone: ChainPillTone.warning, monospace: true, icon: Icons.hub_outlined, ); case 'n8n': - return FaiPill( + return ChainPill( label: l.storeProvenanceN8n(item.provider), - tone: FaiPillTone.accent, + tone: ChainPillTone.accent, monospace: true, icon: Icons.account_tree_outlined, ); case 'temporal': - return FaiPill( + return ChainPill( label: l.storeProvenanceTemporal(item.provider), - tone: FaiPillTone.accent, + tone: ChainPillTone.accent, monospace: true, icon: Icons.timer_outlined, ); @@ -3658,7 +3658,7 @@ class _StoreUninstallVersionPickerDialogState mainAxisSize: MainAxisSize.min, children: [ Text(l.uninstallVersionPickerBody(widget.moduleName)), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), RadioGroup( groupValue: _picked, onChanged: (val) { diff --git a/lib/pages/welcome.dart b/lib/pages/welcome.dart index 8262f5a..14dd4f2 100644 --- a/lib/pages/welcome.dart +++ b/lib/pages/welcome.dart @@ -35,14 +35,14 @@ class WelcomePage extends StatelessWidget { final hubDown = StudioShellState.of(context)?.connected == false; return Scaffold( backgroundColor: theme.scaffoldBackgroundColor, - appBar: AppBar(titleSpacing: FaiSpace.xl, title: Text(l.navWelcome)), + appBar: AppBar(titleSpacing: ChainSpace.xl, title: Text(l.navWelcome)), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.fromLTRB( - FaiSpace.xl, - FaiSpace.lg, - FaiSpace.xl, - FaiSpace.xxl, + ChainSpace.xl, + ChainSpace.lg, + ChainSpace.xl, + ChainSpace.xxl, ), // Center the 960-px content column on wide windows // so the right margin matches the left rather than @@ -59,24 +59,24 @@ class WelcomePage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const _Hero(), - const SizedBox(height: FaiSpace.xxl), + const SizedBox(height: ChainSpace.xxl), // When the hub is down the onboarding checklist // would render dead (all-unchecked, nothing to // probe). Lead with a "start the hub" card // instead; restore the checklist once connected. if (hubDown) ...[ const _HubDownHero(), - const SizedBox(height: FaiSpace.xxl), + const SizedBox(height: ChainSpace.xxl), ] else const _OnboardingChecklist(), const _PillarRow(), - const SizedBox(height: FaiSpace.xxl), + const SizedBox(height: ChainSpace.xxl), const _SectionLabel(textKey: _SectionLabelKey.trust), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), const _TrustPosture(), - const SizedBox(height: FaiSpace.xxl), + const SizedBox(height: ChainSpace.xxl), const _SectionLabel(textKey: _SectionLabelKey.docs), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), const _DocsRow(), ], ), @@ -100,9 +100,9 @@ class _Hero extends StatelessWidget { final l = AppLocalizations.of(context)!; return Container( width: double.infinity, - padding: const EdgeInsets.all(FaiSpace.xxl), + padding: const EdgeInsets.all(ChainSpace.xxl), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), gradient: LinearGradient( colors: [ theme.colorScheme.primary.withValues(alpha: 0.18), @@ -123,7 +123,7 @@ class _Hero extends StatelessWidget { Row( children: [ Container( - padding: const EdgeInsets.all(FaiSpace.md), + padding: const EdgeInsets.all(ChainSpace.md), decoration: BoxDecoration( color: theme.colorScheme.primary.withValues(alpha: 0.14), shape: BoxShape.circle, @@ -134,7 +134,7 @@ class _Hero extends StatelessWidget { color: theme.colorScheme.primary, ), ), - const SizedBox(width: FaiSpace.lg), + const SizedBox(width: ChainSpace.lg), Expanded( child: Text( l.welcomeHeroTitle, @@ -145,7 +145,7 @@ class _Hero extends StatelessWidget { ), ], ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), Text( l.welcomeHeroSubtitle, style: theme.textTheme.titleMedium?.copyWith( @@ -189,13 +189,13 @@ class _HubDownHeroState extends State<_HubDownHero> { final l = AppLocalizations.of(context)!; // No `fai` binary → "Start hub" cannot work; lead the // operator to the install guide instead. - final canStart = SystemActions.faiBinaryExists(); + final canStart = SystemActions.chainBinaryExists(); return Container( width: double.infinity, - padding: const EdgeInsets.all(FaiSpace.xl), + padding: const EdgeInsets.all(ChainSpace.xl), decoration: BoxDecoration( color: theme.colorScheme.errorContainer.withValues(alpha: 0.35), - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), border: Border.all( color: theme.colorScheme.error.withValues(alpha: 0.4), ), @@ -210,7 +210,7 @@ class _HubDownHeroState extends State<_HubDownHero> { size: 24, color: theme.colorScheme.error, ), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), Expanded( child: Text( l.welcomeHubDownTitle, @@ -221,7 +221,7 @@ class _HubDownHeroState extends State<_HubDownHero> { ), ], ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( l.welcomeHubDownBody, style: theme.textTheme.bodyMedium?.copyWith( @@ -229,15 +229,15 @@ class _HubDownHeroState extends State<_HubDownHero> { height: 1.45, ), ), - const SizedBox(height: FaiSpace.xs), + const SizedBox(height: ChainSpace.xs), Text( l.welcomeHubDownEndpoint(HubService.instance.endpointLabel), - style: FaiTheme.mono( + style: ChainTheme.mono( size: 11, color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.lg), + const SizedBox(height: ChainSpace.lg), if (canStart) FilledButton.icon( onPressed: _starting ? null : _start, @@ -253,8 +253,8 @@ class _HubDownHeroState extends State<_HubDownHero> { ), ) else - const FaiBinaryRecovery(), - const SizedBox(height: FaiSpace.sm), + const ChainBinaryRecovery(), + const SizedBox(height: ChainSpace.sm), TextButton.icon( icon: const Icon(Icons.open_in_new, size: 16), label: Text(l.welcomeHubDownDocsLink), @@ -323,7 +323,7 @@ class _PillarRow extends StatelessWidget { children: [ for (final p in pillars) ...[ _Pillar(icon: p.$1, title: p.$2, body: p.$3), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), ], ], ); @@ -339,7 +339,7 @@ class _PillarRow extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ for (var i = 0; i < pillars.length; i++) ...[ - if (i > 0) const SizedBox(width: FaiSpace.md), + if (i > 0) const SizedBox(width: ChainSpace.md), Expanded( child: _Pillar( icon: pillars[i].$1, @@ -367,24 +367,24 @@ class _Pillar extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); return Container( - padding: const EdgeInsets.all(FaiSpace.lg), + padding: const EdgeInsets.all(ChainSpace.lg), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 28, color: theme.colorScheme.primary), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Text( title, style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w700, ), ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), Text( body, style: theme.textTheme.bodyMedium?.copyWith( @@ -427,7 +427,7 @@ class _TrustPosture extends StatelessWidget { return Column( children: [ for (var i = 0; i < rows.length; i++) ...[ - if (i > 0) const SizedBox(height: FaiSpace.sm), + if (i > 0) const SizedBox(height: ChainSpace.sm), _TrustRow(icon: rows[i].$1, title: rows[i].$2, body: rows[i].$3), ], ], @@ -450,17 +450,17 @@ class _TrustRow extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); return Container( - padding: const EdgeInsets.all(FaiSpace.lg), + padding: const EdgeInsets.all(ChainSpace.lg), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 22, color: theme.colorScheme.primary), - const SizedBox(width: FaiSpace.lg), + const SizedBox(width: ChainSpace.lg), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -601,13 +601,13 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> { _aiOk, l.welcomeChecklistAi, l.welcomeChecklistAiHint, - () => FaiSettingsDialog.show(context), + () => ChainSettingsDialog.show(context), ), ( _mcpOk, l.welcomeChecklistMcp, l.welcomeChecklistMcpHint, - () => FaiSettingsDialog.show(context), + () => ChainSettingsDialog.show(context), ), ( _moduleOk, @@ -623,7 +623,7 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> { ), ]; return Padding( - padding: const EdgeInsets.only(bottom: FaiSpace.xxl), + padding: const EdgeInsets.only(bottom: ChainSpace.xxl), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -661,11 +661,11 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> { color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Container( decoration: BoxDecoration( color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Column( @@ -684,7 +684,7 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> { ), ), if (allDone) ...[ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), _AllDoneCelebration(onDismiss: _dismiss), ], ], @@ -707,19 +707,19 @@ class _AllDoneCelebration extends StatelessWidget { final theme = Theme.of(context); final l = AppLocalizations.of(context)!; return Container( - padding: const EdgeInsets.all(FaiSpace.lg), + padding: const EdgeInsets.all(ChainSpace.lg), decoration: BoxDecoration( - color: FaiColors.success.withValues(alpha: 0.08), - borderRadius: BorderRadius.circular(FaiRadius.md), - border: Border.all(color: FaiColors.success.withValues(alpha: 0.35)), + color: ChainColors.success.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(ChainRadius.md), + border: Border.all(color: ChainColors.success.withValues(alpha: 0.35)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Icon(Icons.celebration_outlined, color: FaiColors.success), - const SizedBox(width: FaiSpace.sm), + Icon(Icons.celebration_outlined, color: ChainColors.success), + const SizedBox(width: ChainSpace.sm), Expanded( child: Text( l.welcomeChecklistAllSetTitle, @@ -741,7 +741,7 @@ class _AllDoneCelebration extends StatelessWidget { color: theme.colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), _NextStepRow( icon: Icons.timeline_outlined, title: l.welcomeChecklistNextAuditTitle, @@ -797,12 +797,12 @@ class _NextStepRow extends StatelessWidget { final theme = Theme.of(context); final canActivate = doc != null && onOpenDoc != null; return Padding( - padding: const EdgeInsets.only(bottom: FaiSpace.sm), + padding: const EdgeInsets.only(bottom: ChainSpace.sm), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 18, color: theme.colorScheme.primary), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -823,7 +823,7 @@ class _NextStepRow extends StatelessWidget { ], ), ), - const SizedBox(width: FaiSpace.md), + const SizedBox(width: ChainSpace.md), OutlinedButton( onPressed: canActivate ? () => onOpenDoc!(doc!) : null, child: Text(buttonLabel), @@ -856,16 +856,16 @@ class _ChecklistRow extends StatelessWidget { final theme = Theme.of(context); final l = AppLocalizations.of(context)!; final row = Padding( - padding: const EdgeInsets.all(FaiSpace.lg), + padding: const EdgeInsets.all(ChainSpace.lg), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon( done ? Icons.check_circle : Icons.radio_button_unchecked, size: 20, - color: done ? FaiColors.success : theme.colorScheme.outline, + color: done ? ChainColors.success : theme.colorScheme.outline, ), - const SizedBox(width: FaiSpace.lg), + const SizedBox(width: ChainSpace.lg), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -890,10 +890,10 @@ class _ChecklistRow extends StatelessWidget { ], ), ), - const SizedBox(width: FaiSpace.md), - FaiPill( + const SizedBox(width: ChainSpace.md), + ChainPill( label: done ? l.welcomeChecklistDone : l.welcomeChecklistTodo, - tone: done ? FaiPillTone.success : FaiPillTone.neutral, + tone: done ? ChainPillTone.success : ChainPillTone.neutral, ), ], ), @@ -985,7 +985,7 @@ class _DocsRow extends StatelessWidget { color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), LayoutBuilder( builder: (context, constraints) { final twoCols = constraints.maxWidth >= 640; @@ -997,16 +997,16 @@ class _DocsRow extends StatelessWidget { return Column( children: [ for (var i = 0; i < _kDocs.length; i++) ...[ - if (i > 0) const SizedBox(height: FaiSpace.md), + if (i > 0) const SizedBox(height: ChainSpace.md), _DocCard(entry: _kDocs[i]), ], ], ); } - final cardWidth = (constraints.maxWidth - FaiSpace.md) / 2; + final cardWidth = (constraints.maxWidth - ChainSpace.md) / 2; return Wrap( - spacing: FaiSpace.md, - runSpacing: FaiSpace.md, + spacing: ChainSpace.md, + runSpacing: ChainSpace.md, children: [ for (final d in _kDocs) SizedBox( @@ -1045,30 +1045,30 @@ class _DocCardState extends State<_DocCard> { onEnter: (_) => setState(() => _hovered = true), onExit: (_) => setState(() => _hovered = false), child: AnimatedContainer( - duration: FaiMotion.base, - curve: FaiMotion.easing, + duration: ChainMotion.base, + curve: ChainMotion.easing, transform: Matrix4.translationValues(0, _hovered ? -2 : 0, 0), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(FaiRadius.md), - boxShadow: _hovered ? FaiElevation.medium(theme.brightness) : null, + borderRadius: BorderRadius.circular(ChainRadius.md), + boxShadow: _hovered ? ChainElevation.medium(theme.brightness) : null, ), child: Material( color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), child: InkWell( onTap: () => _DocReaderSheet.show(context, entry), - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), child: Container( - padding: const EdgeInsets.all(FaiSpace.lg), + padding: const EdgeInsets.all(ChainSpace.lg), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), border: Border.all(color: theme.colorScheme.outlineVariant), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(entry.icon, size: 22, color: theme.colorScheme.primary), - const SizedBox(width: FaiSpace.lg), + const SizedBox(width: ChainSpace.lg), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -1159,7 +1159,7 @@ class _DocReaderSheet extends StatefulWidget { backgroundColor: Theme.of(context).colorScheme.surfaceContainer, elevation: 8, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(FaiRadius.md)), + borderRadius: BorderRadius.vertical(top: Radius.circular(ChainRadius.md)), ), builder: (_) => _DocReaderSheet(entry: entry), ); @@ -1191,7 +1191,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> { // the .dart_tool asset manifest can be inconsistent with // the compiled binary — surface "rebuild Studio" as a // first-class hint rather than just dumping the raw - // PlatformException string into FaiErrorBox. + // PlatformException string into ChainErrorBox. try { return await rootBundle.loadString(localised); } catch (firstErr) { @@ -1219,7 +1219,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> { mainAxisSize: MainAxisSize.min, children: [ Padding( - padding: const EdgeInsets.symmetric(vertical: FaiSpace.sm), + padding: const EdgeInsets.symmetric(vertical: ChainSpace.sm), child: Container( width: 40, height: 4, @@ -1231,10 +1231,10 @@ class _DocReaderSheetState extends State<_DocReaderSheet> { ), Padding( padding: const EdgeInsets.fromLTRB( - FaiSpace.xxl, - FaiSpace.sm, - FaiSpace.lg, - FaiSpace.sm, + ChainSpace.xxl, + ChainSpace.sm, + ChainSpace.lg, + ChainSpace.sm, ), child: Row( children: [ @@ -1243,7 +1243,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> { size: 20, color: theme.colorScheme.primary, ), - const SizedBox(width: FaiSpace.sm), + const SizedBox(width: ChainSpace.sm), Expanded( child: Text( widget.entry.title(l), @@ -1268,7 +1268,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> { builder: (context, snap) { if (snap.connectionState == ConnectionState.waiting) { return const Padding( - padding: EdgeInsets.all(FaiSpace.xxl), + padding: EdgeInsets.all(ChainSpace.xxl), child: Center(child: CircularProgressIndicator()), ); } @@ -1276,7 +1276,7 @@ class _DocReaderSheetState extends State<_DocReaderSheet> { final err = snap.error; final structured = err is _DocReaderError ? err : null; return Padding( - padding: const EdgeInsets.all(FaiSpace.xxl), + padding: const EdgeInsets.all(ChainSpace.xxl), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -1286,21 +1286,21 @@ class _DocReaderSheetState extends State<_DocReaderSheet> { color: theme.colorScheme.error, ), ), - const SizedBox(height: FaiSpace.sm), + const SizedBox(height: ChainSpace.sm), if (structured != null) - FaiErrorBox( + ChainErrorBox( text: structured.localizedBody(l), isError: true, maxHeight: 200, ) else - FaiErrorBox( + ChainErrorBox( error: err, isError: true, maxHeight: 200, ), if (structured != null) ...[ - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), TextButton.icon( icon: const Icon(Icons.open_in_new, size: 16), label: Text(structured.forgejoUrl), @@ -1314,14 +1314,14 @@ class _DocReaderSheetState extends State<_DocReaderSheet> { } return Markdown( data: snap.data ?? '', - padding: const EdgeInsets.all(FaiSpace.xxl), + padding: const EdgeInsets.all(ChainSpace.xxl), selectable: true, onTapLink: (text, href, title) async { if (href != null && href.isNotEmpty) { await SystemActions.openInOs(href); } }, - styleSheet: FaiTheme.markdownStyle(theme), + styleSheet: ChainTheme.markdownStyle(theme), ); }, ), diff --git a/lib/theme/theme.dart b/lib/theme/theme.dart index 2036fdc..7f2d911 100644 --- a/lib/theme/theme.dart +++ b/lib/theme/theme.dart @@ -5,8 +5,8 @@ import 'package:flutter_markdown_plus/flutter_markdown_plus.dart'; import 'tokens.dart'; -class FaiTheme { - FaiTheme._(); +class ChainTheme { + ChainTheme._(); /// Bundled font families. Declared in pubspec.yaml `fonts:` and /// shipped as TTF assets under `assets/fonts/` — NOT fetched at @@ -79,7 +79,7 @@ class FaiTheme { } /// JetBrains Mono for monospaced technical content. Used by - /// FaiMono helper. Only Regular (400) and Medium (500) weights + /// ChainMono helper. Only Regular (400) and Medium (500) weights /// are bundled — heavier requests fall back to the nearest. static TextStyle mono({ double size = 12, @@ -125,25 +125,25 @@ class FaiTheme { ).copyWith(backgroundColor: Colors.transparent), codeblockDecoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border.all(color: theme.colorScheme.outlineVariant), ), - codeblockPadding: const EdgeInsets.all(FaiSpace.md), + codeblockPadding: const EdgeInsets.all(ChainSpace.md), blockquote: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface, ), blockquoteDecoration: BoxDecoration( color: theme.colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), border: Border( left: BorderSide(color: theme.colorScheme.primary, width: 3), ), ), blockquotePadding: const EdgeInsets.fromLTRB( - FaiSpace.md, - FaiSpace.sm, - FaiSpace.sm, - FaiSpace.sm, + ChainSpace.md, + ChainSpace.sm, + ChainSpace.sm, + ChainSpace.sm, ), tableCellsDecoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHigh, @@ -164,32 +164,32 @@ class FaiTheme { static ThemeData dark() { final scheme = ColorScheme( brightness: Brightness.dark, - primary: FaiColors.accent, - onPrimary: FaiColors.black, - primaryContainer: FaiColors.accentMuted, - onPrimaryContainer: FaiColors.textStrong, - secondary: FaiColors.accentDeep, - onSecondary: FaiColors.black, - secondaryContainer: FaiColors.surfaceHigh, - onSecondaryContainer: FaiColors.text, - tertiary: FaiColors.accent, - onTertiary: FaiColors.black, - tertiaryContainer: FaiColors.surfaceHigh, - onTertiaryContainer: FaiColors.text, - error: FaiColors.danger, - onError: FaiColors.textStrong, + primary: ChainColors.accent, + onPrimary: ChainColors.black, + primaryContainer: ChainColors.accentMuted, + onPrimaryContainer: ChainColors.textStrong, + secondary: ChainColors.accentDeep, + onSecondary: ChainColors.black, + secondaryContainer: ChainColors.surfaceHigh, + onSecondaryContainer: ChainColors.text, + tertiary: ChainColors.accent, + onTertiary: ChainColors.black, + tertiaryContainer: ChainColors.surfaceHigh, + onTertiaryContainer: ChainColors.text, + error: ChainColors.danger, + onError: ChainColors.textStrong, errorContainer: const Color(0xFF3F1518), onErrorContainer: const Color(0xFFFCA5A5), - surface: FaiColors.black, - onSurface: FaiColors.text, - onSurfaceVariant: FaiColors.muted, - outline: FaiColors.border, - outlineVariant: FaiColors.border, - surfaceContainerLowest: FaiColors.black, - surfaceContainerLow: FaiColors.surface, - surfaceContainer: FaiColors.surface, - surfaceContainerHigh: FaiColors.surfaceHigh, - surfaceContainerHighest: FaiColors.surfaceHigh, + surface: ChainColors.black, + onSurface: ChainColors.text, + onSurfaceVariant: ChainColors.muted, + outline: ChainColors.border, + outlineVariant: ChainColors.border, + surfaceContainerLowest: ChainColors.black, + surfaceContainerLow: ChainColors.surface, + surfaceContainer: ChainColors.surface, + surfaceContainerHigh: ChainColors.surfaceHigh, + surfaceContainerHighest: ChainColors.surfaceHigh, ); return _build(scheme); } @@ -197,32 +197,32 @@ class FaiTheme { static ThemeData light() { final scheme = ColorScheme( brightness: Brightness.light, - primary: FaiColors.accentDeep, - onPrimary: FaiColors.lightSurface, + primary: ChainColors.accentDeep, + onPrimary: ChainColors.lightSurface, primaryContainer: const Color(0xFFE0F2FE), - onPrimaryContainer: FaiColors.accentMuted, - secondary: FaiColors.accentMuted, - onSecondary: FaiColors.lightSurface, - secondaryContainer: FaiColors.lightSurfaceHigh, - onSecondaryContainer: FaiColors.lightText, - tertiary: FaiColors.accentDeep, - onTertiary: FaiColors.lightSurface, + onPrimaryContainer: ChainColors.accentMuted, + secondary: ChainColors.accentMuted, + onSecondary: ChainColors.lightSurface, + secondaryContainer: ChainColors.lightSurfaceHigh, + onSecondaryContainer: ChainColors.lightText, + tertiary: ChainColors.accentDeep, + onTertiary: ChainColors.lightSurface, tertiaryContainer: const Color(0xFFE0F2FE), - onTertiaryContainer: FaiColors.accentMuted, - error: FaiColors.danger, - onError: FaiColors.lightSurface, + onTertiaryContainer: ChainColors.accentMuted, + error: ChainColors.danger, + onError: ChainColors.lightSurface, errorContainer: const Color(0xFFFEE2E2), onErrorContainer: const Color(0xFF7F1D1D), - surface: FaiColors.lightCanvas, - onSurface: FaiColors.lightText, - onSurfaceVariant: FaiColors.lightMuted, - outline: FaiColors.lightBorder, - outlineVariant: FaiColors.lightBorder, - surfaceContainerLowest: FaiColors.lightCanvas, - surfaceContainerLow: FaiColors.lightSurface, - surfaceContainer: FaiColors.lightSurface, - surfaceContainerHigh: FaiColors.lightSurfaceHigh, - surfaceContainerHighest: FaiColors.lightSurfaceHigh, + surface: ChainColors.lightCanvas, + onSurface: ChainColors.lightText, + onSurfaceVariant: ChainColors.lightMuted, + outline: ChainColors.lightBorder, + outlineVariant: ChainColors.lightBorder, + surfaceContainerLowest: ChainColors.lightCanvas, + surfaceContainerLow: ChainColors.lightSurface, + surfaceContainer: ChainColors.lightSurface, + surfaceContainerHigh: ChainColors.lightSurfaceHigh, + surfaceContainerHighest: ChainColors.lightSurfaceHigh, ); return _build(scheme); } @@ -256,7 +256,7 @@ class FaiTheme { color: scheme.surfaceContainer, elevation: 0, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(FaiRadius.md), + borderRadius: BorderRadius.circular(ChainRadius.md), side: BorderSide(color: scheme.outlineVariant), ), margin: EdgeInsets.zero, @@ -266,14 +266,14 @@ class FaiTheme { backgroundColor: scheme.primary, foregroundColor: scheme.onPrimary, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), ), padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.lg, - vertical: FaiSpace.md, + horizontal: ChainSpace.lg, + vertical: ChainSpace.md, ), textStyle: textTheme.labelMedium, - animationDuration: FaiMotion.fast, + animationDuration: ChainMotion.fast, ), ), outlinedButtonTheme: OutlinedButtonThemeData( @@ -281,14 +281,14 @@ class FaiTheme { foregroundColor: scheme.onSurface, side: BorderSide(color: scheme.outline), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), ), padding: const EdgeInsets.symmetric( - horizontal: FaiSpace.lg, - vertical: FaiSpace.md, + horizontal: ChainSpace.lg, + vertical: ChainSpace.md, ), textStyle: textTheme.labelMedium, - animationDuration: FaiMotion.fast, + animationDuration: ChainMotion.fast, ), ), iconButtonTheme: IconButtonThemeData( @@ -323,7 +323,7 @@ class FaiTheme { ), behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(FaiRadius.sm), + borderRadius: BorderRadius.circular(ChainRadius.sm), ), ), ); diff --git a/lib/theme/tokens.dart b/lib/theme/tokens.dart index b583607..fdd010d 100644 --- a/lib/theme/tokens.dart +++ b/lib/theme/tokens.dart @@ -7,8 +7,8 @@ import 'package:flutter/material.dart'; /// Colour palette. Single strong accent (Ch∆In Cyan) plus neutral /// greys. Status colours stay restrained — no candy palette. -class FaiColors { - FaiColors._(); +class ChainColors { + ChainColors._(); // Brand accent. Slightly cooler / more saturated than the // initial #1E3A8A — this reads more as "deep tech, alive" @@ -43,8 +43,8 @@ class FaiColors { /// Spacing scale. Stick to multiples of 4. Keep the vocabulary /// small — six steps cover everything. -class FaiSpace { - FaiSpace._(); +class ChainSpace { + ChainSpace._(); static const xs = 4.0; static const sm = 8.0; static const md = 12.0; @@ -55,8 +55,8 @@ class FaiSpace { } /// Border radii. Two sizes only. -class FaiRadius { - FaiRadius._(); +class ChainRadius { + ChainRadius._(); static const sm = 6.0; static const md = 10.0; } @@ -66,8 +66,8 @@ class FaiRadius { /// Brightness-aware: dark themes lean on deeper, lower-alpha /// shadows (they read as ambient occlusion against near-black /// surfaces); light themes use softer, slightly larger spreads. -class FaiElevation { - FaiElevation._(); +class ChainElevation { + ChainElevation._(); /// Resting elevation for cards and sheets. Just enough to lift /// a surface off the canvas without announcing itself. @@ -106,8 +106,8 @@ class FaiElevation { } /// Animation durations. Keep them under 300ms for power-user feel. -class FaiMotion { - FaiMotion._(); +class ChainMotion { + ChainMotion._(); static const fast = Duration(milliseconds: 120); static const base = Duration(milliseconds: 200); static const slow = Duration(milliseconds: 320); diff --git a/lib/widgets/fai_binary_recovery.dart b/lib/widgets/chain_binary_recovery.dart similarity index 76% rename from lib/widgets/fai_binary_recovery.dart rename to lib/widgets/chain_binary_recovery.dart index d92aa99..263438e 100644 --- a/lib/widgets/fai_binary_recovery.dart +++ b/lib/widgets/chain_binary_recovery.dart @@ -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 _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), ), ], ), diff --git a/lib/widgets/fai_card.dart b/lib/widgets/chain_card.dart similarity index 85% rename from lib/widgets/fai_card.dart rename to lib/widgets/chain_card.dart index 7eb6cc3..63c5e0a 100644 --- a/lib/widgets/fai_card.dart +++ b/lib/widgets/chain_card.dart @@ -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( diff --git a/lib/widgets/fai_data_row.dart b/lib/widgets/chain_data_row.dart similarity index 83% rename from lib/widgets/fai_data_row.dart rename to lib/widgets/chain_data_row.dart index f83aa3b..50cd9bc 100644 --- a/lib/widgets/fai_data_row.dart +++ b/lib/widgets/chain_data_row.dart @@ -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 createState() => _FaiDataRowState(); + State createState() => _FaiDataRowState(); } -class _FaiDataRowState extends State { +class _FaiDataRowState extends State { bool _hovered = false; @override @@ -57,12 +57,12 @@ class _FaiDataRowState extends State { 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 { ), ), 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 { 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 { 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 { ), 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, ), diff --git a/lib/widgets/fai_delta_mark.dart b/lib/widgets/chain_delta_mark.dart similarity index 77% rename from lib/widgets/fai_delta_mark.dart rename to lib/widgets/chain_delta_mark.dart index 025988c..ae05a1f 100644 --- a/lib/widgets/fai_delta_mark.dart +++ b/lib/widgets/chain_delta_mark.dart @@ -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 createState() => _FaiDeltaMarkState(); + State createState() => _FaiDeltaMarkState(); } -class _FaiDeltaMarkState extends State +class _FaiDeltaMarkState extends State with SingleTickerProviderStateMixin { late final AnimationController _ctrl; @@ -44,13 +44,13 @@ class _FaiDeltaMarkState extends State _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 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 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; diff --git a/lib/widgets/fai_empty_state.dart b/lib/widgets/chain_empty_state.dart similarity index 86% rename from lib/widgets/fai_empty_state.dart rename to lib/widgets/chain_empty_state.dart index 979afd9..4c63ffc 100644 --- a/lib/widgets/fai_empty_state.dart +++ b/lib/widgets/chain_empty_state.dart @@ -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!, ], ], diff --git a/lib/widgets/fai_en_badge.dart b/lib/widgets/chain_en_badge.dart similarity index 84% rename from lib/widgets/fai_en_badge.dart rename to lib/widgets/chain_en_badge.dart index 9af93d0..ca73ac1 100644 --- a/lib/widgets/fai_en_badge.dart +++ b/lib/widgets/chain_en_badge.dart @@ -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( diff --git a/lib/widgets/fai_error_box.dart b/lib/widgets/chain_error_box.dart similarity index 90% rename from lib/widgets/fai_error_box.dart rename to lib/widgets/chain_error_box.dart index fecad0c..1963f02 100644 --- a/lib/widgets/fai_error_box.dart +++ b/lib/widgets/chain_error_box.dart @@ -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 createState() => _FaiErrorBoxState(); + State createState() => _FaiErrorBoxState(); } -class _FaiErrorBoxState extends State { +class _FaiErrorBoxState extends State { bool _justCopied = false; bool _detailExpanded = false; @@ -86,14 +86,14 @@ class _FaiErrorBoxState extends State { 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 { ), ], 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 { ), ), 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 { 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 { child: SingleChildScrollView( child: SelectableText( widget.text, - style: FaiTheme.mono(size: 11, color: fg), + style: ChainTheme.mono(size: 11, color: fg), ), ), ), diff --git a/lib/widgets/fai_flow_output.dart b/lib/widgets/chain_flow_output.dart similarity index 87% rename from lib/widgets/fai_flow_output.dart rename to lib/widgets/chain_flow_output.dart index 8e59a03..ebe744e 100644 --- a/lib/widgets/fai_flow_output.dart +++ b/lib/widgets/chain_flow_output.dart @@ -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), diff --git a/lib/widgets/fai_log_viewer.dart b/lib/widgets/chain_log_viewer.dart similarity index 93% rename from lib/widgets/fai_log_viewer.dart rename to lib/widgets/chain_log_viewer.dart index e20451d..c86ad18 100644 --- a/lib/widgets/fai_log_viewer.dart +++ b/lib/widgets/chain_log_viewer.dart @@ -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/.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 showFaiLogViewer( return showDialog( context: context, barrierDismissible: true, - builder: (_) => FaiLogViewer( + builder: (_) => ChainLogViewer( path: path, title: title ?? path.split(Platform.pathSeparator).last, tailLines: tailLines, @@ -72,7 +72,7 @@ Future 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 createState() => _FaiLogViewerState(); + State createState() => _FaiLogViewerState(); } -class _FaiLogViewerState extends State { +class _FaiLogViewerState extends State { List _lines = const []; bool _loading = true; bool _justCopied = false; @@ -141,8 +141,8 @@ class _FaiLogViewerState extends State { 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 { : _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; diff --git a/lib/widgets/fai_module_sheet.dart b/lib/widgets/chain_module_sheet.dart similarity index 89% rename from lib/widgets/fai_module_sheet.dart rename to lib/widgets/chain_module_sheet.dart index 5ae7d36..5c63740 100644 --- a/lib/widgets/fai_module_sheet.dart +++ b/lib/widgets/chain_module_sheet.dart @@ -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 createState() => _FaiModuleSheetState(); + State createState() => _FaiModuleSheetState(); } -class _FaiModuleSheetState extends State { +class _FaiModuleSheetState extends State { late final Future _future; bool _uninstalling = false; @@ -127,12 +127,12 @@ class _FaiModuleSheetState extends State { _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( groupValue: _picked, onChanged: (val) { diff --git a/lib/widgets/fai_pill.dart b/lib/widgets/chain_pill.dart similarity index 63% rename from lib/widgets/fai_pill.dart rename to lib/widgets/chain_pill.dart index 2ab2261..24f73b7 100644 --- a/lib/widgets/fai_pill.dart +++ b/lib/widgets/chain_pill.dart @@ -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); } } diff --git a/lib/widgets/fai_search_palette.dart b/lib/widgets/chain_search_palette.dart similarity index 94% rename from lib/widgets/fai_search_palette.dart rename to lib/widgets/chain_search_palette.dart index 776b3bb..0a0dc8e 100644 --- a/lib/widgets/fai_search_palette.dart +++ b/lib/widgets/chain_search_palette.dart @@ -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 staticHits; + final List 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 show( BuildContext context, { - required List staticHits, + required List staticHits, }) { return showDialog( context: context, barrierColor: Colors.black54, - builder: (_) => FaiSearchPalette(staticHits: staticHits), + builder: (_) => ChainSearchPalette(staticHits: staticHits), ); } @override - State createState() => _FaiSearchPaletteState(); + State createState() => _FaiSearchPaletteState(); } -class _FaiSearchPaletteState extends State { +class _FaiSearchPaletteState extends State { final _query = TextEditingController(); final _focus = FocusNode(); - Future>? _dynamicFuture; + Future>? _dynamicFuture; int _highlight = 0; @override @@ -92,7 +92,7 @@ class _FaiSearchPaletteState extends State { /// Pull modules / store / flows in parallel so the palette /// is searchable as soon as the operator finishes typing the /// first character. - Future> _loadDynamic() async { + Future> _loadDynamic() async { final svc = HubService.instance; final l = AppLocalizations.of(context)!; final results = await Future.wait([ @@ -104,17 +104,17 @@ class _FaiSearchPaletteState extends State { final store = results[1] as List; final flows = results[2] as List; - final hits = []; + final hits = []; 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 { 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 { 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 { /// Filtered + grouped result list. Empty query returns the /// pages first then everything else, capped to 50 rows so the /// dialog stays scrollable. - List _resultsFor(List dynamic_, String raw) { + List _resultsFor(List 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 { shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 640, maxHeight: 540), - child: FutureBuilder>( + child: FutureBuilder>( future: _dynamicFuture, builder: (context, snap) { - final dyn = snap.data ?? const []; + final dyn = snap.data ?? const []; return ValueListenableBuilder( valueListenable: _query, builder: (_, value, _) { diff --git a/lib/widgets/fai_settings_dialog.dart b/lib/widgets/chain_settings_dialog.dart similarity index 93% rename from lib/widgets/fai_settings_dialog.dart rename to lib/widgets/chain_settings_dialog.dart index 3910507..b94b58d 100644 --- a/lib/widgets/fai_settings_dialog.dart +++ b/lib/widgets/chain_settings_dialog.dart @@ -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 show(BuildContext context) async { final ok = await showDialog( context: context, - builder: (_) => const FaiSettingsDialog(), + builder: (_) => const ChainSettingsDialog(), ); return ok ?? false; } @override - State createState() => _FaiSettingsDialogState(); + State 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 { +class _FaiSettingsDialogState extends State { late final TextEditingController _host; late final TextEditingController _port; bool _secure = false; @@ -291,7 +291,7 @@ class _FaiSettingsDialogState extends State { _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 { }; 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 { 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 { ), autofocus: true, ), - const SizedBox(height: FaiSpace.md), + const SizedBox(height: ChainSpace.md), Row( children: [ Expanded( @@ -470,7 +470,7 @@ class _FaiSettingsDialogState extends State { 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 { ], ), 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 { ), ), ], - 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 { 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 { ), ), 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 { 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 { ? 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 { _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 { ), 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 { 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 { 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 { 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( @@ -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( diff --git a/lib/widgets/fai_status_dot.dart b/lib/widgets/chain_status_dot.dart similarity index 76% rename from lib/widgets/fai_status_dot.dart rename to lib/widgets/chain_status_dot.dart index 136aac6..e34ca7d 100644 --- a/lib/widgets/fai_status_dot.dart +++ b/lib/widgets/chain_status_dot.dart @@ -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 createState() => _FaiStatusDotState(); + State createState() => _FaiStatusDotState(); } -class _FaiStatusDotState extends State +class _FaiStatusDotState extends State with SingleTickerProviderStateMixin { late final AnimationController _ctrl; @@ -40,7 +40,7 @@ class _FaiStatusDotState extends State } @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 } /// 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); } diff --git a/lib/widgets/fai_system_ai_editor.dart b/lib/widgets/chain_system_ai_editor.dart similarity index 92% rename from lib/widgets/fai_system_ai_editor.dart rename to lib/widgets/chain_system_ai_editor.dart index e8f9dde..90d70d5 100644 --- a/lib/widgets/fai_system_ai_editor.dart +++ b/lib/widgets/chain_system_ai_editor.dart @@ -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( context: context, - builder: (_) => FaiSystemAiEditor(initial: initial), + builder: (_) => ChainSystemAiEditor(initial: initial), ); } @override - State createState() => _FaiSystemAiEditorState(); + State createState() => _FaiSystemAiEditorState(); } -class _FaiSystemAiEditorState extends State { +class _FaiSystemAiEditorState extends State { late _ProviderPreset _preset; late final TextEditingController _endpoint; late final TextEditingController _model; @@ -365,11 +365,11 @@ class _FaiSystemAiEditorState extends State { 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 { ), ), 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 { 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 { ? 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 { 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 { ), ], 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( diff --git a/lib/widgets/theme_picker_grid.dart b/lib/widgets/theme_picker_grid.dart index e7c781d..4f54bf4 100644 --- a/lib/widgets/theme_picker_grid.dart +++ b/lib/widgets/theme_picker_grid.dart @@ -55,7 +55,7 @@ class _ThemePickerGridState extends State { 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 { _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( diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index adea76b..5597bd2 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -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'; diff --git a/test/fai_log_test.dart b/test/chain_log_test.dart similarity index 71% rename from test/fai_log_test.dart rename to test/chain_log_test.dart index 0dae9d0..4104b0a 100644 --- a/test/fai_log_test.dart +++ b/test/chain_log_test.dart @@ -1,36 +1,36 @@ -// Unit tests for FaiLog — the central error log that +// Unit tests for ChainLog — the central error log that // `~/.fai/logs/studio-errors.log` is the operator-visible // surface of. The tests redirect the singleton at a temp file -// via `FaiLog.testPathOverride` so the real log under HOME is +// via `ChainLog.testPathOverride` so the real log under HOME is // never touched. import 'dart:convert'; import 'dart:io'; -import 'package:chain_studio/data/fai_log.dart'; +import 'package:chain_studio/data/chain_log.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - group('FaiLog', () { + group('ChainLog', () { late Directory tmp; late File logFile; setUp(() { - tmp = Directory.systemTemp.createTempSync('fai_log_test_'); + tmp = Directory.systemTemp.createTempSync('chain_log_test_'); logFile = File('${tmp.path}/studio-errors.log'); - FaiLog.testPathOverride = logFile.path; + ChainLog.testPathOverride = logFile.path; }); tearDown(() { - FaiLog.testPathOverride = null; + ChainLog.testPathOverride = null; try { tmp.deleteSync(recursive: true); } catch (_) {/* best-effort */} }); test('append writes one JSON-shaped line per event', () async { - await FaiLog.instance.error('flows.run', 'boom'); - final lines = await FaiLog.instance.tail(); + await ChainLog.instance.error('flows.run', 'boom'); + final lines = await ChainLog.instance.tail(); expect(lines, hasLength(1)); final entry = jsonDecode(lines.single) as Map; expect(entry['source'], 'flows.run'); @@ -40,22 +40,22 @@ void main() { }); test('context field is preserved when supplied', () async { - await FaiLog.instance.error( + await ChainLog.instance.error( 'theme.plugin.load', Exception('endpoint unreachable'), context: 'capability=studio.theme.space', ); - final lines = await FaiLog.instance.tail(); + final lines = await ChainLog.instance.tail(); final entry = jsonDecode(lines.single) as Map; expect(entry['context'], 'capability=studio.theme.space'); expect(entry['error'], contains('endpoint unreachable')); }); test('tail returns oldest first', () async { - await FaiLog.instance.error('a', '1'); - await FaiLog.instance.error('b', '2'); - await FaiLog.instance.error('c', '3'); - final lines = await FaiLog.instance.tail(); + await ChainLog.instance.error('a', '1'); + await ChainLog.instance.error('b', '2'); + await ChainLog.instance.error('c', '3'); + final lines = await ChainLog.instance.tail(); expect(lines, hasLength(3)); final sources = lines.map((l) => jsonDecode(l)['source']).toList(); expect(sources, ['a', 'b', 'c']); @@ -63,9 +63,9 @@ void main() { test('tail caps output at maxLines and keeps the newest', () async { for (var i = 0; i < 20; i++) { - await FaiLog.instance.error('burst', 'event-$i'); + await ChainLog.instance.error('burst', 'event-$i'); } - final lines = await FaiLog.instance.tail(maxLines: 5); + final lines = await ChainLog.instance.tail(maxLines: 5); expect(lines, hasLength(5)); final last = jsonDecode(lines.last) as Map; expect(last['error'], 'event-19'); @@ -77,7 +77,7 @@ void main() { // `.log.1` and a short fresh `.log`. logFile.parent.createSync(recursive: true); logFile.writeAsBytesSync(List.filled(257 * 1024, 0x20)); - await FaiLog.instance.error('rotation', 'after-cap'); + await ChainLog.instance.error('rotation', 'after-cap'); expect(File('${logFile.path}.1').existsSync(), isTrue); final newSize = logFile.lengthSync(); expect(newSize, lessThan(2 * 1024)); @@ -86,16 +86,16 @@ void main() { }); test('path getter returns the override when set', () { - expect(FaiLog.instance.path, logFile.path); + expect(ChainLog.instance.path, logFile.path); }); test('writes failing silently does not throw', () async { // Redirect to a deliberately impossible path. The error() // call must complete cleanly; the operator UI must never // crash because the disk is full / read-only / missing. - FaiLog.testPathOverride = '/this/path/does/not/exist/ever/x.log'; + ChainLog.testPathOverride = '/this/path/does/not/exist/ever/x.log'; await expectLater( - FaiLog.instance.error('bad.path', 'boom'), + ChainLog.instance.error('bad.path', 'boom'), completes, ); }); diff --git a/test/integration/README.md b/test/integration/README.md index e2f71f4..b3213df 100644 --- a/test/integration/README.md +++ b/test/integration/README.md @@ -35,14 +35,14 @@ flutter test test/integration/ ``` Prereq: a `fai` binary on PATH or at -`../fai_platform/target/release/fai`. The harness skips with a +`../fai_chain/target/release/chain`. The harness skips with a clear message when neither exists, so this command does not fail on a fresh checkout — it just reports skipped tests. To get the binary: ```bash -cd ../fai_platform +cd ../fai_chain cargo build --release --bin fai ``` @@ -52,11 +52,11 @@ A cold `chain serve` spends its first ~30s building the curated-model database and initialising SQLite migrations. `HubFixture.start()` waits up to 60s by default; if your local hub takes longer the first time, run `chain serve` once by hand -against any temp `FAI_DATA_DIR` to warm the per-user cargo / +against any temp `CHAIN_DATA_DIR` to warm the per-user cargo / SBOM caches: ```bash -FAI_DATA_DIR=/tmp/fai_warmup chain serve --bind 127.0.0.1:0 +CHAIN_DATA_DIR=/tmp/chain_warmup chain serve --bind 127.0.0.1:0 # wait for "hub started", Ctrl-C ``` @@ -66,7 +66,7 @@ Subsequent integration-test runs are quick. `.forgejo/workflows/ci.yml` doesn't run these yet. Adding them needs an artifact-passing pattern: the platform build job -publishes `target/release/fai` as a CI artifact; the studio +publishes `target/release/chain` as a CI artifact; the studio test job consumes it. Pattern is straightforward once we want it; it's deferred because we don't yet have enough integration tests to justify the CI runtime. diff --git a/test/integration/capabilities_test.dart b/test/integration/capabilities_test.dart index 0063548..6161e6b 100644 --- a/test/integration/capabilities_test.dart +++ b/test/integration/capabilities_test.dart @@ -22,7 +22,7 @@ // flutter test test/integration/ // // CI does not yet run these by default — they need a built `fai` -// binary in PATH or in ../fai_platform/target/release/. Wire that +// binary in PATH or in ../chain_platform/target/release/. Wire that // in via .forgejo/workflows/ci.yml once the platform CI publishes // a Linux binary as an artifact for downstream jobs to consume. diff --git a/test/integration/hub_fixture.dart b/test/integration/hub_fixture.dart index d0c5836..c376c0a 100644 --- a/test/integration/hub_fixture.dart +++ b/test/integration/hub_fixture.dart @@ -72,7 +72,7 @@ class HubFixture { if (skipIfBinaryMissing) { markTestSkipped( 'Hub fixture skipped: no `fai` binary found on PATH or in ' - '../fai_platform/target/release/fai. Build it with ' + '../chain_platform/target/release/fai. Build it with ' '`cargo build --release` and re-run.', ); return null; @@ -86,15 +86,15 @@ class HubFixture { // Run as a child process. `serve --bind` lets us avoid the // default 50051 (and any local daemon the developer happens - // to be running). FAI_DATA_DIR keeps the temp footprint + // to be running). CHAIN_DATA_DIR keeps the temp footprint // contained — no state leaks into ~/.fai. final process = await Process.start( binary, ['serve', '--bind', addr], environment: { ...Platform.environment, - 'FAI_DATA_DIR': tempDir.path, - 'FAI_MODULES_DIR': '${tempDir.path}/modules', + 'CHAIN_DATA_DIR': tempDir.path, + 'CHAIN_MODULES_DIR': '${tempDir.path}/modules', // Suppress info logs without a CLI flag — the binary // respects RUST_LOG for tracing-subscriber. 'RUST_LOG': 'warn', @@ -136,7 +136,7 @@ class HubFixture { await tempDir.delete(recursive: true); throw StateError( 'Hub did not become healthy within $readyTimeout. ' - 'Check `fai serve` works manually with FAI_DATA_DIR=$tempDir.', + 'Check `fai serve` works manually with CHAIN_DATA_DIR=$tempDir.', ); } @@ -172,8 +172,8 @@ class HubFixture { } final candidate = Platform.isWindows - ? '../fai_platform/target/release/fai.exe' - : '../fai_platform/target/release/fai'; + ? '../chain_platform/target/release/fai.exe' + : '../chain_platform/target/release/fai'; final f = File(candidate); if (await f.exists()) { return f.absolute.path; diff --git a/test/integration/plugin_theme_test.dart b/test/integration/plugin_theme_test.dart index 1457bf3..1a7e796 100644 --- a/test/integration/plugin_theme_test.dart +++ b/test/integration/plugin_theme_test.dart @@ -10,9 +10,9 @@ // flutter test test/integration/plugin_theme_test.dart // // Pre-reqs (handled by HubFixture's skip-when-missing path): -// - fai binary on PATH or at ../fai_platform/target/release/fai +// - fai binary on PATH or at ../chain_platform/target/release/fai // - studio-theme-solarized plugin built to module.wasm in its -// fai_plugins dir. +// chain_plugins dir. import 'dart:io'; @@ -33,7 +33,7 @@ void main() { // add network + bundle-fetch latency that doesn't // belong in an in-process round-trip test. final pluginSrc = Directory( - '../fai_plugins/studio-theme-solarized', + '../chain_plugins/studio-theme-solarized', ); if (!await pluginSrc.exists()) { markTestSkipped('studio-theme-solarized source not available'); diff --git a/test/load_docs_test.dart b/test/load_docs_test.dart index 3960cde..f113a7c 100644 --- a/test/load_docs_test.dart +++ b/test/load_docs_test.dart @@ -7,7 +7,7 @@ import 'package:chain_studio/l10n/app_localizations.dart'; import 'package:chain_studio/theme/theme.dart'; /// Recreates the exact DocReaderSheet code path that -/// welcome.dart uses, including FaiTheme.markdownStyle, so a +/// welcome.dart uses, including ChainTheme.markdownStyle, so a /// regression in either rootBundle, the markdown parser, or /// the style sheet surfaces here instead of only at runtime /// when an operator clicks a doc card. @@ -16,7 +16,7 @@ void main() { testWidgets('DocReaderSheet renders all four german + english docs', (tester) async { - final theme = FaiTheme.light(); + final theme = ChainTheme.light(); for (final slug in ['architecture', 'security', 'audit', 'flows']) { for (final locale in ['de', 'en']) { final path = locale == 'de' @@ -39,7 +39,7 @@ void main() { data: text, padding: const EdgeInsets.all(8), selectable: true, - styleSheet: FaiTheme.markdownStyle(theme), + styleSheet: ChainTheme.markdownStyle(theme), ), ), )); diff --git a/tools/today/README.md b/tools/today/README.md index a386e6b..61cb78f 100644 --- a/tools/today/README.md +++ b/tools/today/README.md @@ -51,10 +51,10 @@ A starter plist lives at `launchd/ai.flemming.chain.today.plist`. | Variable | Default | Example | |-------------------|----------------------------------|-------------------------------------------------| -| `FAI_TODAY_API` | `http://127.0.0.1:11434/api/generate` | `https://api.openai.com/v1/chat/completions` | -| `FAI_TODAY_MODEL` | `gemma3:4b` | `gpt-4o-mini` | -| `FAI_TODAY_KEY` | (unset) | `$OPENAI_API_KEY` | -| `FAI_TODAY_N` | `3` | `1` (single shot) | +| `CHAIN_TODAY_API` | `http://127.0.0.1:11434/api/generate` | `https://api.openai.com/v1/chat/completions` | +| `CHAIN_TODAY_MODEL` | `gemma3:4b` | `gpt-4o-mini` | +| `CHAIN_TODAY_KEY` | (unset) | `$OPENAI_API_KEY` | +| `CHAIN_TODAY_N` | `3` | `1` (single shot) | OpenAI-compatible endpoints will work as long as they speak `chat/completions` or `generate`; the script auto-detects from the URL path. diff --git a/tools/today/collect.sh b/tools/today/collect.sh index 4d0430b..0fd0832 100755 --- a/tools/today/collect.sh +++ b/tools/today/collect.sh @@ -9,7 +9,7 @@ # Does not read the audit log without --with-audit. set -euo pipefail -SINCE="${FAI_TODAY_SINCE:-24 hours ago}" +SINCE="${CHAIN_TODAY_SINCE:-24 hours ago}" # Walk every fai-* directory next to chain_studio so the collector # works from any of the sibling checkouts. Adjust here if the @@ -31,10 +31,10 @@ for repo in "$ROOT"/fai_*/; do done emit_section "STORE INDEX SEED CHANGES" -seed="$ROOT/fai_platform/crates/fai_hub/store-index/seed.yaml" -if [ -f "$seed" ] && [ -d "$ROOT/fai_platform/.git" ]; then - diff=$(git -C "$ROOT/fai_platform" log --since="$SINCE" --oneline -- \ - crates/fai_hub/store-index/seed.yaml 2>/dev/null || true) +seed="$ROOT/fai_chain/crates/chain_hub/store-index/seed.yaml" +if [ -f "$seed" ] && [ -d "$ROOT/fai_chain/.git" ]; then + diff=$(git -C "$ROOT/fai_chain" log --since="$SINCE" --oneline -- \ + crates/chain_hub/store-index/seed.yaml 2>/dev/null || true) if [ -n "$diff" ]; then printf '%s\n' "$diff" else @@ -43,9 +43,9 @@ if [ -f "$seed" ] && [ -d "$ROOT/fai_platform/.git" ]; then fi emit_section "ARCHITECTURE / SYSTEM-GAPS CHANGES" -arch_dir="$ROOT/fai_platform/docs" -if [ -d "$arch_dir" ] && [ -d "$ROOT/fai_platform/.git" ]; then - changes=$(git -C "$ROOT/fai_platform" log --since="$SINCE" --oneline -- \ +arch_dir="$ROOT/fai_chain/docs" +if [ -d "$arch_dir" ] && [ -d "$ROOT/fai_chain/.git" ]; then + changes=$(git -C "$ROOT/fai_chain" log --since="$SINCE" --oneline -- \ docs/architecture docs/advanced 2>/dev/null || true) if [ -n "$changes" ]; then printf '%s\n' "$changes" @@ -67,9 +67,9 @@ if [ -d "$studio/.git" ]; then fi emit_section "AUDIT-LOG HIGHLIGHTS" -if [ "${FAI_TODAY_WITH_AUDIT:-0}" = "1" ] && command -v fai >/dev/null 2>&1; then +if [ "${CHAIN_TODAY_WITH_AUDIT:-0}" = "1" ] && command -v fai >/dev/null 2>&1; then fai audit recent --limit 20 2>/dev/null | head -40 || \ printf '(audit query failed; daemon may be stopped)\n' else - printf '(audit pull disabled — set FAI_TODAY_WITH_AUDIT=1 to opt in)\n' + printf '(audit pull disabled — set CHAIN_TODAY_WITH_AUDIT=1 to opt in)\n' fi diff --git a/tools/today/propose.sh b/tools/today/propose.sh index 942590b..95946ed 100755 --- a/tools/today/propose.sh +++ b/tools/today/propose.sh @@ -5,17 +5,17 @@ # Defaults to a local Ollama at 127.0.0.1:11434 with gemma3:4b. # See README.md for env-var overrides. # -# Output: ~/.fai/today/proposals//candidate-.yaml +# Output: ~/.chain/today/proposals//candidate-.yaml # Operator then runs accept.sh with the chosen file. set -euo pipefail HERE="$(cd "$(dirname "$0")" && pwd)" DATE="$(date -u +%Y-%m-%d)" -OUT_DIR="${FAI_TODAY_OUT:-$HOME/.fai/today/proposals/$DATE}" -N="${FAI_TODAY_N:-3}" -API="${FAI_TODAY_API:-http://127.0.0.1:11434/api/generate}" -MODEL="${FAI_TODAY_MODEL:-gemma3:4b}" -KEY="${FAI_TODAY_KEY:-}" +OUT_DIR="${CHAIN_TODAY_OUT:-$HOME/.chain/today/proposals/$DATE}" +N="${CHAIN_TODAY_N:-3}" +API="${CHAIN_TODAY_API:-http://127.0.0.1:11434/api/generate}" +MODEL="${CHAIN_TODAY_MODEL:-gemma3:4b}" +KEY="${CHAIN_TODAY_KEY:-}" mkdir -p "$OUT_DIR"