diff --git a/config/feature_flags.yaml b/config/feature_flags.yaml index 21491816..417024e6 100644 --- a/config/feature_flags.yaml +++ b/config/feature_flags.yaml @@ -134,6 +134,12 @@ mobile: build_modes: [debug, profile, release] description: Mobile settings gateway tab ui_surface: settings_page + vault_server: + enabled: false + release_tier: experimental + build_modes: [debug, profile, release] + description: Mobile Vault server integration section + ui_surface: settings_page gateway_setup_code: enabled: false release_tier: experimental @@ -313,6 +319,12 @@ desktop: build_modes: [debug, profile, release] description: Desktop settings gateway tab ui_surface: settings_page + vault_server: + enabled: false + release_tier: experimental + build_modes: [debug, profile, release] + description: Desktop Vault server integration section + ui_surface: settings_page gateway_setup_code: enabled: false release_tier: experimental @@ -432,6 +444,12 @@ web: build_modes: [debug, profile, release] description: Web settings gateway tab ui_surface: web_settings_page + vault_server: + enabled: false + release_tier: experimental + build_modes: [] + description: Web does not expose vault server integration + ui_surface: web_settings_page gateway_setup_code: enabled: false release_tier: experimental diff --git a/lib/app/ui_feature_manifest.dart b/lib/app/ui_feature_manifest.dart index c3289d6d..3015e406 100644 --- a/lib/app/ui_feature_manifest.dart +++ b/lib/app/ui_feature_manifest.dart @@ -65,6 +65,7 @@ abstract final class UiFeatureKeys { static const settingsGeneral = 'settings.general'; static const settingsWorkspace = 'settings.workspace'; static const settingsGateway = 'settings.gateway'; + static const settingsVaultServer = 'settings.vault_server'; static const settingsGatewaySetupCode = 'settings.gateway_setup_code'; static const settingsAgents = 'settings.agents'; static const settingsAppearance = 'settings.appearance'; @@ -255,6 +256,12 @@ mobile: build_modes: [debug, profile, release] description: Mobile settings gateway tab ui_surface: settings_page + vault_server: + enabled: false + release_tier: experimental + build_modes: [debug, profile, release] + description: Mobile Vault server integration section + ui_surface: settings_page gateway_setup_code: enabled: false release_tier: experimental @@ -434,6 +441,12 @@ desktop: build_modes: [debug, profile, release] description: Desktop settings gateway tab ui_surface: settings_page + vault_server: + enabled: false + release_tier: experimental + build_modes: [debug, profile, release] + description: Desktop Vault server integration section + ui_surface: settings_page gateway_setup_code: enabled: false release_tier: experimental @@ -553,6 +566,12 @@ web: build_modes: [debug, profile, release] description: Web settings gateway tab ui_surface: web_settings_page + vault_server: + enabled: false + release_tier: experimental + build_modes: [] + description: Web does not expose vault server integration + ui_surface: web_settings_page gateway_setup_code: enabled: false release_tier: experimental @@ -932,6 +951,9 @@ class UiFeatureAccess { bool get supportsGatewaySetupCode => isEnabledPath(UiFeatureKeys.settingsGatewaySetupCode); + bool get supportsVaultServer => + isEnabledPath(UiFeatureKeys.settingsVaultServer); + List get availableSettingsTabs { return SettingsTab.values .where( diff --git a/lib/features/settings/settings_page.dart b/lib/features/settings/settings_page.dart index f186539c..9367821e 100644 --- a/lib/features/settings/settings_page.dart +++ b/lib/features/settings/settings_page.dart @@ -229,13 +229,24 @@ class _SettingsPageState extends State { UiFeatureAccess uiFeatures, ) { if (_detail != null) { - return _buildDetailContent(context, controller, settings, _detail!); + return _buildDetailContent( + context, + controller, + settings, + uiFeatures, + _detail!, + ); } return switch (_tab) { SettingsTab.general => _buildGeneral(context, controller, settings), SettingsTab.workspace => _buildWorkspace(context, controller, settings), - SettingsTab.gateway => _buildGateway(context, controller, settings), + SettingsTab.gateway => _buildGateway( + context, + controller, + settings, + uiFeatures, + ), SettingsTab.agents => _buildAgents(context, controller, settings), SettingsTab.appearance => _buildAppearance(context, controller), SettingsTab.diagnostics => _buildDiagnostics(context, controller), @@ -253,6 +264,7 @@ class _SettingsPageState extends State { BuildContext context, AppController controller, SettingsSnapshot settings, + UiFeatureAccess uiFeatures, SettingsDetailPage detail, ) { final workspaceSections = _buildWorkspace(context, controller, settings); @@ -268,8 +280,10 @@ class _SettingsPageState extends State { ), const SizedBox(height: 16), _buildOpenClawGatewayCard(context, controller, settings), - const SizedBox(height: 16), - _buildVaultProviderCard(context, controller, settings), + if (uiFeatures.supportsVaultServer) ...[ + const SizedBox(height: 16), + _buildVaultProviderCard(context, controller, settings), + ], const SizedBox(height: 16), _buildAiGatewayCard(context, controller, settings), ], @@ -295,7 +309,17 @@ class _SettingsPageState extends State { ), ), const SizedBox(height: 16), - _buildVaultProviderCard(context, controller, settings), + if (uiFeatures.supportsVaultServer) + _buildVaultProviderCard(context, controller, settings) + else + SurfaceCard( + child: Text( + appText( + '当前发布配置未开放 Vault Server 参数。', + 'Vault Server settings are disabled in this release configuration.', + ), + ), + ), ], SettingsDetailPage.ollamaProvider => [ _buildDetailIntro( @@ -891,6 +915,7 @@ class _SettingsPageState extends State { BuildContext context, AppController controller, SettingsSnapshot settings, + UiFeatureAccess uiFeatures, ) { return [ _buildCollapsibleGatewaySection( @@ -902,16 +927,18 @@ class _SettingsPageState extends State { }), child: _buildOpenClawGatewayCard(context, controller, settings), ), - const SizedBox(height: 16), - _buildCollapsibleGatewaySection( - context: context, - title: appText('Vault Server', 'Vault Server'), - expanded: _vaultServerExpanded, - onChanged: (value) => setState(() { - _vaultServerExpanded = value; - }), - child: _buildVaultProviderCard(context, controller, settings), - ), + if (uiFeatures.supportsVaultServer) ...[ + const SizedBox(height: 16), + _buildCollapsibleGatewaySection( + context: context, + title: appText('Vault Server', 'Vault Server'), + expanded: _vaultServerExpanded, + onChanged: (value) => setState(() { + _vaultServerExpanded = value; + }), + child: _buildVaultProviderCard(context, controller, settings), + ), + ], const SizedBox(height: 16), _buildCollapsibleGatewaySection( context: context, diff --git a/test/features/settings_page_suite.dart b/test/features/settings_page_suite.dart index 83d9afda..75a29a1a 100644 --- a/test/features/settings_page_suite.dart +++ b/test/features/settings_page_suite.dart @@ -106,7 +106,7 @@ void main() { await tester.pumpAndSettle(); expect(find.text('OpenClaw Gateway'), findsOneWidget); - expect(find.text('Vault Server'), findsOneWidget); + expect(find.text('Vault Server'), findsNothing); expect(find.byKey(const ValueKey('ai-gateway-url-field')), findsOneWidget); expect(find.byKey(const ValueKey('gateway-mode-field')), findsNothing); expect(find.text('认证诊断'), findsNothing); @@ -139,6 +139,33 @@ void main() { ); }); + testWidgets('SettingsPage can expose vault section when feature enabled', ( + WidgetTester tester, + ) async { + final manifest = UiFeatureManifest.fallback().copyWithFeature( + platform: UiFeaturePlatform.desktop, + module: 'settings', + feature: 'vault_server', + enabled: true, + releaseTier: UiFeatureReleaseTier.experimental, + ); + final controller = await createTestController( + tester, + uiFeatureManifest: manifest, + ); + + await pumpPage( + tester, + child: SettingsPage(controller: controller), + platform: TargetPlatform.macOS, + ); + + await tester.tap(find.text('集成')); + await tester.pumpAndSettle(); + + expect(find.text('Vault Server'), findsOneWidget); + }); + testWidgets('SettingsPage gateway sections can collapse individually', ( WidgetTester tester, ) async {