refactor: improve styling and layout in authentication and connection issue pages
- Removed unused flutter_animate dependency to streamline the codebase. - Adjusted container dimensions and padding in the connection issue page for better layout consistency. - Updated spacing values and text styles to enhance readability and align with the overall theme. - Refined border radius and background colors for improved aesthetics in various components. - Enhanced the user interface by ensuring consistent styling across authentication and connection issue pages.
This commit is contained in:
@@ -2,7 +2,6 @@ import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ class _ConnectionIssuePageState extends ConsumerState<ConnectionIssuePage> {
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 420),
|
||||
constraints: const BoxConstraints(maxWidth: 480),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
@@ -71,13 +71,13 @@ class _ConnectionIssuePageState extends ConsumerState<ConnectionIssuePage> {
|
||||
const SizedBox(height: Spacing.sm),
|
||||
_buildServerDetails(context, activeServer),
|
||||
],
|
||||
const SizedBox(height: Spacing.md),
|
||||
const SizedBox(height: Spacing.lg),
|
||||
Text(
|
||||
l10n.connectionIssueSubtitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: context.conduitTheme.bodyMedium?.copyWith(
|
||||
color: context.conduitTheme.textSecondary,
|
||||
height: 1.45,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -111,27 +111,30 @@ class _ConnectionIssuePageState extends ConsumerState<ConnectionIssuePage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 72,
|
||||
height: 72,
|
||||
width: 64,
|
||||
height: 64,
|
||||
decoration: BoxDecoration(
|
||||
color: context.conduitTheme.surfaceContainerHighest,
|
||||
color: context.conduitTheme.error.withValues(alpha: 0.1),
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: ConduitShadows.high(context),
|
||||
border: Border.all(
|
||||
color: context.conduitTheme.error.withValues(alpha: 0.2),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
child: Icon(
|
||||
Platform.isIOS
|
||||
? CupertinoIcons.wifi_exclamationmark
|
||||
: Icons.wifi_off_rounded,
|
||||
color: iconColor,
|
||||
size: 34,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: Spacing.md),
|
||||
const SizedBox(height: Spacing.lg),
|
||||
Text(
|
||||
l10n.connectionIssueTitle,
|
||||
textAlign: TextAlign.center,
|
||||
style: context.conduitTheme.headingMedium?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: context.conduitTheme.textPrimary,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -470,9 +469,8 @@ class _ServerConnectionPageState extends ConsumerState<ServerConnectionPage> {
|
||||
return Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => setState(
|
||||
() => _showAdvancedSettings = !_showAdvancedSettings,
|
||||
),
|
||||
onTap: () =>
|
||||
setState(() => _showAdvancedSettings = !_showAdvancedSettings),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.button),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -597,7 +595,9 @@ class _ServerConnectionPageState extends ConsumerState<ServerConnectionPage> {
|
||||
const SizedBox(width: Spacing.sm),
|
||||
ConduitIconButton(
|
||||
icon: Platform.isIOS ? CupertinoIcons.plus : Icons.add,
|
||||
onPressed: _customHeaders.length >= 10 ? null : _addCustomHeader,
|
||||
onPressed: _customHeaders.length >= 10
|
||||
? null
|
||||
: _addCustomHeader,
|
||||
tooltip: _customHeaders.length >= 10
|
||||
? AppLocalizations.of(context)!.maximumHeadersReached
|
||||
: AppLocalizations.of(context)!.addHeader,
|
||||
|
||||
@@ -320,7 +320,7 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
|
||||
}
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -328,10 +328,10 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
|
||||
vertical: Spacing.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.5),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor,
|
||||
color: theme.dividerColor.withValues(alpha: 0.5),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
@@ -379,10 +379,10 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
|
||||
margin: const EdgeInsets.only(top: Spacing.sm),
|
||||
padding: const EdgeInsets.all(Spacing.sm),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor,
|
||||
color: theme.dividerColor.withValues(alpha: 0.5),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
@@ -1168,7 +1168,7 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
|
||||
}
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -1176,10 +1176,10 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
|
||||
vertical: Spacing.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.5),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor,
|
||||
color: theme.dividerColor.withValues(alpha: 0.5),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
@@ -1223,10 +1223,10 @@ class _AssistantMessageWidgetState extends ConsumerState<AssistantMessageWidget>
|
||||
margin: const EdgeInsets.only(top: Spacing.sm),
|
||||
padding: const EdgeInsets.all(Spacing.sm),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor,
|
||||
color: theme.dividerColor.withValues(alpha: 0.5),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
@@ -1418,7 +1418,7 @@ class _TimelineRow extends StatelessWidget {
|
||||
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.sm),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
child: wrapped,
|
||||
);
|
||||
}
|
||||
@@ -1643,15 +1643,19 @@ class _QueryPills extends StatelessWidget {
|
||||
onTap: () => _launchUri(
|
||||
'https://www.google.com/search?q=${Uri.encodeComponent(query)}',
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.sm),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.sm,
|
||||
vertical: 6,
|
||||
vertical: Spacing.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.4),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.sm),
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.25),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor.withValues(alpha: 0.3),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -1703,15 +1707,19 @@ class _LinkPills extends StatelessWidget {
|
||||
.map(
|
||||
(item) => InkWell(
|
||||
onTap: item.url != null ? () => _launchUri(item.url!) : null,
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.sm),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.sm,
|
||||
vertical: 6,
|
||||
vertical: Spacing.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.4),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.sm),
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.25),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor.withValues(alpha: 0.3),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -2168,15 +2176,15 @@ class FollowUpSuggestionBar extends StatelessWidget {
|
||||
children: [
|
||||
Icon(
|
||||
Icons.lightbulb_outline,
|
||||
size: 14,
|
||||
color: theme.textSecondary.withValues(alpha: 0.8),
|
||||
size: 12,
|
||||
color: theme.textSecondary.withValues(alpha: 0.7),
|
||||
),
|
||||
const SizedBox(width: Spacing.xxs),
|
||||
Text(
|
||||
'Continue with',
|
||||
style: TextStyle(
|
||||
fontSize: AppTypography.labelSmall,
|
||||
color: theme.textSecondary.withValues(alpha: 0.8),
|
||||
color: theme.textSecondary.withValues(alpha: 0.7),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
@@ -2184,7 +2192,7 @@ class FollowUpSuggestionBar extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: Spacing.xs),
|
||||
Wrap(
|
||||
spacing: Spacing.sm,
|
||||
spacing: Spacing.xs,
|
||||
runSpacing: Spacing.xs,
|
||||
children: [
|
||||
for (final suggestion in trimmedSuggestions)
|
||||
@@ -2217,7 +2225,7 @@ class _MinimalFollowUpButton extends StatelessWidget {
|
||||
|
||||
return InkWell(
|
||||
onTap: enabled ? onPressed : null,
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.sm),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.sm,
|
||||
@@ -2225,14 +2233,14 @@ class _MinimalFollowUpButton extends StatelessWidget {
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: enabled
|
||||
? theme.surfaceContainer.withValues(alpha: 0.3)
|
||||
? theme.surfaceContainer.withValues(alpha: 0.2)
|
||||
: theme.surfaceContainer.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.sm),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
border: Border.all(
|
||||
color: enabled
|
||||
? theme.buttonPrimary.withValues(alpha: 0.2)
|
||||
: theme.dividerColor.withValues(alpha: 0.3),
|
||||
width: 1,
|
||||
? theme.buttonPrimary.withValues(alpha: 0.15)
|
||||
: theme.dividerColor.withValues(alpha: 0.2),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
@@ -2240,10 +2248,10 @@ class _MinimalFollowUpButton extends StatelessWidget {
|
||||
children: [
|
||||
Icon(
|
||||
Icons.arrow_forward,
|
||||
size: 12,
|
||||
size: 11,
|
||||
color: enabled
|
||||
? theme.buttonPrimary.withValues(alpha: 0.8)
|
||||
: theme.textSecondary.withValues(alpha: 0.5),
|
||||
? theme.buttonPrimary.withValues(alpha: 0.7)
|
||||
: theme.textSecondary.withValues(alpha: 0.4),
|
||||
),
|
||||
const SizedBox(width: Spacing.xxs),
|
||||
Flexible(
|
||||
@@ -2251,7 +2259,7 @@ class _MinimalFollowUpButton extends StatelessWidget {
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: enabled
|
||||
? theme.buttonPrimary
|
||||
? theme.buttonPrimary.withValues(alpha: 0.9)
|
||||
: theme.textSecondary.withValues(alpha: 0.5),
|
||||
fontSize: AppTypography.bodySmall,
|
||||
fontWeight: FontWeight.w500,
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||
import '../../../shared/theme/theme_extensions.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:conduit/l10n/app_localizations.dart';
|
||||
import '../services/file_attachment_service.dart';
|
||||
@@ -75,10 +74,7 @@ class _FileAttachmentCard extends ConsumerWidget {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
fileState.fileIcon,
|
||||
style: const TextStyle(fontSize: 20),
|
||||
),
|
||||
Text(fileState.fileIcon, style: const TextStyle(fontSize: 20)),
|
||||
const Spacer(),
|
||||
_buildStatusIcon(context),
|
||||
],
|
||||
@@ -225,10 +221,7 @@ class MessageAttachmentPreview extends StatelessWidget {
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'📎',
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
const Text('📎', style: TextStyle(fontSize: 14)),
|
||||
const SizedBox(width: Spacing.xs),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.attachmentLabel,
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'enhanced_attachment.dart';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:conduit/l10n/app_localizations.dart';
|
||||
import 'package:conduit/shared/widgets/chat_action_button.dart';
|
||||
@@ -149,13 +148,6 @@ class _UserMessageBubbleState extends ConsumerState<UserMessageBubble>
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppBorderRadius.messageBubble,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.conduitTheme.cardShadow.withValues(alpha: 0.1),
|
||||
blurRadius: 6,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
@@ -196,15 +188,6 @@ class _UserMessageBubbleState extends ConsumerState<UserMessageBubble>
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppBorderRadius.messageBubble,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.conduitTheme.cardShadow.withValues(
|
||||
alpha: 0.08,
|
||||
),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
@@ -248,15 +231,6 @@ class _UserMessageBubbleState extends ConsumerState<UserMessageBubble>
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.conduitTheme.cardShadow.withValues(
|
||||
alpha: 0.06,
|
||||
),
|
||||
blurRadius: 3,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
@@ -294,13 +268,6 @@ class _UserMessageBubbleState extends ConsumerState<UserMessageBubble>
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppBorderRadius.messageBubble,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.conduitTheme.cardShadow.withValues(alpha: 0.1),
|
||||
blurRadius: 6,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
@@ -341,15 +308,6 @@ class _UserMessageBubbleState extends ConsumerState<UserMessageBubble>
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppBorderRadius.messageBubble,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.conduitTheme.cardShadow.withValues(
|
||||
alpha: 0.08,
|
||||
),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
@@ -390,15 +348,6 @@ class _UserMessageBubbleState extends ConsumerState<UserMessageBubble>
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.conduitTheme.cardShadow.withValues(
|
||||
alpha: 0.06,
|
||||
),
|
||||
blurRadius: 3,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
@@ -509,173 +458,148 @@ class _UserMessageBubbleState extends ConsumerState<UserMessageBubble>
|
||||
);
|
||||
|
||||
return GestureDetector(
|
||||
onLongPress: () => _toggleActions(),
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.only(
|
||||
bottom: Spacing.md,
|
||||
left: Spacing.xxxl,
|
||||
right: Spacing.xs,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
// Display images outside and above the text bubble (iMessage style)
|
||||
// Prioritize files array over attachmentIds to avoid duplication
|
||||
if (hasFilesFromArray) ...[
|
||||
_buildUserFileImages(),
|
||||
] else if (hasImages) ...[
|
||||
_buildUserAttachmentImages(),
|
||||
],
|
||||
onLongPress: () => _toggleActions(),
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.only(
|
||||
bottom: Spacing.md,
|
||||
left: Spacing.xxxl,
|
||||
right: Spacing.xs,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
// Display images outside and above the text bubble (iMessage style)
|
||||
// Prioritize files array over attachmentIds to avoid duplication
|
||||
if (hasFilesFromArray) ...[
|
||||
_buildUserFileImages(),
|
||||
] else if (hasImages) ...[
|
||||
_buildUserAttachmentImages(),
|
||||
],
|
||||
|
||||
// Display text bubble if there's text content
|
||||
if (hasText) const SizedBox(height: Spacing.xs),
|
||||
if (hasText)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Flexible(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.82,
|
||||
// Display text bubble if there's text content
|
||||
if (hasText) const SizedBox(height: Spacing.xs),
|
||||
if (hasText)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Flexible(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.82,
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.chatBubblePadding,
|
||||
vertical: Spacing.sm,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: context.conduitTheme.chatBubbleUser,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppBorderRadius.messageBubble,
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.chatBubblePadding,
|
||||
vertical: Spacing.sm,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
context.conduitTheme.chatBubbleUser
|
||||
.withValues(alpha: 0.95),
|
||||
context.conduitTheme.chatBubbleUser,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppBorderRadius.messageBubble,
|
||||
),
|
||||
border: Border.all(
|
||||
color:
|
||||
context.conduitTheme.chatBubbleUserBorder,
|
||||
width: BorderWidth.regular,
|
||||
),
|
||||
boxShadow: ConduitShadows.small(context),
|
||||
),
|
||||
child: _isEditing
|
||||
? Focus(
|
||||
focusNode: _editFocusNode,
|
||||
autofocus: true,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: inlineEditFill,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppBorderRadius.sm,
|
||||
),
|
||||
border: Border.all(
|
||||
color: context
|
||||
.conduitTheme
|
||||
.inputBorderFocused
|
||||
.withValues(alpha: 0.6),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.xs,
|
||||
vertical: Spacing.xxs,
|
||||
),
|
||||
child: Platform.isIOS
|
||||
? CupertinoTextField(
|
||||
controller: _editController,
|
||||
maxLines: null,
|
||||
padding: EdgeInsets.zero,
|
||||
autofillHints: const <String>[],
|
||||
style: AppTypography
|
||||
.chatMessageStyle
|
||||
.copyWith(
|
||||
color:
|
||||
inlineEditTextColor,
|
||||
),
|
||||
decoration:
|
||||
const BoxDecoration(),
|
||||
cursorColor: context
|
||||
.conduitTheme
|
||||
.buttonPrimary,
|
||||
onSubmitted: (_) =>
|
||||
_saveInlineEdit(),
|
||||
)
|
||||
: TextField(
|
||||
controller: _editController,
|
||||
maxLines: null,
|
||||
autofillHints: const <String>[],
|
||||
style: AppTypography
|
||||
.chatMessageStyle
|
||||
.copyWith(
|
||||
color:
|
||||
inlineEditTextColor,
|
||||
),
|
||||
decoration:
|
||||
const InputDecoration(
|
||||
isCollapsed: true,
|
||||
border: InputBorder.none,
|
||||
contentPadding:
|
||||
EdgeInsets.zero,
|
||||
),
|
||||
cursorColor: context
|
||||
.conduitTheme
|
||||
.buttonPrimary,
|
||||
onSubmitted: (_) =>
|
||||
_saveInlineEdit(),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
widget.message.content,
|
||||
style: AppTypography.chatMessageStyle
|
||||
.copyWith(
|
||||
color: context
|
||||
.conduitTheme
|
||||
.chatBubbleUserText,
|
||||
),
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.left,
|
||||
textHeightBehavior:
|
||||
const TextHeightBehavior(
|
||||
applyHeightToFirstAscent: false,
|
||||
applyHeightToLastDescent: false,
|
||||
leadingDistribution:
|
||||
TextLeadingDistribution.even,
|
||||
),
|
||||
),
|
||||
border: Border.all(
|
||||
color: context.conduitTheme.chatBubbleUserBorder
|
||||
.withValues(alpha: 0.5),
|
||||
width: BorderWidth.standard,
|
||||
),
|
||||
),
|
||||
child: _isEditing
|
||||
? Focus(
|
||||
focusNode: _editFocusNode,
|
||||
autofocus: true,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: inlineEditFill,
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppBorderRadius.small,
|
||||
),
|
||||
border: Border.all(
|
||||
color: context
|
||||
.conduitTheme
|
||||
.inputBorderFocused
|
||||
.withValues(alpha: 0.5),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.xs,
|
||||
vertical: Spacing.xxs,
|
||||
),
|
||||
child: Platform.isIOS
|
||||
? CupertinoTextField(
|
||||
controller: _editController,
|
||||
maxLines: null,
|
||||
padding: EdgeInsets.zero,
|
||||
autofillHints: const <String>[],
|
||||
style: AppTypography
|
||||
.chatMessageStyle
|
||||
.copyWith(
|
||||
color: inlineEditTextColor,
|
||||
),
|
||||
decoration: const BoxDecoration(),
|
||||
cursorColor: context
|
||||
.conduitTheme
|
||||
.buttonPrimary,
|
||||
onSubmitted: (_) =>
|
||||
_saveInlineEdit(),
|
||||
)
|
||||
: TextField(
|
||||
controller: _editController,
|
||||
maxLines: null,
|
||||
autofillHints: const <String>[],
|
||||
style: AppTypography
|
||||
.chatMessageStyle
|
||||
.copyWith(
|
||||
color: inlineEditTextColor,
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
isCollapsed: true,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
cursorColor: context
|
||||
.conduitTheme
|
||||
.buttonPrimary,
|
||||
onSubmitted: (_) =>
|
||||
_saveInlineEdit(),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
widget.message.content,
|
||||
style: AppTypography.chatMessageStyle.copyWith(
|
||||
color:
|
||||
context.conduitTheme.chatBubbleUserText,
|
||||
),
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.left,
|
||||
textHeightBehavior: const TextHeightBehavior(
|
||||
applyHeightToFirstAscent: false,
|
||||
applyHeightToLastDescent: false,
|
||||
leadingDistribution:
|
||||
TextLeadingDistribution.even,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (hasText) const SizedBox(height: Spacing.xs),
|
||||
|
||||
// Action buttons below the message
|
||||
if (_showActions) ...[
|
||||
const SizedBox(height: Spacing.sm),
|
||||
_buildUserActionButtons(),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.animate()
|
||||
.fadeIn(duration: AnimationDuration.messageAppear)
|
||||
.slideX(
|
||||
begin: AnimationValues.messageSlideDistance,
|
||||
end: 0,
|
||||
duration: AnimationDuration.messageSlide,
|
||||
curve: AnimationCurves.messageSlide,
|
||||
);
|
||||
),
|
||||
if (hasText) const SizedBox(height: Spacing.xs),
|
||||
|
||||
// Action buttons below the message
|
||||
if (_showActions) ...[
|
||||
const SizedBox(height: Spacing.sm),
|
||||
_buildUserActionButtons(),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Assistant-only message renderer removed.
|
||||
|
||||
@@ -565,16 +565,19 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: AppTypography.labelStyle.copyWith(color: theme.textSecondary),
|
||||
style: AppTypography.labelStyle.copyWith(
|
||||
color: theme.textSecondary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: Spacing.xs),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.6),
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.4),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.xs),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor,
|
||||
color: theme.dividerColor.withValues(alpha: 0.5),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
@@ -1029,18 +1032,15 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
||||
color: isHover
|
||||
? theme.buttonPrimary.withValues(alpha: 0.08)
|
||||
: theme.surfaceContainer.withValues(alpha: 0.03),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
border: Border.all(
|
||||
color: isHover
|
||||
? theme.buttonPrimary.withValues(alpha: 0.6)
|
||||
: theme.dividerColor,
|
||||
width: BorderWidth.regular,
|
||||
? theme.buttonPrimary.withValues(alpha: 0.5)
|
||||
: theme.dividerColor.withValues(alpha: 0.5),
|
||||
width: BorderWidth.standard,
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.md,
|
||||
vertical: Spacing.sm,
|
||||
),
|
||||
padding: const EdgeInsets.all(Spacing.sm),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
@@ -1048,14 +1048,15 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
||||
? CupertinoIcons.folder_badge_minus
|
||||
: Icons.folder_off_outlined,
|
||||
color: theme.iconPrimary,
|
||||
size: IconSize.small,
|
||||
),
|
||||
const SizedBox(width: Spacing.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Drop here to remove from folder',
|
||||
style: AppTypography.bodyMediumStyle.copyWith(
|
||||
style: AppTypography.bodySmallStyle.copyWith(
|
||||
color: theme.textPrimary,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1351,55 +1352,46 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
||||
if (user != null) ...[
|
||||
const SizedBox(height: Spacing.sm),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: Spacing.sm,
|
||||
vertical: Spacing.xs,
|
||||
),
|
||||
padding: const EdgeInsets.all(Spacing.sm),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.05),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
color: theme.surfaceContainer.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
border: Border.all(
|
||||
color: theme.dividerColor,
|
||||
width: BorderWidth.regular,
|
||||
color: theme.dividerColor.withValues(alpha: 0.5),
|
||||
width: BorderWidth.standard,
|
||||
),
|
||||
boxShadow: ConduitShadows.card(context),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: IconSize.xl,
|
||||
height: IconSize.xl,
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppBorderRadius.avatar,
|
||||
),
|
||||
border: Border.all(
|
||||
color: theme.buttonPrimary.withValues(alpha: 0.35),
|
||||
color: theme.buttonPrimary.withValues(alpha: 0.25),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: UserAvatar(
|
||||
size: IconSize.xl,
|
||||
size: 36,
|
||||
imageUrl: avatarUrl,
|
||||
fallbackText: initial,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: Spacing.xs),
|
||||
const SizedBox(width: Spacing.sm),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
displayName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: AppTypography.bodySmallStyle.copyWith(
|
||||
color: theme.textPrimary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: Text(
|
||||
displayName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: AppTypography.bodySmallStyle.copyWith(
|
||||
color: theme.textPrimary,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
@@ -1408,12 +1400,13 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
||||
Navigator.of(context).maybePop();
|
||||
context.pushNamed(RouteNames.profile);
|
||||
},
|
||||
visualDensity: VisualDensity.compact,
|
||||
icon: Icon(
|
||||
Platform.isIOS
|
||||
? CupertinoIcons.settings
|
||||
: Icons.settings_rounded,
|
||||
color: theme.iconSecondary,
|
||||
size: IconSize.listItem,
|
||||
size: IconSize.small,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1621,19 +1614,14 @@ class _ConversationTile extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = context.conduitTheme;
|
||||
final brightness = Theme.of(context).brightness;
|
||||
final borderRadius = BorderRadius.zero;
|
||||
final Color background = selected
|
||||
? theme.buttonPrimary.withValues(
|
||||
alpha: brightness == Brightness.dark ? 0.28 : 0.16,
|
||||
)
|
||||
? theme.buttonPrimary.withValues(alpha: 0.1)
|
||||
: theme.surfaceContainer;
|
||||
final Color borderColor = selected
|
||||
? theme.buttonPrimary.withValues(alpha: 0.7)
|
||||
? theme.buttonPrimary.withValues(alpha: 0.5)
|
||||
: theme.surfaceContainerHighest.withValues(alpha: 0.40);
|
||||
final List<BoxShadow> shadow = selected
|
||||
? ConduitShadows.low(context)
|
||||
: const [];
|
||||
final List<BoxShadow> shadow = const [];
|
||||
|
||||
Color? overlayForStates(Set<WidgetState> states) {
|
||||
if (states.contains(WidgetState.pressed)) {
|
||||
|
||||
@@ -598,7 +598,7 @@ class _PaletteOption extends StatelessWidget {
|
||||
|
||||
return InkWell(
|
||||
onTap: onSelect,
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
||||
borderRadius: BorderRadius.circular(AppBorderRadius.small),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: Spacing.sm),
|
||||
child: Row(
|
||||
@@ -607,7 +607,7 @@ class _PaletteOption extends StatelessWidget {
|
||||
Icon(
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_off,
|
||||
color: isSelected ? theme.buttonPrimary : theme.iconSecondary,
|
||||
size: IconSize.md,
|
||||
size: IconSize.small,
|
||||
),
|
||||
const SizedBox(width: Spacing.sm),
|
||||
Expanded(
|
||||
@@ -635,7 +635,7 @@ class _PaletteOption extends StatelessWidget {
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
color: theme.buttonPrimary,
|
||||
size: IconSize.sm,
|
||||
size: IconSize.small,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -674,14 +674,14 @@ class _PaletteColorDot extends StatelessWidget {
|
||||
final theme = context.conduitTheme;
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(right: Spacing.xs),
|
||||
width: 20,
|
||||
height: 20,
|
||||
width: 18,
|
||||
height: 18,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: theme.dividerColor.withValues(alpha: 0.4),
|
||||
width: 1.2,
|
||||
color: theme.dividerColor.withValues(alpha: 0.3),
|
||||
width: BorderWidth.thin,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'package:flutter/services.dart';
|
||||
import '../../../shared/theme/theme_extensions.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
@@ -306,11 +305,7 @@ class ProfilePage extends ConsumerWidget {
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
UserAvatar(
|
||||
size: 56,
|
||||
imageUrl: avatarUrl,
|
||||
fallbackText: initial,
|
||||
),
|
||||
UserAvatar(size: 56, imageUrl: avatarUrl, fallbackText: initial),
|
||||
const SizedBox(width: Spacing.md),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -1187,7 +1182,8 @@ class _DefaultModelBottomSheetState
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
] else if (model.isMultimodal || _modelSupportsReasoning(model)) ...[
|
||||
] else if (model.isMultimodal ||
|
||||
_modelSupportsReasoning(model)) ...[
|
||||
const SizedBox(height: Spacing.xs),
|
||||
Row(
|
||||
children: [
|
||||
|
||||
Reference in New Issue
Block a user