import 'dart:async'; import 'package:flutter/material.dart'; import '../../app/app_controller.dart'; import '../../app/ui_feature_manifest.dart'; import '../../i18n/app_language.dart'; import '../../runtime/runtime_models.dart'; Future showMobileAssistantTargetSheet( BuildContext context, { required AppController controller, required Future Function(AssistantExecutionTarget target) onSelected, }) { final features = controller.featuresFor(UiFeaturePlatform.mobile); final targets = controller.visibleAssistantExecutionTargets( features.availableExecutionTargets, ); final current = controller.currentAssistantExecutionTarget; return showMobileAssistantSheet( context, title: appText('运行目标', 'Execution Target'), children: targets .map( (target) => ListTile( key: Key('mobile-assistant-target-item-${target.name}'), leading: Icon( target.isGateway ? Icons.cloud_queue_rounded : Icons.smart_toy_outlined, ), title: Text(target.label), trailing: target == current ? const Icon(Icons.check_rounded) : null, onTap: () { Navigator.of(context).pop(); unawaited(onSelected(target)); }, ), ) .toList(growable: false), ); } Future showMobileAssistantProviderSheet( BuildContext context, { required AppController controller, required AssistantExecutionTarget target, required SingleAgentProvider selectedProvider, required Future Function(SingleAgentProvider provider) onSelected, }) { final providers = controller.providerCatalogForExecutionTarget(target); return showMobileAssistantSheet( context, title: appText('Provider', 'Provider'), children: providers.isEmpty ? [ Padding( key: const Key('mobile-assistant-provider-empty-state'), padding: const EdgeInsets.fromLTRB(16, 12, 16, 20), child: Text( appText( 'Bridge 尚未提供可用 Provider。不会自动伪造默认 Provider。', 'Bridge has not provided any provider. No default provider is fabricated.', ), ), ), ] : providers .map( (provider) => ListTile( key: Key( 'mobile-assistant-provider-item-${provider.providerId}', ), leading: CircleAvatar( radius: 14, child: Text(mobileProviderBadgeLabel(provider)), ), title: Text(provider.label), subtitle: provider.unavailableReason.trim().isEmpty ? null : Text(provider.unavailableReason), enabled: provider.enabled, trailing: provider == selectedProvider ? const Icon(Icons.check_rounded) : null, onTap: provider.enabled ? () { Navigator.of(context).pop(); unawaited(onSelected(provider)); } : null, ), ) .toList(growable: false), ); } Future showMobileAssistantPermissionSheet( BuildContext context, { required AppController controller, }) { final current = controller.assistantPermissionLevel; return showMobileAssistantSheet( context, title: appText('权限', 'Permissions'), children: AssistantPermissionLevel.values .map( (level) => ListTile( key: Key('mobile-assistant-permission-item-${level.name}'), leading: Icon(mobilePermissionIcon(level)), title: Text(level.label), trailing: level == current ? const Icon(Icons.check_rounded) : null, onTap: () { Navigator.of(context).pop(); unawaited(controller.setAssistantPermissionLevel(level)); }, ), ) .toList(growable: false), ); } Future showMobileAssistantThinkingSheet( BuildContext context, { required String value, required ValueChanged onSelected, }) { const values = ['low', 'medium', 'high', 'max']; return showMobileAssistantSheet( context, title: appText('推理强度', 'Reasoning'), children: values .map( (item) => ListTile( key: Key('mobile-assistant-thinking-item-$item'), leading: const Icon(Icons.psychology_alt_outlined), title: Text(mobileThinkingLabel(item)), trailing: item == value ? const Icon(Icons.check_rounded) : null, onTap: () { Navigator.of(context).pop(); onSelected(item); }, ), ) .toList(growable: false), ); } Future showMobileAssistantSheet( BuildContext context, { required String title, required List children, }) { return showModalBottomSheet( context: context, isScrollControlled: true, showDragHandle: true, builder: (sheetContext) { return SafeArea( top: false, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 10), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 0, 6, 8), child: Row( children: [ Expanded( child: Text( title, style: Theme.of(sheetContext).textTheme.titleLarge, ), ), IconButton( key: const Key('mobile-assistant-sheet-close'), onPressed: () => Navigator.of(sheetContext).pop(), icon: const Icon(Icons.close_rounded), ), ], ), ), Flexible(child: ListView(shrinkWrap: true, children: children)), ], ), ), ); }, ); } String mobileThinkingLabel(String value) { return switch (value) { 'low' => appText('低推理', 'Low'), 'medium' => appText('中推理', 'Medium'), 'high' => appText('高推理', 'High'), 'max' => appText('最大推理', 'Max'), _ => value, }; } IconData mobilePermissionIcon(AssistantPermissionLevel level) { return switch (level) { AssistantPermissionLevel.defaultAccess => Icons.verified_user_outlined, AssistantPermissionLevel.fullAccess => Icons.error_outline_rounded, }; } String mobileProviderBadgeLabel(SingleAgentProvider provider) { final badge = provider.badge.trim(); if (badge.isNotEmpty) { return badge; } final label = provider.label.trim(); return label.isEmpty ? '?' : label.characters.first; }