refactor: app customization
This commit is contained in:
@@ -43,13 +43,11 @@ class EnhancedImageAttachment extends ConsumerStatefulWidget {
|
|||||||
|
|
||||||
class _EnhancedImageAttachmentState
|
class _EnhancedImageAttachmentState
|
||||||
extends ConsumerState<EnhancedImageAttachment>
|
extends ConsumerState<EnhancedImageAttachment>
|
||||||
with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
String? _cachedImageData;
|
String? _cachedImageData;
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
String? _errorMessage;
|
String? _errorMessage;
|
||||||
late AnimationController _animationController;
|
// Removed unused animation and state flags
|
||||||
late Animation<double> _fadeAnimation;
|
|
||||||
bool _hasShownContent = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get wantKeepAlive => true;
|
bool get wantKeepAlive => true;
|
||||||
@@ -57,14 +55,6 @@ class _EnhancedImageAttachmentState
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_animationController = AnimationController(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
vsync: this,
|
|
||||||
);
|
|
||||||
_fadeAnimation = CurvedAnimation(
|
|
||||||
parent: _animationController,
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
);
|
|
||||||
// Defer loading until after first frame to avoid accessing inherited widgets
|
// Defer loading until after first frame to avoid accessing inherited widgets
|
||||||
// (e.g., Localizations) during initState
|
// (e.g., Localizations) during initState
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
@@ -75,7 +65,6 @@ class _EnhancedImageAttachmentState
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_animationController.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,11 +76,7 @@ class _EnhancedImageAttachmentState
|
|||||||
setState(() {
|
setState(() {
|
||||||
_cachedImageData = _globalImageCache[widget.attachmentId];
|
_cachedImageData = _globalImageCache[widget.attachmentId];
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
_hasShownContent = true;
|
|
||||||
});
|
});
|
||||||
if (!widget.disableAnimation) {
|
|
||||||
_animationController.forward();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -119,11 +104,7 @@ class _EnhancedImageAttachmentState
|
|||||||
setState(() {
|
setState(() {
|
||||||
_cachedImageData = widget.attachmentId;
|
_cachedImageData = widget.attachmentId;
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
_hasShownContent = true;
|
|
||||||
});
|
});
|
||||||
if (!widget.disableAnimation) {
|
|
||||||
_animationController.forward();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -140,11 +121,7 @@ class _EnhancedImageAttachmentState
|
|||||||
setState(() {
|
setState(() {
|
||||||
_cachedImageData = fullUrl;
|
_cachedImageData = fullUrl;
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
_hasShownContent = true;
|
|
||||||
});
|
});
|
||||||
if (!widget.disableAnimation) {
|
|
||||||
_animationController.forward();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -214,11 +191,7 @@ class _EnhancedImageAttachmentState
|
|||||||
setState(() {
|
setState(() {
|
||||||
_cachedImageData = fileContent;
|
_cachedImageData = fileContent;
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
_hasShownContent = true;
|
|
||||||
});
|
});
|
||||||
if (!widget.disableAnimation) {
|
|
||||||
_animationController.forward();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final error = l10n.failedToLoadImage(e.toString());
|
final error = l10n.failedToLoadImage(e.toString());
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import '../../../core/services/settings_service.dart';
|
|||||||
import '../../../shared/theme/theme_extensions.dart';
|
import '../../../shared/theme/theme_extensions.dart';
|
||||||
import '../../../shared/widgets/conduit_components.dart';
|
import '../../../shared/widgets/conduit_components.dart';
|
||||||
import '../../../shared/utils/ui_utils.dart';
|
import '../../../shared/utils/ui_utils.dart';
|
||||||
|
import '../../../core/providers/app_providers.dart';
|
||||||
|
import '../../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
class AppCustomizationPage extends ConsumerWidget {
|
class AppCustomizationPage extends ConsumerWidget {
|
||||||
const AppCustomizationPage({super.key});
|
const AppCustomizationPage({super.key});
|
||||||
@@ -15,6 +17,13 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final settings = ref.watch(appSettingsProvider);
|
final settings = ref.watch(appSettingsProvider);
|
||||||
|
final themeMode = ref.watch(themeModeProvider);
|
||||||
|
final platformBrightness = MediaQuery.platformBrightnessOf(context);
|
||||||
|
final bool isDarkEffective =
|
||||||
|
themeMode == ThemeMode.dark ||
|
||||||
|
(themeMode == ThemeMode.system &&
|
||||||
|
platformBrightness == Brightness.dark);
|
||||||
|
final locale = ref.watch(localeProvider);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: context.conduitTheme.surfaceBackground,
|
backgroundColor: context.conduitTheme.surfaceBackground,
|
||||||
@@ -30,10 +39,10 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
color: context.conduitTheme.textPrimary,
|
color: context.conduitTheme.textPrimary,
|
||||||
),
|
),
|
||||||
onPressed: () => Navigator.of(context).maybePop(),
|
onPressed: () => Navigator.of(context).maybePop(),
|
||||||
tooltip: 'Back',
|
tooltip: AppLocalizations.of(context)!.back,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'App Customization',
|
AppLocalizations.of(context)!.appCustomization,
|
||||||
style: AppTypography.headlineSmallStyle.copyWith(
|
style: AppTypography.headlineSmallStyle.copyWith(
|
||||||
color: context.conduitTheme.textPrimary,
|
color: context.conduitTheme.textPrimary,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@@ -47,7 +56,7 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Display',
|
AppLocalizations.of(context)!.display,
|
||||||
style: context.conduitTheme.headingSmall?.copyWith(
|
style: context.conduitTheme.headingSmall?.copyWith(
|
||||||
color: context.conduitTheme.textPrimary,
|
color: context.conduitTheme.textPrimary,
|
||||||
),
|
),
|
||||||
@@ -57,6 +66,150 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
// Dark mode toggle
|
||||||
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: Spacing.listItemPadding,
|
||||||
|
vertical: Spacing.sm,
|
||||||
|
),
|
||||||
|
leading: Container(
|
||||||
|
padding: const EdgeInsets.all(Spacing.sm),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.conduitTheme.buttonPrimary
|
||||||
|
.withValues(alpha: Alpha.highlight),
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(AppBorderRadius.small),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
UiUtils.platformIcon(
|
||||||
|
ios: CupertinoIcons.moon_stars,
|
||||||
|
android: Icons.dark_mode,
|
||||||
|
),
|
||||||
|
color: context.conduitTheme.buttonPrimary,
|
||||||
|
size: IconSize.medium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context)!.darkMode,
|
||||||
|
style: context.conduitTheme.bodyLarge?.copyWith(
|
||||||
|
color: context.conduitTheme.textPrimary,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
themeMode == ThemeMode.system
|
||||||
|
? AppLocalizations.of(context)!.followingSystem(
|
||||||
|
platformBrightness == Brightness.dark
|
||||||
|
? AppLocalizations.of(context)!.themeDark
|
||||||
|
: AppLocalizations.of(context)!.themeLight,
|
||||||
|
)
|
||||||
|
: (isDarkEffective
|
||||||
|
? AppLocalizations.of(context)!
|
||||||
|
.currentlyUsingDarkTheme
|
||||||
|
: AppLocalizations.of(context)!
|
||||||
|
.currentlyUsingLightTheme),
|
||||||
|
style: context.conduitTheme.bodySmall?.copyWith(
|
||||||
|
color: context.conduitTheme.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Switch.adaptive(
|
||||||
|
value: isDarkEffective,
|
||||||
|
onChanged: (value) {
|
||||||
|
ref
|
||||||
|
.read(themeModeProvider.notifier)
|
||||||
|
.setTheme(value ? ThemeMode.dark : ThemeMode.light);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
final newValue = !isDarkEffective;
|
||||||
|
ref
|
||||||
|
.read(themeModeProvider.notifier)
|
||||||
|
.setTheme(
|
||||||
|
newValue ? ThemeMode.dark : ThemeMode.light);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
||||||
|
|
||||||
|
// App language selector
|
||||||
|
Builder(builder: (context) {
|
||||||
|
final currentCode = locale?.languageCode ?? 'system';
|
||||||
|
final label = () {
|
||||||
|
switch (currentCode) {
|
||||||
|
case 'en':
|
||||||
|
return 'English';
|
||||||
|
case 'de':
|
||||||
|
return 'Deutsch';
|
||||||
|
case 'fr':
|
||||||
|
return 'Français';
|
||||||
|
case 'it':
|
||||||
|
return 'Italiano';
|
||||||
|
default:
|
||||||
|
return 'System';
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: Spacing.listItemPadding,
|
||||||
|
vertical: Spacing.sm,
|
||||||
|
),
|
||||||
|
leading: Container(
|
||||||
|
padding: const EdgeInsets.all(Spacing.sm),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.conduitTheme.buttonPrimary
|
||||||
|
.withValues(alpha: Alpha.highlight),
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(AppBorderRadius.small),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
UiUtils.platformIcon(
|
||||||
|
ios: CupertinoIcons.globe,
|
||||||
|
android: Icons.language,
|
||||||
|
),
|
||||||
|
color: context.conduitTheme.buttonPrimary,
|
||||||
|
size: IconSize.medium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context)!.appLanguage,
|
||||||
|
style: context.conduitTheme.bodyLarge?.copyWith(
|
||||||
|
color: context.conduitTheme.textPrimary,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(
|
||||||
|
label,
|
||||||
|
style: context.conduitTheme.bodySmall?.copyWith(
|
||||||
|
color: context.conduitTheme.textSecondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Icon(
|
||||||
|
UiUtils.platformIcon(
|
||||||
|
ios: CupertinoIcons.chevron_right,
|
||||||
|
android: Icons.chevron_right,
|
||||||
|
),
|
||||||
|
color: context.conduitTheme.iconSecondary,
|
||||||
|
size: IconSize.small,
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
final selected = await _showLanguageSelector(
|
||||||
|
context, currentCode);
|
||||||
|
if (selected != null) {
|
||||||
|
if (selected == 'system') {
|
||||||
|
await ref
|
||||||
|
.read(localeProvider.notifier)
|
||||||
|
.setLocale(null);
|
||||||
|
} else {
|
||||||
|
await ref
|
||||||
|
.read(localeProvider.notifier)
|
||||||
|
.setLocale(Locale(selected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
||||||
|
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: Spacing.listItemPadding,
|
horizontal: Spacing.listItemPadding,
|
||||||
@@ -65,14 +218,15 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
// Use platform defaults for switch colors to match theme
|
// Use platform defaults for switch colors to match theme
|
||||||
value: settings.omitProviderInModelName,
|
value: settings.omitProviderInModelName,
|
||||||
title: Text(
|
title: Text(
|
||||||
'Hide provider in model names',
|
AppLocalizations.of(context)!.hideProviderInModelNames,
|
||||||
style: context.conduitTheme.bodyLarge?.copyWith(
|
style: context.conduitTheme.bodyLarge?.copyWith(
|
||||||
color: context.conduitTheme.textPrimary,
|
color: context.conduitTheme.textPrimary,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'Show names like "gpt-4o" instead of "openai/gpt-4o".',
|
AppLocalizations.of(context)!
|
||||||
|
.hideProviderInModelNamesDescription,
|
||||||
style: context.conduitTheme.bodySmall?.copyWith(
|
style: context.conduitTheme.bodySmall?.copyWith(
|
||||||
color: context.conduitTheme.textSecondary,
|
color: context.conduitTheme.textSecondary,
|
||||||
),
|
),
|
||||||
@@ -105,7 +259,7 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
|
|
||||||
const SizedBox(height: Spacing.lg),
|
const SizedBox(height: Spacing.lg),
|
||||||
Text(
|
Text(
|
||||||
'Realtime',
|
AppLocalizations.of(context)!.realtime,
|
||||||
style: context.conduitTheme.headingSmall?.copyWith(
|
style: context.conduitTheme.headingSmall?.copyWith(
|
||||||
color: context.conduitTheme.textPrimary,
|
color: context.conduitTheme.textPrimary,
|
||||||
),
|
),
|
||||||
@@ -138,14 +292,14 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'Transport mode',
|
AppLocalizations.of(context)!.transportMode,
|
||||||
style: context.conduitTheme.bodyLarge?.copyWith(
|
style: context.conduitTheme.bodyLarge?.copyWith(
|
||||||
color: context.conduitTheme.textPrimary,
|
color: context.conduitTheme.textPrimary,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'Choose how the app connects for realtime updates.',
|
AppLocalizations.of(context)!.transportModeDescription,
|
||||||
style: context.conduitTheme.bodySmall?.copyWith(
|
style: context.conduitTheme.bodySmall?.copyWith(
|
||||||
color: context.conduitTheme.textSecondary,
|
color: context.conduitTheme.textSecondary,
|
||||||
),
|
),
|
||||||
@@ -166,21 +320,23 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
.read(appSettingsProvider.notifier)
|
.read(appSettingsProvider.notifier)
|
||||||
.setSocketTransportMode(v);
|
.setSocketTransportMode(v);
|
||||||
},
|
},
|
||||||
items: const [
|
items: [
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: 'auto',
|
value: 'auto',
|
||||||
child: Text('Auto (Polling + WebSocket)'),
|
child: Text(AppLocalizations.of(context)!
|
||||||
|
.transportModeAuto),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: 'ws',
|
value: 'ws',
|
||||||
child: Text('WebSocket only'),
|
child: Text(AppLocalizations.of(context)!
|
||||||
|
.transportModeWs),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Mode',
|
labelText: AppLocalizations.of(context)!.mode,
|
||||||
border: OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
isDense: true,
|
isDense: true,
|
||||||
contentPadding: EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: 12,
|
horizontal: 12,
|
||||||
vertical: 10,
|
vertical: 10,
|
||||||
),
|
),
|
||||||
@@ -196,8 +352,10 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
settings.socketTransportMode == 'auto'
|
settings.socketTransportMode == 'auto'
|
||||||
? 'More robust on restrictive networks. Upgrades to WebSocket when possible.'
|
? AppLocalizations.of(context)!
|
||||||
: 'Lower overhead, but may fail behind strict proxies/firewalls.',
|
.transportModeAutoInfo
|
||||||
|
: AppLocalizations.of(context)!
|
||||||
|
.transportModeWsInfo,
|
||||||
style: context.conduitTheme.caption?.copyWith(
|
style: context.conduitTheme.caption?.copyWith(
|
||||||
color: context.conduitTheme.textSecondary,
|
color: context.conduitTheme.textSecondary,
|
||||||
),
|
),
|
||||||
@@ -211,4 +369,56 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String?> _showLanguageSelector(BuildContext context, String current) {
|
||||||
|
return showModalBottomSheet<String>(
|
||||||
|
context: context,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) => Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: context.conduitTheme.surfaceBackground,
|
||||||
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
top: Radius.circular(AppBorderRadius.modal),
|
||||||
|
),
|
||||||
|
boxShadow: ConduitShadows.modal,
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
|
top: false,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: Spacing.sm),
|
||||||
|
ListTile(
|
||||||
|
title: Text(AppLocalizations.of(context)!.system),
|
||||||
|
trailing: current == 'system' ? const Icon(Icons.check) : null,
|
||||||
|
onTap: () => Navigator.pop(context, 'system'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(AppLocalizations.of(context)!.english),
|
||||||
|
trailing: current == 'en' ? const Icon(Icons.check) : null,
|
||||||
|
onTap: () => Navigator.pop(context, 'en'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(AppLocalizations.of(context)!.deutsch),
|
||||||
|
trailing: current == 'de' ? const Icon(Icons.check) : null,
|
||||||
|
onTap: () => Navigator.pop(context, 'de'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(AppLocalizations.of(context)!.francais),
|
||||||
|
trailing: current == 'fr' ? const Icon(Icons.check) : null,
|
||||||
|
onTap: () => Navigator.pop(context, 'fr'),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(AppLocalizations.of(context)!.italiano),
|
||||||
|
trailing: current == 'it' ? const Icon(Icons.check) : null,
|
||||||
|
onTap: () => Navigator.pop(context, 'it'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: Spacing.sm),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,17 +229,13 @@ class ProfilePage extends ConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
_buildDefaultModelTile(context, ref),
|
_buildDefaultModelTile(context, ref),
|
||||||
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
||||||
_buildThemeToggleTile(context, ref),
|
|
||||||
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
|
||||||
_buildLanguageTile(context, ref),
|
|
||||||
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
|
||||||
_buildAccountOption(
|
_buildAccountOption(
|
||||||
icon: UiUtils.platformIcon(
|
icon: UiUtils.platformIcon(
|
||||||
ios: CupertinoIcons.slider_horizontal_3,
|
ios: CupertinoIcons.slider_horizontal_3,
|
||||||
android: Icons.tune,
|
android: Icons.tune,
|
||||||
),
|
),
|
||||||
title: 'App Customization',
|
title: AppLocalizations.of(context)!.appCustomization,
|
||||||
subtitle: 'Personalize how names and UI display',
|
subtitle: AppLocalizations.of(context)!.appCustomizationSubtitle,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@@ -463,199 +459,7 @@ class ProfilePage extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLanguageTile(BuildContext context, WidgetRef ref) {
|
// Theme and language controls moved to AppCustomizationPage.
|
||||||
final locale = ref.watch(localeProvider);
|
|
||||||
final currentCode = locale?.languageCode ?? 'system';
|
|
||||||
final label = () {
|
|
||||||
switch (currentCode) {
|
|
||||||
case 'en':
|
|
||||||
return 'English';
|
|
||||||
case 'de':
|
|
||||||
return 'Deutsch';
|
|
||||||
case 'fr':
|
|
||||||
return 'Français';
|
|
||||||
case 'it':
|
|
||||||
return 'Italiano';
|
|
||||||
default:
|
|
||||||
return 'System';
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
|
|
||||||
return ListTile(
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: Spacing.listItemPadding,
|
|
||||||
vertical: Spacing.sm,
|
|
||||||
),
|
|
||||||
leading: Container(
|
|
||||||
padding: const EdgeInsets.all(Spacing.sm),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: context.conduitTheme.buttonPrimary.withValues(
|
|
||||||
alpha: Alpha.highlight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
UiUtils.platformIcon(
|
|
||||||
ios: CupertinoIcons.globe,
|
|
||||||
android: Icons.language,
|
|
||||||
),
|
|
||||||
color: context.conduitTheme.buttonPrimary,
|
|
||||||
size: IconSize.medium,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!.appLanguage,
|
|
||||||
style: context.conduitTheme.bodyLarge?.copyWith(
|
|
||||||
color: context.conduitTheme.textPrimary,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
label,
|
|
||||||
style: context.conduitTheme.bodySmall?.copyWith(
|
|
||||||
color: context.conduitTheme.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing: Icon(
|
|
||||||
UiUtils.platformIcon(
|
|
||||||
ios: CupertinoIcons.chevron_right,
|
|
||||||
android: Icons.chevron_right,
|
|
||||||
),
|
|
||||||
color: context.conduitTheme.iconSecondary,
|
|
||||||
size: IconSize.small,
|
|
||||||
),
|
|
||||||
onTap: () async {
|
|
||||||
final selected = await _showLanguageSelector(context, currentCode);
|
|
||||||
if (selected != null) {
|
|
||||||
if (selected == 'system') {
|
|
||||||
await ref.read(localeProvider.notifier).setLocale(null);
|
|
||||||
} else {
|
|
||||||
await ref.read(localeProvider.notifier).setLocale(Locale(selected));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String?> _showLanguageSelector(BuildContext context, String current) {
|
|
||||||
return showModalBottomSheet<String>(
|
|
||||||
context: context,
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
isScrollControlled: true,
|
|
||||||
builder: (context) => Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: context.conduitTheme.surfaceBackground,
|
|
||||||
borderRadius: const BorderRadius.vertical(
|
|
||||||
top: Radius.circular(AppBorderRadius.modal),
|
|
||||||
),
|
|
||||||
boxShadow: ConduitShadows.modal,
|
|
||||||
),
|
|
||||||
child: SafeArea(
|
|
||||||
top: false,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: Spacing.sm),
|
|
||||||
ListTile(
|
|
||||||
title: Text(AppLocalizations.of(context)!.system),
|
|
||||||
trailing: current == 'system' ? const Icon(Icons.check) : null,
|
|
||||||
onTap: () => Navigator.pop(context, 'system'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text(AppLocalizations.of(context)!.english),
|
|
||||||
trailing: current == 'en' ? const Icon(Icons.check) : null,
|
|
||||||
onTap: () => Navigator.pop(context, 'en'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text(AppLocalizations.of(context)!.deutsch),
|
|
||||||
trailing: current == 'de' ? const Icon(Icons.check) : null,
|
|
||||||
onTap: () => Navigator.pop(context, 'de'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text(AppLocalizations.of(context)!.francais),
|
|
||||||
trailing: current == 'fr' ? const Icon(Icons.check) : null,
|
|
||||||
onTap: () => Navigator.pop(context, 'fr'),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: Text(AppLocalizations.of(context)!.italiano),
|
|
||||||
trailing: current == 'it' ? const Icon(Icons.check) : null,
|
|
||||||
onTap: () => Navigator.pop(context, 'it'),
|
|
||||||
),
|
|
||||||
const SizedBox(height: Spacing.sm),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildThemeToggleTile(BuildContext context, WidgetRef ref) {
|
|
||||||
final themeMode = ref.watch(themeModeProvider);
|
|
||||||
final platformBrightness = MediaQuery.platformBrightnessOf(context);
|
|
||||||
final bool isDarkEffective =
|
|
||||||
themeMode == ThemeMode.dark ||
|
|
||||||
(themeMode == ThemeMode.system &&
|
|
||||||
platformBrightness == Brightness.dark);
|
|
||||||
|
|
||||||
return ListTile(
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: Spacing.listItemPadding,
|
|
||||||
vertical: Spacing.sm,
|
|
||||||
),
|
|
||||||
leading: Container(
|
|
||||||
padding: const EdgeInsets.all(Spacing.sm),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: context.conduitTheme.buttonPrimary.withValues(
|
|
||||||
alpha: Alpha.highlight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
UiUtils.platformIcon(
|
|
||||||
ios: CupertinoIcons.moon_stars,
|
|
||||||
android: Icons.dark_mode,
|
|
||||||
),
|
|
||||||
color: context.conduitTheme.buttonPrimary,
|
|
||||||
size: IconSize.medium,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!.darkMode,
|
|
||||||
style: context.conduitTheme.bodyLarge?.copyWith(
|
|
||||||
color: context.conduitTheme.textPrimary,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
themeMode == ThemeMode.system
|
|
||||||
? AppLocalizations.of(context)!.followingSystem(
|
|
||||||
platformBrightness == Brightness.dark
|
|
||||||
? AppLocalizations.of(context)!.themeDark
|
|
||||||
: AppLocalizations.of(context)!.themeLight,
|
|
||||||
)
|
|
||||||
: (isDarkEffective
|
|
||||||
? AppLocalizations.of(context)!.currentlyUsingDarkTheme
|
|
||||||
: AppLocalizations.of(context)!.currentlyUsingLightTheme),
|
|
||||||
style: context.conduitTheme.bodySmall?.copyWith(
|
|
||||||
color: context.conduitTheme.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing: Switch.adaptive(
|
|
||||||
value: isDarkEffective,
|
|
||||||
onChanged: (value) {
|
|
||||||
ref
|
|
||||||
.read(themeModeProvider.notifier)
|
|
||||||
.setTheme(value ? ThemeMode.dark : ThemeMode.light);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
final newValue = !isDarkEffective;
|
|
||||||
ref
|
|
||||||
.read(themeModeProvider.notifier)
|
|
||||||
.setTheme(newValue ? ThemeMode.dark : ThemeMode.light);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAboutTile(BuildContext context) {
|
Widget _buildAboutTile(BuildContext context) {
|
||||||
return _buildAccountOption(
|
return _buildAccountOption(
|
||||||
|
|||||||
@@ -259,4 +259,18 @@
|
|||||||
"description": "Zeigt an, wie lange der Assistent nachgedacht hat.",
|
"description": "Zeigt an, wie lange der Assistent nachgedacht hat.",
|
||||||
"placeholders": {"duration": {"type": "String", "example": "3 s"}}
|
"placeholders": {"duration": {"type": "String", "example": "3 s"}}
|
||||||
}
|
}
|
||||||
|
,
|
||||||
|
"appCustomization": "App-Anpassung",
|
||||||
|
"appCustomizationSubtitle": "Personalisieren, wie Namen und UI angezeigt werden",
|
||||||
|
"display": "Anzeige",
|
||||||
|
"realtime": "Echtzeit",
|
||||||
|
"hideProviderInModelNames": "Anbieter in Modellnamen ausblenden",
|
||||||
|
"hideProviderInModelNamesDescription": "Zeige Namen wie \"gpt-4o\" statt \"openai/gpt-4o\".",
|
||||||
|
"transportMode": "Transportmodus",
|
||||||
|
"transportModeDescription": "Wähle, wie die App für Echtzeit-Updates verbindet.",
|
||||||
|
"mode": "Modus",
|
||||||
|
"transportModeAuto": "Auto (Polling + WebSocket)",
|
||||||
|
"transportModeWs": "Nur WebSocket",
|
||||||
|
"transportModeAutoInfo": "Robuster in restriktiven Netzwerken. Wechselt nach Möglichkeit zu WebSocket.",
|
||||||
|
"transportModeWsInfo": "Geringerer Overhead, kann jedoch hinter strikten Proxys/Firewalls fehlschlagen."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,4 +283,18 @@
|
|||||||
"description": "Shows how long the assistant thought before replying.",
|
"description": "Shows how long the assistant thought before replying.",
|
||||||
"placeholders": {"duration": {"type": "String", "example": "3s"}}
|
"placeholders": {"duration": {"type": "String", "example": "3s"}}
|
||||||
}
|
}
|
||||||
|
,
|
||||||
|
"appCustomization": "App Customization",
|
||||||
|
"appCustomizationSubtitle": "Personalize how names and UI display",
|
||||||
|
"display": "Display",
|
||||||
|
"realtime": "Realtime",
|
||||||
|
"hideProviderInModelNames": "Hide provider in model names",
|
||||||
|
"hideProviderInModelNamesDescription": "Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".",
|
||||||
|
"transportMode": "Transport mode",
|
||||||
|
"transportModeDescription": "Choose how the app connects for realtime updates.",
|
||||||
|
"mode": "Mode",
|
||||||
|
"transportModeAuto": "Auto (Polling + WebSocket)",
|
||||||
|
"transportModeWs": "WebSocket only",
|
||||||
|
"transportModeAutoInfo": "More robust on restrictive networks. Upgrades to WebSocket when possible.",
|
||||||
|
"transportModeWsInfo": "Lower overhead, but may fail behind strict proxies/firewalls."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,4 +259,18 @@
|
|||||||
"description": "Indique la durée de réflexion de l'assistant.",
|
"description": "Indique la durée de réflexion de l'assistant.",
|
||||||
"placeholders": {"duration": {"type": "String", "example": "3 s"}}
|
"placeholders": {"duration": {"type": "String", "example": "3 s"}}
|
||||||
}
|
}
|
||||||
|
,
|
||||||
|
"appCustomization": "Personnalisation de l'app",
|
||||||
|
"appCustomizationSubtitle": "Personnalisez l'affichage des noms et de l'UI",
|
||||||
|
"display": "Affichage",
|
||||||
|
"realtime": "Temps réel",
|
||||||
|
"hideProviderInModelNames": "Masquer le fournisseur dans les noms de modèles",
|
||||||
|
"hideProviderInModelNamesDescription": "Afficher des noms comme \"gpt-4o\" au lieu de \"openai/gpt-4o\".",
|
||||||
|
"transportMode": "Mode de transport",
|
||||||
|
"transportModeDescription": "Choisissez comment l'app se connecte pour les mises à jour en temps réel.",
|
||||||
|
"mode": "Mode",
|
||||||
|
"transportModeAuto": "Auto (Polling + WebSocket)",
|
||||||
|
"transportModeWs": "WebSocket uniquement",
|
||||||
|
"transportModeAutoInfo": "Plus robuste sur les réseaux restrictifs. Passe à WebSocket lorsque possible.",
|
||||||
|
"transportModeWsInfo": "Moins de surcharge, mais peut échouer derrière des proxys/firewalls stricts."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,4 +259,18 @@
|
|||||||
"description": "Mostra per quanto tempo ha pensato l'assistente.",
|
"description": "Mostra per quanto tempo ha pensato l'assistente.",
|
||||||
"placeholders": {"duration": {"type": "String", "example": "3 s"}}
|
"placeholders": {"duration": {"type": "String", "example": "3 s"}}
|
||||||
}
|
}
|
||||||
|
,
|
||||||
|
"appCustomization": "Personalizzazione app",
|
||||||
|
"appCustomizationSubtitle": "Personalizza la visualizzazione dei nomi e dell'UI",
|
||||||
|
"display": "Schermo",
|
||||||
|
"realtime": "Tempo reale",
|
||||||
|
"hideProviderInModelNames": "Nascondi provider nei nomi dei modelli",
|
||||||
|
"hideProviderInModelNamesDescription": "Mostra nomi come \"gpt-4o\" invece di \"openai/gpt-4o\".",
|
||||||
|
"transportMode": "Modalità di trasporto",
|
||||||
|
"transportModeDescription": "Scegli come l'app si connette per gli aggiornamenti in tempo reale.",
|
||||||
|
"mode": "Modalità",
|
||||||
|
"transportModeAuto": "Auto (Polling + WebSocket)",
|
||||||
|
"transportModeWs": "Solo WebSocket",
|
||||||
|
"transportModeAutoInfo": "Più robusto nelle reti restrittive. Passa a WebSocket quando possibile.",
|
||||||
|
"transportModeWsInfo": "Minore overhead, ma può fallire dietro proxy/firewall restrittivi."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1230,13 +1230,13 @@ abstract class AppLocalizations {
|
|||||||
/// No description provided for @appLanguage.
|
/// No description provided for @appLanguage.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'App language'**
|
/// **'App Language'**
|
||||||
String get appLanguage;
|
String get appLanguage;
|
||||||
|
|
||||||
/// No description provided for @darkMode.
|
/// No description provided for @darkMode.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Dark mode'**
|
/// **'Dark Mode'**
|
||||||
String get darkMode;
|
String get darkMode;
|
||||||
|
|
||||||
/// No description provided for @webSearch.
|
/// No description provided for @webSearch.
|
||||||
@@ -1472,6 +1472,84 @@ abstract class AppLocalizations {
|
|||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Thought for {duration}'**
|
/// **'Thought for {duration}'**
|
||||||
String thoughtForDuration(String duration);
|
String thoughtForDuration(String duration);
|
||||||
|
|
||||||
|
/// No description provided for @appCustomization.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'App Customization'**
|
||||||
|
String get appCustomization;
|
||||||
|
|
||||||
|
/// No description provided for @appCustomizationSubtitle.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Personalize how names and UI display'**
|
||||||
|
String get appCustomizationSubtitle;
|
||||||
|
|
||||||
|
/// No description provided for @display.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Display'**
|
||||||
|
String get display;
|
||||||
|
|
||||||
|
/// No description provided for @realtime.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Realtime'**
|
||||||
|
String get realtime;
|
||||||
|
|
||||||
|
/// No description provided for @hideProviderInModelNames.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Hide provider in model names'**
|
||||||
|
String get hideProviderInModelNames;
|
||||||
|
|
||||||
|
/// No description provided for @hideProviderInModelNamesDescription.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".'**
|
||||||
|
String get hideProviderInModelNamesDescription;
|
||||||
|
|
||||||
|
/// No description provided for @transportMode.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Transport mode'**
|
||||||
|
String get transportMode;
|
||||||
|
|
||||||
|
/// No description provided for @transportModeDescription.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Choose how the app connects for realtime updates.'**
|
||||||
|
String get transportModeDescription;
|
||||||
|
|
||||||
|
/// No description provided for @mode.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Mode'**
|
||||||
|
String get mode;
|
||||||
|
|
||||||
|
/// No description provided for @transportModeAuto.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Auto (Polling + WebSocket)'**
|
||||||
|
String get transportModeAuto;
|
||||||
|
|
||||||
|
/// No description provided for @transportModeWs.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'WebSocket only'**
|
||||||
|
String get transportModeWs;
|
||||||
|
|
||||||
|
/// No description provided for @transportModeAutoInfo.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'More robust on restrictive networks. Upgrades to WebSocket when possible.'**
|
||||||
|
String get transportModeAutoInfo;
|
||||||
|
|
||||||
|
/// No description provided for @transportModeWsInfo.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Lower overhead, but may fail behind strict proxies/firewalls.'**
|
||||||
|
String get transportModeWsInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
|
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||||
|
|||||||
@@ -727,4 +727,43 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String thoughtForDuration(String duration) {
|
String thoughtForDuration(String duration) {
|
||||||
return 'Gedacht für $duration';
|
return 'Gedacht für $duration';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appCustomization => 'App-Anpassung';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appCustomizationSubtitle => 'Personalisieren, wie Namen und UI angezeigt werden';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get display => 'Anzeige';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get realtime => 'Echtzeit';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hideProviderInModelNames => 'Anbieter in Modellnamen ausblenden';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hideProviderInModelNamesDescription => 'Zeige Namen wie \"gpt-4o\" statt \"openai/gpt-4o\".';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportMode => 'Transportmodus';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeDescription => 'Wähle, wie die App für Echtzeit-Updates verbindet.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get mode => 'Modus';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeAuto => 'Auto (Polling + WebSocket)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeWs => 'Nur WebSocket';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeAutoInfo => 'Robuster in restriktiven Netzwerken. Wechselt nach Möglichkeit zu WebSocket.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeWsInfo => 'Geringerer Overhead, kann jedoch hinter strikten Proxys/Firewalls fehlschlagen.';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,4 +727,43 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String thoughtForDuration(String duration) {
|
String thoughtForDuration(String duration) {
|
||||||
return 'Thought for $duration';
|
return 'Thought for $duration';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appCustomization => 'App Customization';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appCustomizationSubtitle => 'Personalize how names and UI display';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get display => 'Display';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get realtime => 'Realtime';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hideProviderInModelNames => 'Hide provider in model names';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hideProviderInModelNamesDescription => 'Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportMode => 'Transport mode';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeDescription => 'Choose how the app connects for realtime updates.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get mode => 'Mode';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeAuto => 'Auto (Polling + WebSocket)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeWs => 'WebSocket only';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeAutoInfo => 'More robust on restrictive networks. Upgrades to WebSocket when possible.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeWsInfo => 'Lower overhead, but may fail behind strict proxies/firewalls.';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,4 +727,43 @@ class AppLocalizationsFr extends AppLocalizations {
|
|||||||
String thoughtForDuration(String duration) {
|
String thoughtForDuration(String duration) {
|
||||||
return 'A réfléchi pendant $duration';
|
return 'A réfléchi pendant $duration';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appCustomization => 'Personnalisation de l\'app';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appCustomizationSubtitle => 'Personnalisez l\'affichage des noms et de l\'UI';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get display => 'Affichage';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get realtime => 'Temps réel';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hideProviderInModelNames => 'Masquer le fournisseur dans les noms de modèles';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hideProviderInModelNamesDescription => 'Afficher des noms comme \"gpt-4o\" au lieu de \"openai/gpt-4o\".';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportMode => 'Mode de transport';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeDescription => 'Choisissez comment l\'app se connecte pour les mises à jour en temps réel.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get mode => 'Mode';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeAuto => 'Auto (Polling + WebSocket)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeWs => 'WebSocket uniquement';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeAutoInfo => 'Plus robuste sur les réseaux restrictifs. Passe à WebSocket lorsque possible.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeWsInfo => 'Moins de surcharge, mais peut échouer derrière des proxys/firewalls stricts.';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -727,4 +727,43 @@ class AppLocalizationsIt extends AppLocalizations {
|
|||||||
String thoughtForDuration(String duration) {
|
String thoughtForDuration(String duration) {
|
||||||
return 'Ha pensato per $duration';
|
return 'Ha pensato per $duration';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appCustomization => 'Personalizzazione app';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get appCustomizationSubtitle => 'Personalizza la visualizzazione dei nomi e dell\'UI';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get display => 'Schermo';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get realtime => 'Tempo reale';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hideProviderInModelNames => 'Nascondi provider nei nomi dei modelli';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get hideProviderInModelNamesDescription => 'Mostra nomi come \"gpt-4o\" invece di \"openai/gpt-4o\".';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportMode => 'Modalità di trasporto';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeDescription => 'Scegli come l\'app si connette per gli aggiornamenti in tempo reale.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get mode => 'Modalità';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeAuto => 'Auto (Polling + WebSocket)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeWs => 'Solo WebSocket';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeAutoInfo => 'Più robusto nelle reti restrittive. Passa a WebSocket quando possibile.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get transportModeWsInfo => 'Minore overhead, ma può fallire dietro proxy/firewall restrittivi.';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user