feat: toggle full model name display in header
This commit is contained in:
@@ -35,6 +35,7 @@ import '../../onboarding/views/onboarding_sheet.dart';
|
||||
import '../../../shared/widgets/sheet_handle.dart';
|
||||
import '../../../shared/widgets/measure_size.dart';
|
||||
import '../../../shared/widgets/conduit_components.dart';
|
||||
import '../../../shared/widgets/middle_ellipsis_text.dart';
|
||||
import '../../../core/services/settings_service.dart';
|
||||
// Removed unused PlatformUtils import
|
||||
import '../../../core/services/platform_service.dart' as ps;
|
||||
@@ -56,16 +57,21 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
||||
bool _isDeactivated = false;
|
||||
double _inputHeight = 0; // dynamic input height to position scroll button
|
||||
|
||||
String _formatModelDisplayName(String name) {
|
||||
String _formatModelDisplayName(
|
||||
String name, {
|
||||
required bool omitProvider,
|
||||
}) {
|
||||
var display = name.trim();
|
||||
// Prefer the segment after the last '/'
|
||||
if (display.contains('/')) {
|
||||
display = display.split('/').last.trim();
|
||||
}
|
||||
// If an org prefix like 'OpenAI: gpt-4o' exists, use the part after ':'
|
||||
if (display.contains(':')) {
|
||||
final parts = display.split(':');
|
||||
display = parts.last.trim();
|
||||
if (omitProvider) {
|
||||
// Prefer the segment after the last '/'
|
||||
if (display.contains('/')) {
|
||||
display = display.split('/').last.trim();
|
||||
}
|
||||
// If an org prefix like 'OpenAI: gpt-4o' exists, use the part after ':'
|
||||
if (display.contains(':')) {
|
||||
final parts = display.split(':');
|
||||
display = parts.last.trim();
|
||||
}
|
||||
}
|
||||
return display;
|
||||
}
|
||||
@@ -698,6 +704,8 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
||||
String? displayModelName;
|
||||
final rawModel = message.model;
|
||||
if (rawModel != null && rawModel.isNotEmpty) {
|
||||
final omitProvider =
|
||||
ref.watch(appSettingsProvider).omitProviderInModelName;
|
||||
final modelsAsync = ref.watch(modelsProvider);
|
||||
if (modelsAsync.hasValue) {
|
||||
final models = modelsAsync.value!;
|
||||
@@ -706,14 +714,23 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
||||
final match = models.firstWhere(
|
||||
(m) => m.id == rawModel || m.name == rawModel,
|
||||
);
|
||||
displayModelName = match.name;
|
||||
displayModelName = _formatModelDisplayName(
|
||||
match.name,
|
||||
omitProvider: omitProvider,
|
||||
);
|
||||
} catch (_) {
|
||||
// As a fallback, format the raw value to be more readable
|
||||
displayModelName = _formatModelDisplayName(rawModel);
|
||||
displayModelName = _formatModelDisplayName(
|
||||
rawModel,
|
||||
omitProvider: omitProvider,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Models not loaded yet; format raw value for readability
|
||||
displayModelName = _formatModelDisplayName(rawModel);
|
||||
displayModelName = _formatModelDisplayName(
|
||||
rawModel,
|
||||
omitProvider: omitProvider,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1133,17 +1150,27 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
||||
),
|
||||
const SizedBox(width: Spacing.xs),
|
||||
Flexible(
|
||||
child: Text(
|
||||
_formatModelDisplayName(selectedModel.name),
|
||||
style: AppTypography.headlineSmallStyle
|
||||
.copyWith(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final omitProvider = ref
|
||||
.watch(appSettingsProvider)
|
||||
.omitProviderInModelName;
|
||||
final label = _formatModelDisplayName(
|
||||
selectedModel.name,
|
||||
omitProvider: omitProvider,
|
||||
);
|
||||
return MiddleEllipsisText(
|
||||
label,
|
||||
style: AppTypography.headlineSmallStyle
|
||||
.copyWith(
|
||||
color:
|
||||
context.conduitTheme.textPrimary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
textAlign: TextAlign.center,
|
||||
semanticsLabel: label,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: Spacing.xs),
|
||||
|
||||
110
lib/features/profile/views/app_customization_page.dart
Normal file
110
lib/features/profile/views/app_customization_page.dart
Normal file
@@ -0,0 +1,110 @@
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../core/services/settings_service.dart';
|
||||
import '../../../shared/theme/theme_extensions.dart';
|
||||
import '../../../shared/widgets/conduit_components.dart';
|
||||
import '../../../shared/utils/ui_utils.dart';
|
||||
|
||||
class AppCustomizationPage extends ConsumerWidget {
|
||||
const AppCustomizationPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final settings = ref.watch(appSettingsProvider);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: context.conduitTheme.surfaceBackground,
|
||||
appBar: AppBar(
|
||||
backgroundColor: context.conduitTheme.surfaceBackground,
|
||||
elevation: Elevation.none,
|
||||
leading: IconButton(
|
||||
icon: Icon(
|
||||
UiUtils.platformIcon(
|
||||
ios: CupertinoIcons.back,
|
||||
android: Icons.arrow_back,
|
||||
),
|
||||
color: context.conduitTheme.textPrimary,
|
||||
),
|
||||
onPressed: () => Navigator.of(context).maybePop(),
|
||||
tooltip: 'Back',
|
||||
),
|
||||
title: Text(
|
||||
'App Customization',
|
||||
style: AppTypography.headlineSmallStyle.copyWith(
|
||||
color: context.conduitTheme.textPrimary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(Spacing.pagePadding),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Display',
|
||||
style: context.conduitTheme.headingSmall?.copyWith(
|
||||
color: context.conduitTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: Spacing.md),
|
||||
ConduitCard(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Column(
|
||||
children: [
|
||||
SwitchListTile.adaptive(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.listItemPadding,
|
||||
vertical: Spacing.sm,
|
||||
),
|
||||
// Use platform defaults for switch colors to match theme
|
||||
value: settings.omitProviderInModelName,
|
||||
title: Text(
|
||||
'Hide provider in model names',
|
||||
style: context.conduitTheme.bodyLarge?.copyWith(
|
||||
color: context.conduitTheme.textPrimary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
'Show names like "gpt-4o" instead of "openai/gpt-4o".',
|
||||
style: context.conduitTheme.bodySmall?.copyWith(
|
||||
color: context.conduitTheme.textSecondary,
|
||||
),
|
||||
),
|
||||
onChanged: (v) {
|
||||
ref
|
||||
.read(appSettingsProvider.notifier)
|
||||
.setOmitProviderInModelName(v);
|
||||
},
|
||||
secondary: Container(
|
||||
padding: const EdgeInsets.all(Spacing.sm),
|
||||
decoration: BoxDecoration(
|
||||
color: context.conduitTheme.buttonPrimary
|
||||
.withValues(alpha: Alpha.highlight),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppBorderRadius.small),
|
||||
),
|
||||
child: Icon(
|
||||
Platform.isIOS
|
||||
? CupertinoIcons.textformat
|
||||
: Icons.text_fields,
|
||||
color: context.conduitTheme.buttonPrimary,
|
||||
size: IconSize.medium,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import '../../../core/models/model.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import '../../chat/views/chat_page_helpers.dart';
|
||||
import 'app_customization_page.dart';
|
||||
|
||||
/// Profile page (You tab) showing user info and main actions
|
||||
/// Enhanced with production-grade design tokens for better cohesion
|
||||
@@ -232,6 +233,22 @@ class ProfilePage extends ConsumerWidget {
|
||||
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
||||
_buildLanguageTile(context, ref),
|
||||
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
||||
_buildAccountOption(
|
||||
icon: UiUtils.platformIcon(
|
||||
ios: CupertinoIcons.slider_horizontal_3,
|
||||
android: Icons.tune,
|
||||
),
|
||||
title: 'App Customization',
|
||||
subtitle: 'Personalize how names and UI display',
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const AppCustomizationPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
||||
_buildAboutTile(context),
|
||||
Divider(color: context.conduitTheme.dividerColor, height: 1),
|
||||
_buildAccountOption(
|
||||
|
||||
Reference in New Issue
Block a user