refactor: formatting

This commit is contained in:
cogwheel0
2025-09-24 12:00:49 +05:30
parent b8c024d0b0
commit 5f013b1b73
27 changed files with 158 additions and 121 deletions

View File

@@ -15,7 +15,9 @@ class TokenValidator {
} }
// Check if it's an API key format (starts with sk- or similar) // Check if it's an API key format (starts with sk- or similar)
if (token.startsWith('sk-') || token.startsWith('api-') || token.startsWith('key-')) { if (token.startsWith('sk-') ||
token.startsWith('api-') ||
token.startsWith('key-')) {
// API key format - validate differently // API key format - validate differently
if (token.length < 20) { if (token.length < 20) {
return TokenValidationResult.invalid('API key too short'); return TokenValidationResult.invalid('API key too short');

View File

@@ -22,17 +22,23 @@ sealed class Folder with _$Folder {
// Extract conversation IDs from items.chats if available // Extract conversation IDs from items.chats if available
final items = json['items'] as Map<String, dynamic>?; final items = json['items'] as Map<String, dynamic>?;
final chats = items?['chats'] as List?; final chats = items?['chats'] as List?;
// Handle both string IDs and conversation objects // Handle both string IDs and conversation objects
final conversationIds = chats?.map((chat) { final conversationIds =
if (chat is String) { chats
return chat; ?.map((chat) {
} else if (chat is Map<String, dynamic>) { if (chat is String) {
return chat['id'] as String? ?? ''; return chat;
} } else if (chat is Map<String, dynamic>) {
return ''; return chat['id'] as String? ?? '';
}).where((id) => id.isNotEmpty).toList().cast<String>() ?? <String>[]; }
return '';
})
.where((id) => id.isNotEmpty)
.toList()
.cast<String>() ??
<String>[];
// Handle Unix timestamp conversion // Handle Unix timestamp conversion
DateTime? parseTimestamp(dynamic timestamp) { DateTime? parseTimestamp(dynamic timestamp) {
if (timestamp == null) return null; if (timestamp == null) return null;
@@ -44,7 +50,7 @@ sealed class Folder with _$Folder {
} }
return null; return null;
} }
// Create the modified JSON with proper field mapping // Create the modified JSON with proper field mapping
return Folder( return Folder(
id: json['id'] as String, id: json['id'] as String,

View File

@@ -23,4 +23,4 @@ sealed class Tool with _$Tool {
meta: json['meta'] as Map<String, dynamic>?, meta: json['meta'] as Map<String, dynamic>?,
); );
} }
} }

View File

@@ -43,12 +43,12 @@ class InputValidationService {
try { try {
final uri = Uri.parse(urlToValidate); final uri = Uri.parse(urlToValidate);
// Validate scheme // Validate scheme
if (!uri.hasScheme || (uri.scheme != 'http' && uri.scheme != 'https')) { if (!uri.hasScheme || (uri.scheme != 'http' && uri.scheme != 'https')) {
return 'Use http:// or https:// only'; return 'Use http:// or https:// only';
} }
// Validate host // Validate host
if (!uri.hasAuthority || uri.host.isEmpty) { if (!uri.hasAuthority || uri.host.isEmpty) {
return 'Please enter a server address (e.g., 192.168.1.10:3000)'; return 'Please enter a server address (e.g., 192.168.1.10:3000)';
@@ -65,7 +65,6 @@ class InputValidationService {
if (_isIPAddress(uri.host) && !_isValidIPAddress(uri.host)) { if (_isIPAddress(uri.host) && !_isValidIPAddress(uri.host)) {
return 'Invalid IP address format (use 192.168.1.10)'; return 'Invalid IP address format (use 192.168.1.10)';
} }
} catch (e) { } catch (e) {
return 'Invalid server address format'; return 'Invalid server address format';
} }
@@ -82,7 +81,7 @@ class InputValidationService {
static bool _isValidIPAddress(String ip) { static bool _isValidIPAddress(String ip) {
final parts = ip.split('.'); final parts = ip.split('.');
if (parts.length != 4) return false; if (parts.length != 4) return false;
for (final part in parts) { for (final part in parts) {
final num = int.tryParse(part); final num = int.tryParse(part);
if (num == null || num < 0 || num > 255) return false; if (num == null || num < 0 || num > 255) return false;

View File

@@ -16,7 +16,9 @@ class OptimizedStorageService {
required FlutterSecureStorage secureStorage, required FlutterSecureStorage secureStorage,
required SharedPreferences prefs, required SharedPreferences prefs,
}) : _prefs = prefs, }) : _prefs = prefs,
_secureCredentialStorage = SecureCredentialStorage(instance: secureStorage); _secureCredentialStorage = SecureCredentialStorage(
instance: secureStorage,
);
// Optimized key names with versioning // Optimized key names with versioning
static const String _authTokenKey = 'auth_token_v3'; static const String _authTokenKey = 'auth_token_v3';

View File

@@ -74,7 +74,9 @@ class PersistentStreamingService with WidgetsBindingObserver {
_connectivitySubscription?.cancel(); _connectivitySubscription?.cancel();
_connectivityService = service; _connectivityService = service;
_connectivitySubscription = service.isConnected.listen(_handleConnectivityChange); _connectivitySubscription = service.isConnected.listen(
_handleConnectivityChange,
);
} }
void _handleConnectivityChange(bool connected) { void _handleConnectivityChange(bool connected) {

View File

@@ -336,10 +336,12 @@ class PlatformService {
// on Android 15+. Only control icon brightness; colors come from theme + EdgeToEdge. // on Android 15+. Only control icon brightness; colors come from theme + EdgeToEdge.
SystemChrome.setSystemUIOverlayStyle( SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle( SystemUiOverlayStyle(
statusBarIconBrightness: statusBarIconBrightness: isDarkContent
isDarkContent ? Brightness.dark : Brightness.light, ? Brightness.dark
systemNavigationBarIconBrightness: : Brightness.light,
isDarkContent ? Brightness.dark : Brightness.light, systemNavigationBarIconBrightness: isDarkContent
? Brightness.dark
: Brightness.light,
// Do NOT set status/navigation bar colors on Android. // Do NOT set status/navigation bar colors on Android.
), ),
); );

View File

@@ -9,10 +9,12 @@ class SecureCredentialStorage {
late final FlutterSecureStorage _secureStorage; late final FlutterSecureStorage _secureStorage;
SecureCredentialStorage({FlutterSecureStorage? instance}) { SecureCredentialStorage({FlutterSecureStorage? instance}) {
_secureStorage = instance ?? FlutterSecureStorage( _secureStorage =
aOptions: _getAndroidOptions(), instance ??
iOptions: _getIOSOptions(), FlutterSecureStorage(
); aOptions: _getAndroidOptions(),
iOptions: _getIOSOptions(),
);
} }
static const String _credentialsKey = 'user_credentials_v2'; static const String _credentialsKey = 'user_credentials_v2';

View File

@@ -16,7 +16,9 @@ class StorageService {
required SharedPreferences prefs, required SharedPreferences prefs,
}) : _secureStorage = secureStorage, }) : _secureStorage = secureStorage,
_prefs = prefs, _prefs = prefs,
_secureCredentialStorage = SecureCredentialStorage(instance: secureStorage); _secureCredentialStorage = SecureCredentialStorage(
instance: secureStorage,
);
// Secure storage keys // Secure storage keys
static const String _authTokenKey = 'auth_token'; static const String _authTokenKey = 'auth_token';

View File

@@ -26,4 +26,4 @@ final toolsServiceProvider = Provider<ToolsService?>((ref) {
final apiService = ref.watch(apiServiceProvider); final apiService = ref.watch(apiServiceProvider);
if (apiService == null) return null; if (apiService == null) return null;
return ToolsService(apiService); return ToolsService(apiService);
}); });

View File

@@ -10,8 +10,8 @@ class InactivityWatchdog {
required Duration window, required Duration window,
required this.onTimeout, required this.onTimeout,
Duration? absoluteCap, Duration? absoluteCap,
}) : _window = window, }) : _window = window,
_absoluteCap = absoluteCap; _absoluteCap = absoluteCap;
final void Function() onTimeout; final void Function() onTimeout;
@@ -80,4 +80,3 @@ class InactivityWatchdog {
} catch (_) {} } catch (_) {}
} }
} }

View File

@@ -44,35 +44,36 @@ class ReviewerModeService {
bool isVoiceInput = false, bool isVoiceInput = false,
}) { }) {
final lowerMessage = userMessage.toLowerCase(); final lowerMessage = userMessage.toLowerCase();
// Determine response category // Determine response category
String category = 'general'; String category = 'general';
if (lowerMessage.contains('hello') || if (lowerMessage.contains('hello') ||
lowerMessage.contains('hi') || lowerMessage.contains('hi') ||
lowerMessage.contains('hey') || lowerMessage.contains('hey') ||
lowerMessage.contains('greet')) { lowerMessage.contains('greet')) {
category = 'greeting'; category = 'greeting';
} else if (lowerMessage.contains('code') || } else if (lowerMessage.contains('code') ||
lowerMessage.contains('program') || lowerMessage.contains('program') ||
lowerMessage.contains('function') || lowerMessage.contains('function') ||
lowerMessage.contains('debug')) { lowerMessage.contains('debug')) {
category = 'code'; category = 'code';
} else if (lowerMessage.contains('feature') || } else if (lowerMessage.contains('feature') ||
lowerMessage.contains('capability') || lowerMessage.contains('capability') ||
lowerMessage.contains('what can') || lowerMessage.contains('what can') ||
lowerMessage.contains('help')) { lowerMessage.contains('help')) {
category = 'features'; category = 'features';
} else if (filename != null) { } else if (filename != null) {
category = 'attachments'; category = 'attachments';
} else if (isVoiceInput) { } else if (isVoiceInput) {
category = 'voice'; category = 'voice';
} }
// Get responses for category // Get responses for category
final responses = _cannedResponses[category] ?? _cannedResponses['general']!; final responses =
_cannedResponses[category] ?? _cannedResponses['general']!;
final response = responses[_random.nextInt(responses.length)]; final response = responses[_random.nextInt(responses.length)];
// Replace placeholders // Replace placeholders
return response return response
.replaceAll('{query}', userMessage) .replaceAll('{query}', userMessage)
@@ -92,4 +93,4 @@ class ReviewerModeService {
isVoiceInput: isVoiceInput, isVoiceInput: isVoiceInput,
); );
} }
} }

View File

@@ -65,4 +65,3 @@ class _PressableScaleState extends State<PressableScale>
); );
} }
} }

View File

@@ -229,7 +229,7 @@ class BrandService {
return AppBar( return AppBar(
title: Text( title: Text(
title, title,
style: context != null style: context != null
? context.conduitTheme.headingSmall?.copyWith( ? context.conduitTheme.headingSmall?.copyWith(
color: context.conduitTheme.textPrimary, color: context.conduitTheme.textPrimary,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,

View File

@@ -3,13 +3,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'outbound_task.freezed.dart'; part 'outbound_task.freezed.dart';
part 'outbound_task.g.dart'; part 'outbound_task.g.dart';
enum TaskStatus { enum TaskStatus { queued, running, succeeded, failed, cancelled }
queued,
running,
succeeded,
failed,
cancelled,
}
@freezed @freezed
abstract class OutboundTask with _$OutboundTask { abstract class OutboundTask with _$OutboundTask {
@@ -74,7 +68,6 @@ abstract class OutboundTask with _$OutboundTask {
String? error, String? error,
}) = GenerateImageTask; }) = GenerateImageTask;
const factory OutboundTask.generateTitle({ const factory OutboundTask.generateTitle({
required String id, required String id,
required String conversationId, required String conversationId,
@@ -106,16 +99,16 @@ abstract class OutboundTask with _$OutboundTask {
// Provide a unified nullable conversationId across variants // Provide a unified nullable conversationId across variants
String? get maybeConversationId => map( String? get maybeConversationId => map(
sendTextMessage: (t) => t.conversationId, sendTextMessage: (t) => t.conversationId,
uploadMedia: (t) => t.conversationId, uploadMedia: (t) => t.conversationId,
executeToolCall: (t) => t.conversationId, executeToolCall: (t) => t.conversationId,
generateImage: (t) => t.conversationId, generateImage: (t) => t.conversationId,
generateTitle: (t) => t.conversationId, generateTitle: (t) => t.conversationId,
imageToDataUrl: (t) => t.conversationId, imageToDataUrl: (t) => t.conversationId,
); );
String get threadKey => String get threadKey =>
(maybeConversationId == null || maybeConversationId!.isEmpty) (maybeConversationId == null || maybeConversationId!.isEmpty)
? 'new' ? 'new'
: maybeConversationId!; : maybeConversationId!;
} }

View File

@@ -751,10 +751,6 @@ class ConduitThemeExtension extends ThemeExtension<ConduitThemeExtension> {
fontFamily: AppTypography.monospaceFontFamily, fontFamily: AppTypography.monospaceFontFamily,
), ),
); );
} }
/// Extension method to easily access Conduit theme from BuildContext /// Extension method to easily access Conduit theme from BuildContext

View File

@@ -31,7 +31,8 @@ class _ChatActionButtonState extends ConsumerState<ChatActionButton> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = context.conduitTheme; final theme = context.conduitTheme;
final hapticEnabled = ref.read(hapticEnabledProvider); final hapticEnabled = ref.read(hapticEnabledProvider);
final radius = widget.borderRadius ?? BorderRadius.circular(AppBorderRadius.lg); final radius =
widget.borderRadius ?? BorderRadius.circular(AppBorderRadius.lg);
final overlay = theme.buttonPrimary.withValues(alpha: 0.08); final overlay = theme.buttonPrimary.withValues(alpha: 0.08);
return Tooltip( return Tooltip(
@@ -100,4 +101,3 @@ class _ChatActionButtonState extends ConsumerState<ChatActionButton> {
); );
} }
} }

View File

@@ -101,7 +101,8 @@ class ConduitButton extends ConsumerWidget {
), ),
child: isLoading child: isLoading
? Semantics( ? Semantics(
label: AppLocalizations.of(context)?.loadingContent ?? 'Loading', label:
AppLocalizations.of(context)?.loadingContent ?? 'Loading',
excludeSemantics: true, excludeSemantics: true,
child: SizedBox( child: SizedBox(
width: IconSize.small, width: IconSize.small,
@@ -206,7 +207,10 @@ class ConduitInput extends StatelessWidget {
SizedBox(height: Spacing.sm), SizedBox(height: Spacing.sm),
], ],
Semantics( Semantics(
label: semanticLabel ?? label ?? (AppLocalizations.of(context)?.inputField ?? 'Input field'), label:
semanticLabel ??
label ??
(AppLocalizations.of(context)?.inputField ?? 'Input field'),
textField: true, textField: true,
child: TextField( child: TextField(
controller: controller, controller: controller,
@@ -784,7 +788,10 @@ class AccessibleFormField extends StatelessWidget {
SizedBox(height: isCompact ? Spacing.xs : Spacing.sm), SizedBox(height: isCompact ? Spacing.xs : Spacing.sm),
], ],
Semantics( Semantics(
label: semanticLabel ?? label ?? (AppLocalizations.of(context)?.inputField ?? 'Input field'), label:
semanticLabel ??
label ??
(AppLocalizations.of(context)?.inputField ?? 'Input field'),
textField: true, textField: true,
child: TextFormField( child: TextFormField(
controller: controller, controller: controller,

View File

@@ -348,8 +348,13 @@ class LoadingStateWrapper<T> extends StatelessWidget {
return asyncValue.when( return asyncValue.when(
data: builder, data: builder,
loading: () => showLoadingOverlay loading: () => showLoadingOverlay
? ConduitLoading.overlay(message: AppLocalizations.of(context)!.loadingContent) ? ConduitLoading.overlay(
: loadingWidget ?? ConduitLoading.primary(message: AppLocalizations.of(context)!.loadingContent), message: AppLocalizations.of(context)!.loadingContent,
)
: loadingWidget ??
ConduitLoading.primary(
message: AppLocalizations.of(context)!.loadingContent,
),
error: (error, stackTrace) { error: (error, stackTrace) {
if (errorBuilder != null) { if (errorBuilder != null) {
return errorBuilder!(error, stackTrace); return errorBuilder!(error, stackTrace);

View File

@@ -155,7 +155,11 @@ class ConduitMarkdownConfig {
); );
} }
static Widget _buildBase64Image(String dataUrl, BuildContext context, ConduitThemeExtension theme) { static Widget _buildBase64Image(
String dataUrl,
BuildContext context,
ConduitThemeExtension theme,
) {
try { try {
// Extract base64 part from data URL // Extract base64 part from data URL
final commaIndex = dataUrl.indexOf(','); final commaIndex = dataUrl.indexOf(',');

View File

@@ -20,7 +20,8 @@ class StreamingMarkdownWidget extends StatefulWidget {
}); });
@override @override
State<StreamingMarkdownWidget> createState() => _StreamingMarkdownWidgetState(); State<StreamingMarkdownWidget> createState() =>
_StreamingMarkdownWidgetState();
} }
class _StreamingMarkdownWidgetState extends State<StreamingMarkdownWidget> { class _StreamingMarkdownWidgetState extends State<StreamingMarkdownWidget> {
@@ -59,38 +60,38 @@ class _StreamingMarkdownWidgetState extends State<StreamingMarkdownWidget> {
if (fenceCount % 2 != 0) { if (fenceCount % 2 != 0) {
content += '\n```'; content += '\n```';
} }
// Fix incomplete bold/italic markers // Fix incomplete bold/italic markers
final boldCount = RegExp(r'\*\*').allMatches(content).length; final boldCount = RegExp(r'\*\*').allMatches(content).length;
if (boldCount % 2 != 0) { if (boldCount % 2 != 0) {
content += '**'; content += '**';
} }
final italicCount = RegExp(r'(?<!\*)\*(?!\*)').allMatches(content).length; final italicCount = RegExp(r'(?<!\*)\*(?!\*)').allMatches(content).length;
if (italicCount % 2 != 0) { if (italicCount % 2 != 0) {
content += '*'; content += '*';
} }
// Fix incomplete link brackets // Fix incomplete link brackets
final openBrackets = '['.allMatches(content).length; final openBrackets = '['.allMatches(content).length;
final closeBrackets = ']'.allMatches(content).length; final closeBrackets = ']'.allMatches(content).length;
if (openBrackets > closeBrackets) { if (openBrackets > closeBrackets) {
content += ']' * (openBrackets - closeBrackets); content += ']' * (openBrackets - closeBrackets);
} }
final openParens = '('.allMatches(content).length; final openParens = '('.allMatches(content).length;
final closeParens = ')'.allMatches(content).length; final closeParens = ')'.allMatches(content).length;
if (openParens > closeParens) { if (openParens > closeParens) {
content += ')' * (openParens - closeParens); content += ')' * (openParens - closeParens);
} }
return content; return content;
} }
@override @override
void didUpdateWidget(StreamingMarkdownWidget oldWidget) { void didUpdateWidget(StreamingMarkdownWidget oldWidget) {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
// Handle stream changes // Handle stream changes
if (widget.contentStream != oldWidget.contentStream) { if (widget.contentStream != oldWidget.contentStream) {
_streamSubscription?.cancel(); _streamSubscription?.cancel();
@@ -98,7 +99,7 @@ class _StreamingMarkdownWidgetState extends State<StreamingMarkdownWidget> {
_streamSubscription = widget.contentStream!.listen(_handleChunk); _streamSubscription = widget.contentStream!.listen(_handleChunk);
} }
} }
// Handle static content changes // Handle static content changes
if (widget.staticContent != oldWidget.staticContent) { if (widget.staticContent != oldWidget.staticContent) {
setState(() { setState(() {
@@ -182,9 +183,7 @@ class MarkdownWithLoading extends StatelessWidget {
if (isLoading && (content == null || content!.isEmpty)) { if (isLoading && (content == null || content!.isEmpty)) {
return Container( return Container(
padding: padding ?? const EdgeInsets.all(16), padding: padding ?? const EdgeInsets.all(16),
child: const Center( child: const Center(child: CircularProgressIndicator()),
child: CircularProgressIndicator(),
),
); );
} }
@@ -194,4 +193,4 @@ class MarkdownWithLoading extends StatelessWidget {
padding: padding, padding: padding,
); );
} }
} }

View File

@@ -22,8 +22,9 @@ class MiddleEllipsisText extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder( return LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final TextStyle effectiveStyle = final TextStyle effectiveStyle = DefaultTextStyle.of(
DefaultTextStyle.of(context).style.merge(style); context,
).style.merge(style);
final TextDirection direction = Directionality.of(context); final TextDirection direction = Directionality.of(context);
final double maxWidth = constraints.maxWidth; final double maxWidth = constraints.maxWidth;
@@ -74,8 +75,10 @@ class MiddleEllipsisText extends StatelessWidget {
? '' ? ''
: text.substring(text.length - rightCount); : text.substring(text.length - rightCount);
final trialSpan = final trialSpan = TextSpan(
TextSpan(text: '$start$ellipsis$end', style: effectiveStyle); text: '$start$ellipsis$end',
style: effectiveStyle,
);
final trialPainter = TextPainter( final trialPainter = TextPainter(
text: trialSpan, text: trialSpan,
textDirection: direction, textDirection: direction,
@@ -116,4 +119,3 @@ class MiddleEllipsisText extends StatelessWidget {
); );
} }
} }

View File

@@ -130,7 +130,9 @@ class _OptimizedListState<T> extends ConsumerState<OptimizedList<T>> {
return widget.emptyWidget ?? return widget.emptyWidget ??
ImprovedEmptyState( ImprovedEmptyState(
title: AppLocalizations.of(context)!.noItems, title: AppLocalizations.of(context)!.noItems,
subtitle: widget.emptyMessage ?? AppLocalizations.of(context)!.noItemsToDisplay, subtitle:
widget.emptyMessage ??
AppLocalizations.of(context)!.noItemsToDisplay,
icon: Icons.inbox_outlined, icon: Icons.inbox_outlined,
); );
} }
@@ -138,7 +140,8 @@ class _OptimizedListState<T> extends ConsumerState<OptimizedList<T>> {
// Build the list // Build the list
Widget listWidget; Widget listWidget;
final ScrollPhysics effectivePhysics = widget.physics ?? final ScrollPhysics effectivePhysics =
widget.physics ??
(widget.onRefresh != null (widget.onRefresh != null
? const AlwaysScrollableScrollPhysics() ? const AlwaysScrollableScrollPhysics()
: const ClampingScrollPhysics()); : const ClampingScrollPhysics());
@@ -276,14 +279,16 @@ class OptimizedSliverList<T> extends ConsumerWidget {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: child:
emptyWidget ?? emptyWidget ??
Builder(builder: (context) { Builder(
final l10n = AppLocalizations.of(context)!; builder: (context) {
return ImprovedEmptyState( final l10n = AppLocalizations.of(context)!;
title: l10n.noItems, return ImprovedEmptyState(
subtitle: emptyMessage ?? l10n.noItemsToDisplay, title: l10n.noItems,
icon: Icons.inbox_outlined, subtitle: emptyMessage ?? l10n.noItemsToDisplay,
); icon: Icons.inbox_outlined,
}), );
},
),
); );
} }

View File

@@ -9,15 +9,18 @@ class SheetHandle extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( return Center(
child: Container( child: Container(
margin: margin ?? const EdgeInsets.only(top: Spacing.sm, bottom: Spacing.md), margin:
margin ??
const EdgeInsets.only(top: Spacing.sm, bottom: Spacing.md),
width: 40, width: 40,
height: 4, height: 4,
decoration: BoxDecoration( decoration: BoxDecoration(
color: context.conduitTheme.textPrimary.withValues(alpha: Alpha.medium), color: context.conduitTheme.textPrimary.withValues(
alpha: Alpha.medium,
),
borderRadius: BorderRadius.circular(AppBorderRadius.xs), borderRadius: BorderRadius.circular(AppBorderRadius.xs),
), ),
), ),
); );
} }
} }

View File

@@ -145,8 +145,10 @@ class ThemedDialogs {
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(AppBorderRadius.md), borderRadius: BorderRadius.circular(AppBorderRadius.md),
borderSide: borderSide: BorderSide(
BorderSide(color: theme.buttonPrimary, width: 1), color: theme.buttonPrimary,
width: 1,
),
), ),
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
horizontal: Spacing.md, horizontal: Spacing.md,
@@ -155,8 +157,8 @@ class ThemedDialogs {
), ),
onSubmitted: (v) { onSubmitted: (v) {
final trimmed = v.trim(); final trimmed = v.trim();
final unchanged = (initialValue != null && final unchanged =
trimmed == initialValue.trim()); (initialValue != null && trimmed == initialValue.trim());
if (trimmed.isEmpty || unchanged) return; if (trimmed.isEmpty || unchanged) return;
Navigator.of(ctx).pop(trimmed); Navigator.of(ctx).pop(trimmed);
}, },

View File

@@ -56,12 +56,16 @@ Future<void> main(List<String> args) async {
if (trPh == null) { if (trPh == null) {
// If string exists but no meta placeholders, warn only. // If string exists but no meta placeholders, warn only.
if (keys.contains(k) && basePh.isNotEmpty) { if (keys.contains(k) && basePh.isNotEmpty) {
warnings.add('[${f.path}] Key "$k" missing @meta placeholders; base has ${basePh.toList()..sort()}'); warnings.add(
'[${f.path}] Key "$k" missing @meta placeholders; base has ${basePh.toList()..sort()}',
);
} }
continue; continue;
} }
if (basePh.length != trPh.length || !basePh.containsAll(trPh)) { if (basePh.length != trPh.length || !basePh.containsAll(trPh)) {
warnings.add('[${f.path}] Placeholder mismatch for "$k": expected ${basePh.toList()..sort()}, got ${trPh.toList()..sort()}'); warnings.add(
'[${f.path}] Placeholder mismatch for "$k": expected ${basePh.toList()..sort()}, got ${trPh.toList()..sort()}',
);
} }
} }
} }
@@ -96,9 +100,7 @@ Map<String, dynamic> _readJson(File f) {
} }
Set<String> _nonMetaKeys(Map<String, dynamic> m) { Set<String> _nonMetaKeys(Map<String, dynamic> m) {
return m.keys return m.keys.where((k) => !k.startsWith('@') && k != '@@locale').toSet();
.where((k) => !k.startsWith('@') && k != '@@locale')
.toSet();
} }
Map<String, Set<String>> _placeholdersMap(Map<String, dynamic> m) { Map<String, Set<String>> _placeholdersMap(Map<String, dynamic> m) {

View File

@@ -42,7 +42,9 @@ Future<void> main() async {
} }
if (missingMeta.isEmpty && missingDescription.isEmpty) { if (missingMeta.isEmpty && missingDescription.isEmpty) {
stdout.writeln('ARB descriptions check passed: all keys have @meta.description.'); stdout.writeln(
'ARB descriptions check passed: all keys have @meta.description.',
);
return; return;
} }
@@ -53,11 +55,12 @@ Future<void> main() async {
} }
} }
if (missingDescription.isNotEmpty) { if (missingDescription.isNotEmpty) {
stderr.writeln('Missing description in @meta for keys (${missingDescription.length}):'); stderr.writeln(
'Missing description in @meta for keys (${missingDescription.length}):',
);
for (final k in missingDescription) { for (final k in missingDescription) {
stderr.writeln(' - $k'); stderr.writeln(' - $k');
} }
} }
exit(1); exit(1);
} }