feat: image generation

This commit is contained in:
cogwheel0
2025-08-21 14:37:49 +05:30
parent 0c4f323814
commit e63c57d1fe
4 changed files with 466 additions and 148 deletions

View File

@@ -6,6 +6,7 @@ import 'dart:io' show Platform;
import '../../../core/models/tool.dart';
import '../../../shared/theme/theme_extensions.dart';
import '../../chat/providers/chat_providers.dart';
import '../../../core/providers/app_providers.dart';
import '../providers/tools_providers.dart';
class UnifiedToolsModal extends ConsumerStatefulWidget {
@@ -19,6 +20,8 @@ class _UnifiedToolsModalState extends ConsumerState<UnifiedToolsModal> {
@override
Widget build(BuildContext context) {
final webSearchEnabled = ref.watch(webSearchEnabledProvider);
final imageGenEnabled = ref.watch(imageGenerationEnabledProvider);
final imageGenAvailable = ref.watch(imageGenerationAvailableProvider);
final selectedToolIds = ref.watch(selectedToolIdsProvider);
final toolsAsync = ref.watch(toolsListProvider);
@@ -60,6 +63,12 @@ class _UnifiedToolsModalState extends ConsumerState<UnifiedToolsModal> {
_buildWebSearchToggle(webSearchEnabled),
const SizedBox(height: Spacing.md),
// Image Generation Toggle (conditionally shown)
if (imageGenAvailable) ...[
_buildImageGenerationToggle(imageGenEnabled),
const SizedBox(height: Spacing.md),
],
// Tools Section
toolsAsync.when(
data: (tools) {
@@ -95,10 +104,15 @@ class _UnifiedToolsModalState extends ConsumerState<UnifiedToolsModal> {
),
),
const SizedBox(height: Spacing.sm),
...tools.map((tool) => Padding(
padding: const EdgeInsets.only(bottom: Spacing.sm),
child: _buildToolCard(tool, selectedToolIds.contains(tool.id)),
)),
...tools.map(
(tool) => Padding(
padding: const EdgeInsets.only(bottom: Spacing.sm),
child: _buildToolCard(
tool,
selectedToolIds.contains(tool.id),
),
),
),
],
);
},
@@ -221,17 +235,95 @@ class _UnifiedToolsModalState extends ConsumerState<UnifiedToolsModal> {
);
}
Widget _buildImageGenerationToggle(bool imageGenEnabled) {
return GestureDetector(
onTap: () {
HapticFeedback.lightImpact();
ref.read(imageGenerationEnabledProvider.notifier).state =
!imageGenEnabled;
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(Spacing.md),
decoration: BoxDecoration(
color: imageGenEnabled
? context.conduitTheme.buttonPrimary
: context.conduitTheme.cardBackground,
borderRadius: BorderRadius.circular(AppBorderRadius.md),
border: Border.all(
color: imageGenEnabled
? context.conduitTheme.buttonPrimary
: context.conduitTheme.cardBorder,
width: BorderWidth.regular,
),
),
child: Row(
children: [
Icon(
Platform.isIOS ? CupertinoIcons.photo : Icons.image,
size: IconSize.medium,
color: imageGenEnabled
? context.conduitTheme.buttonPrimaryText
: context.conduitTheme.textPrimary.withValues(
alpha: Alpha.strong,
),
),
const SizedBox(width: Spacing.sm),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Image Generation',
style: AppTypography.labelStyle.copyWith(
color: imageGenEnabled
? context.conduitTheme.buttonPrimaryText
: context.conduitTheme.textPrimary,
fontWeight: FontWeight.w600,
),
),
Text(
imageGenEnabled
? 'I can generate images from your prompt'
: 'Enable to generate images with your request',
style: AppTypography.captionStyle.copyWith(
color: imageGenEnabled
? context.conduitTheme.buttonPrimaryText.withValues(
alpha: Alpha.strong,
)
: context.conduitTheme.textSecondary,
),
),
],
),
),
Icon(
imageGenEnabled ? Icons.toggle_on : Icons.toggle_off,
size: IconSize.large,
color: imageGenEnabled
? context.conduitTheme.buttonPrimaryText
: context.conduitTheme.textSecondary,
),
],
),
),
);
}
Widget _buildToolCard(Tool tool, bool isSelected) {
return GestureDetector(
onTap: () {
HapticFeedback.lightImpact();
final currentIds = ref.read(selectedToolIdsProvider);
if (isSelected) {
ref.read(selectedToolIdsProvider.notifier).state =
currentIds.where((id) => id != tool.id).toList();
ref.read(selectedToolIdsProvider.notifier).state = currentIds
.where((id) => id != tool.id)
.toList();
} else {
ref.read(selectedToolIdsProvider.notifier).state =
[...currentIds, tool.id];
ref.read(selectedToolIdsProvider.notifier).state = [
...currentIds,
tool.id,
];
}
},
child: Container(
@@ -274,7 +366,7 @@ class _UnifiedToolsModalState extends ConsumerState<UnifiedToolsModal> {
fontWeight: FontWeight.w600,
),
),
if (tool.meta?['description'] != null &&
if (tool.meta?['description'] != null &&
tool.meta!['description'].toString().isNotEmpty)
Text(
tool.meta!['description'].toString(),
@@ -306,11 +398,13 @@ class _UnifiedToolsModalState extends ConsumerState<UnifiedToolsModal> {
IconData _getToolIcon(Tool tool) {
final toolName = tool.name.toLowerCase();
if (toolName.contains('image') || toolName.contains('vision')) {
return Platform.isIOS ? CupertinoIcons.photo : Icons.image;
} else if (toolName.contains('code') || toolName.contains('python')) {
return Platform.isIOS ? CupertinoIcons.chevron_left_slash_chevron_right : Icons.code;
return Platform.isIOS
? CupertinoIcons.chevron_left_slash_chevron_right
: Icons.code;
} else if (toolName.contains('calculator') || toolName.contains('math')) {
return Icons.calculate;
} else if (toolName.contains('file') || toolName.contains('document')) {