commit 918ec834ea27304772a8eb032dd0efa647291115
parent ad616574131050e2b2cb297f2d63c8307ed293d8
Author: ajcoder13 <avnijhalani@gmail.com>
Date: Wed, 3 Dec 2025 01:41:23 +0530
Caption from AI coming only when Magic Draw actiavted, prompt not necessary for model to work
Diffstat:
2 files changed, 103 insertions(+), 184 deletions(-)
diff --git a/lib/ui/pages/canvas_board_page.dart b/lib/ui/pages/canvas_board_page.dart
@@ -1165,7 +1165,7 @@ class _CanvasBoardPageState extends State<CanvasBoardPage> {
// ... (Rest of your UI: AI Description, MagicDrawTools, etc.) ...
// I have truncated the bottom part as it remains unchanged.
- if (_aiDescription != null && !_isMagicDrawActive)
+ if (_aiDescription != null && _isMagicDrawActive)
Positioned(
top: 10,
left: 16,
@@ -1254,6 +1254,9 @@ class _CanvasBoardPageState extends State<CanvasBoardPage> {
isMagicPanelDisabled: _isMagicPanelDisabled,
onViewModeToggle:
(enabled) => setState(() => _isViewMode = enabled),
+ hasImageLayers: elements.any(
+ (e) => e['type'] == 'file_image',
+ ),
),
TextToolsOverlay(
@@ -1440,18 +1443,12 @@ class _CanvasBoardPageState extends State<CanvasBoardPage> {
});
}
- Future<void> _processInpainting(String prompt, String serviceId) async {
- if (prompt.isEmpty) {
- ScaffoldMessenger.of(
- context,
- ).showSnackBar(const SnackBar(content: Text("Please enter a prompt!")));
- return;
- }
-
+ Future<void> _processInpainting(String prompt, String modelId) async {
setState(() => _isInpainting = true);
_resetInactivityTimer();
try {
+ // Check if there are image layers (which dictates the context)
bool hasImageLayers = elements.any((e) => e['type'] == 'file_image');
if (_tempBaseImage == null) {
@@ -1464,34 +1461,42 @@ class _CanvasBoardPageState extends State<CanvasBoardPage> {
_canvasSize,
_tempBaseImage,
);
+
if (maskFile == null) throw Exception("Failed to generate mask");
String? newImageUrl;
- // --- SWITCH SERVICE ---
- if (serviceId == 'api') {
- // Call the dedicated API endpoint
- newImageUrl = await FlaskService().inpaintApiImage(
- imagePath: _tempBaseImage!.path,
- maskPath: maskFile.path,
- prompt: prompt,
- );
- } else {
- // Default 'flask' service logic
- if (hasImageLayers) {
- newImageUrl = await FlaskService().inpaintImage(
+ // --- LOGIC SWITCHING BASED ON MODEL ID ---
+
+ // CASE 1: INPAINTING (When hasImageLayers is TRUE)
+ if (hasImageLayers) {
+ if (modelId == 'inpaint_api') {
+ // Call API Inpainting
+ newImageUrl = await FlaskService().inpaintApiImage(
imagePath: _tempBaseImage!.path,
maskPath: maskFile.path,
prompt: prompt,
);
} else {
- newImageUrl = await FlaskService().sketchToImage(
- sketchPath: _tempBaseImage!.path,
- userPrompt: prompt,
- stylePrompt: "high quality, realistic",
+ // Default: Standard Flask Inpainting ('inpaint_standard')
+ newImageUrl = await FlaskService().inpaintImage(
+ imagePath: _tempBaseImage!.path,
+ maskPath: maskFile.path,
+ prompt: prompt,
);
}
}
+ // CASE 2: SKETCH TO IMAGE (When hasImageLayers is FALSE)
+ else {
+ // Currently all sketch IDs map to the main sketch endpoint,
+ // but you can pass the ID if your backend supports different sketch models.
+ newImageUrl = await FlaskService().sketchToImage(
+ sketchPath: _tempBaseImage!.path,
+ userPrompt: prompt,
+ stylePrompt:
+ "high quality, realistic", // You could vary this based on modelId
+ );
+ }
if (newImageUrl != null) {
_addGeneratedImage(newImageUrl);
@@ -1816,7 +1821,7 @@ class _ManipulatingBoxState extends State<_ManipulatingBox> {
late Offset _pos;
late Size _size;
late double _rot;
-
+
// Gesture state
double _initialRotation = 0.0;
double _initialScale = 1.0;
@@ -1853,8 +1858,6 @@ class _ManipulatingBoxState extends State<_ManipulatingBox> {
final double zoom = matrix.getMaxScaleOnAxis();
final double handleScale = (1 / zoom).clamp(0.2, 5.0);
final double edgeThickness = 18 * handleScale;
- final double buttonSize = 32 * handleScale;
- final double iconSize = 14 * handleScale;
return Positioned(
left: _pos.dx,
@@ -1889,21 +1892,25 @@ class _ManipulatingBoxState extends State<_ManipulatingBox> {
if (_isTwoFingerGesture && details.pointerCount == 2) {
// Two-finger: handle rotation and zoom
final newRotation = _initialRotation + details.rotation;
-
+
// Handle scale (zoom) - maintain aspect ratio
final scaleFactor = details.scale;
- final newArea = _initialScale * scaleFactor * scaleFactor;
+ final newArea =
+ _initialScale * scaleFactor * scaleFactor;
final aspectRatio = _size.width / _size.height;
- final newWidth = math.sqrt(newArea * aspectRatio).clamp(20.0, 5000.0);
+ final newWidth = math
+ .sqrt(newArea * aspectRatio)
+ .clamp(20.0, 5000.0);
final newHeight = newWidth / aspectRatio;
-
+
setState(() {
_rot = newRotation % (2 * math.pi);
_size = Size(newWidth, newHeight);
});
-
+
widget.onUpdate(_pos, _size, _rot);
- } else if (!_isTwoFingerGesture && details.pointerCount == 1) {
+ } else if (!_isTwoFingerGesture &&
+ details.pointerCount == 1) {
// Single-finger: handle drag using incremental focal point delta
final currentFocalPoint = details.focalPoint;
final delta = currentFocalPoint - _previousFocalPoint;
@@ -1914,7 +1921,8 @@ class _ManipulatingBoxState extends State<_ManipulatingBox> {
final rotated = _rotateVector(scaledDelta, -_rot);
setState(() {
_pos += rotated;
- _previousFocalPoint = currentFocalPoint; // Update for next frame
+ _previousFocalPoint =
+ currentFocalPoint; // Update for next frame
});
widget.onUpdate(_pos, _size, _rot);
}
@@ -2045,92 +2053,6 @@ class _ManipulatingBoxState extends State<_ManipulatingBox> {
),
),
),
-
- // ======================
- // CORNER RESIZE HANDLES
- // ======================
- if (widget.isSelected && !widget.isEditing) ...[
- // TOP-LEFT corner
- Positioned(
- top: -10 * handleScale,
- left: -10 * handleScale,
- child: _cornerHandle(
- size: 20 * handleScale,
- onDrag: (d) {
- final local = _rotateVector(d.delta, -_rot);
- setState(() {
- _pos += Offset(local.dx, local.dy);
- _size = Size(
- (_size.width - local.dx).clamp(20, 5000),
- (_size.height - local.dy).clamp(20, 5000),
- );
- });
- widget.onUpdate(_pos, _size, _rot);
- },
- ),
- ),
-
- // TOP-RIGHT corner
- Positioned(
- top: -10 * handleScale,
- right: -10 * handleScale,
- child: _cornerHandle(
- size: 20 * handleScale,
- onDrag: (d) {
- final local = _rotateVector(d.delta, -_rot);
- setState(() {
- _pos += Offset(0, local.dy);
- _size = Size(
- (_size.width + local.dx).clamp(20, 5000),
- (_size.height - local.dy).clamp(20, 5000),
- );
- });
- widget.onUpdate(_pos, _size, _rot);
- },
- ),
- ),
-
- // BOTTOM-LEFT corner
- Positioned(
- bottom: -10 * handleScale,
- left: -10 * handleScale,
- child: _cornerHandle(
- size: 20 * handleScale,
- onDrag: (d) {
- final local = _rotateVector(d.delta, -_rot);
- setState(() {
- _pos += Offset(local.dx, 0);
- _size = Size(
- (_size.width - local.dx).clamp(20, 5000),
- (_size.height + local.dy).clamp(20, 5000),
- );
- });
- widget.onUpdate(_pos, _size, _rot);
- },
- ),
- ),
-
- // BOTTOM-RIGHT corner
- Positioned(
- bottom: -10 * handleScale,
- right: -10 * handleScale,
- child: _cornerHandle(
- size: 20 * handleScale,
- onDrag: (d) {
- final local = _rotateVector(d.delta, -_rot);
- setState(() {
- _size = Size(
- (_size.width + local.dx).clamp(20, 5000),
- (_size.height + local.dy).clamp(20, 5000),
- );
- });
- widget.onUpdate(_pos, _size, _rot);
- },
- ),
- ),
- ],
-
-
],
),
),
@@ -2139,33 +2061,6 @@ class _ManipulatingBoxState extends State<_ManipulatingBox> {
);
}
- Widget _cornerHandle({
- required double size,
- required Function(DragUpdateDetails) onDrag,
- }) {
- return GestureDetector(
- behavior: HitTestBehavior.translucent,
- onPanStart: (_) => widget.onDragStart(),
- onPanUpdate: onDrag,
- onPanEnd: (_) => widget.onDragEnd(_pos, _size, _rot),
- child: Container(
- width: size, // large invisible touch area
- height: size,
- alignment: Alignment.center,
- color: Colors.transparent,
- child: Container(
- width: 6 * (1 / widget.viewScale), // <<< tiny visual square
- height: 6 * (1 / widget.viewScale), // <<< tiny visual square
- decoration: BoxDecoration(
- color: Colors.white,
- border: Border.all(color: Colors.white, width: 1),
- shape: BoxShape.rectangle,
- ),
- ),
- ),
- );
- }
-
Widget _buildCircleButton({
required double size,
required IconData icon,
diff --git a/lib/ui/pages/canvas_toolbar/magic_draw_overlay.dart b/lib/ui/pages/canvas_toolbar/magic_draw_overlay.dart
@@ -4,9 +4,9 @@ import 'package:flutter_svg/flutter_svg.dart';
// --- HELPER CLASS FOR DROPDOWN ---
class AIModelOption {
- final String id; // 'flask' or 'api'
- final String name; // Display Name
- final String? badge; // Optional badge
+ final String id;
+ final String name;
+ final String? badge;
AIModelOption({required this.id, required this.name, this.badge});
}
@@ -20,16 +20,16 @@ class MagicDrawTools extends StatefulWidget {
final Function(double) onWidthChanged;
final Function(bool) onEraserToggle;
final VoidCallback onClose;
-
- // CHANGED: Accepts Prompt AND Model ID
final Function(String prompt, String modelId) onPromptSubmit;
-
final bool isProcessing;
final List<Color> brandColors;
final Function(bool) onViewModeToggle;
final Function(bool) onMagicPanelActivityToggle;
final bool isMagicPanelDisabled;
+ // NEW: To decide which dropdown list to show
+ final bool hasImageLayers;
+
const MagicDrawTools({
super.key,
required this.isActive,
@@ -46,6 +46,7 @@ class MagicDrawTools extends StatefulWidget {
required this.onViewModeToggle,
required this.onMagicPanelActivityToggle,
required this.isMagicPanelDisabled,
+ required this.hasImageLayers, // <-- Added
});
@override
@@ -65,21 +66,55 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
];
bool _isViewMode = false;
-
- // --- DROPDOWN STATE ---
bool _showModelMenu = false;
late AIModelOption _selectedModel;
- // --- DEFINE YOUR SERVICES HERE ---
- final List<AIModelOption> _aiModels = [
- AIModelOption(id: 'flask', name: 'Inpainting', badge: null),
- AIModelOption(id: 'api', name: 'Inpainting API', badge: null),
+ // --- 1. LIST FOR INPAINTING (When Image Exists) ---
+ final List<AIModelOption> _inpaintingModels = [
+ AIModelOption(
+ id: 'inpaint_standard',
+ name: 'Standard Inpainting',
+ badge: null,
+ ),
+ AIModelOption(id: 'inpaint_api', name: 'Inpainting API', badge: 'Pro'),
+ ];
+
+ // --- 2. LIST FOR SKETCH TO IMAGE (When No Image Exists) ---
+ final List<AIModelOption> _sketchModels = [
+ AIModelOption(id: 'sketch_fusion', name: 'Slider Fusion', badge: null),
+ AIModelOption(
+ id: 'sketch_advanced',
+ name: 'Advanced Fusion',
+ badge: 'Fastest',
+ ),
+ AIModelOption(id: 'sketch_creative', name: 'Creative Flow', badge: 'New'),
];
@override
void initState() {
super.initState();
- _selectedModel = _aiModels.first; // Default to first option
+ _updateSelectedModelGroup();
+ }
+
+ @override
+ void didUpdateWidget(MagicDrawTools oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ // If the layer state changes (image added/removed), reset selection to correct group
+ if (oldWidget.hasImageLayers != widget.hasImageLayers) {
+ _updateSelectedModelGroup();
+ }
+ }
+
+ void _updateSelectedModelGroup() {
+ if (widget.hasImageLayers) {
+ _selectedModel = _inpaintingModels.first;
+ } else {
+ _selectedModel = _sketchModels.first;
+ }
+ }
+
+ List<AIModelOption> get _currentModelList {
+ return widget.hasImageLayers ? _inpaintingModels : _sketchModels;
}
@override
@@ -92,10 +127,7 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
}
void _handleSubmit() {
- if (_promptController.text.trim().isNotEmpty &&
- !widget.isProcessing &&
- !widget.isMagicPanelDisabled) {
- // PASS SELECTED MODEL ID
+ if (!widget.isProcessing && !widget.isMagicPanelDisabled) {
widget.onPromptSubmit(_promptController.text.trim(), _selectedModel.id);
}
}
@@ -105,16 +137,14 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
setState(() {
_isViewMode = newViewMode;
_showStrokeSlider = false;
- _showModelMenu = false; // Close menu if view mode toggled
+ _showModelMenu = false;
});
widget.onViewModeToggle(newViewMode);
}
- // NEW: Toggle Dropdown Visibility
void _toggleModelMenu() {
setState(() {
- if (!_showModelMenu)
- _showStrokeSlider = false; // Close slider if menu opens
+ if (!_showModelMenu) _showStrokeSlider = false;
_showModelMenu = !_showModelMenu;
});
}
@@ -122,7 +152,7 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
void _handleToolTap(VoidCallback toolAction) {
setState(() {
_showStrokeSlider = false;
- _showModelMenu = false; // Close menu on tool tap
+ _showModelMenu = false;
if (_isViewMode) {
_isViewMode = false;
widget.onViewModeToggle(false);
@@ -148,7 +178,6 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
if (_showStrokeSlider) _buildTaperedStrokeSlider(),
const SizedBox(height: 8),
- // WRAPPER CONTAINER FOR MENU + PANEL
Container(
decoration: BoxDecoration(
color: Colors.white,
@@ -164,9 +193,7 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
- // RENDER DROPDOWN ON TOP
if (_showModelMenu) _buildModelDropdown(),
-
_buildMagicDrawPanel(),
],
),
@@ -204,20 +231,19 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
);
}
- // --- NEW: DROPDOWN MENU UI ---
Widget _buildModelDropdown() {
return Container(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- const Text(
- 'Local',
- style: TextStyle(color: Colors.grey, fontSize: 12),
+ Text(
+ widget.hasImageLayers ? 'Inpainting Models' : 'Sketch Models',
+ style: const TextStyle(color: Colors.grey, fontSize: 12),
),
const SizedBox(height: 4),
- ..._aiModels.map((model) {
+ ..._currentModelList.map((model) {
final isSelected = model.id == _selectedModel.id;
return GestureDetector(
onTap: () {
@@ -283,7 +309,6 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
- // --- TRIGGER ICON FOR DROPDOWN ---
GestureDetector(
onTap: _toggleModelMenu,
child: Container(
@@ -294,7 +319,7 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
- Icons.star_half, // Using star icon as requested
+ Icons.star_half,
color: Color(0xFFD8705D),
size: 20,
),
@@ -350,7 +375,7 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
children: [
_buildToolIcon(Icons.pan_tool, widget.isMagicPanelDisabled, () {
setState(() => _showStrokeSlider = false);
- _showModelMenu = false; // Close menu
+ _showModelMenu = false;
if (!widget.isMagicPanelDisabled) {
widget.onEraserToggle(false);
@@ -366,7 +391,7 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
() {
setState(() {
_showStrokeSlider = false;
- _showModelMenu = false; // Close menu
+ _showModelMenu = false;
});
if (_isViewMode) {
@@ -421,7 +446,7 @@ class _MagicDrawToolsState extends State<MagicDrawTools> {
setState(() {
_showStrokeSlider = !_showStrokeSlider;
- _showModelMenu = false; // Close menu
+ _showModelMenu = false;
});
},
child: Container(
@@ -1002,7 +1027,6 @@ class _AdvancedColorPickerSheetState extends State<_AdvancedColorPickerSheet>
],
),
),
- // EXCLUDED: Gradient feature as requested.
],
),
),