creek

The AI Image Editor of 2030
commit d601ad746c8825abbe47fedcd6b3a4274bb9f26d
parent 3b4a61633cd4d6a6a9a0af74dd1906a5076b7bc5
Author: maydayv7 <maydayv7@gmail.com>
Date:   Wed,  3 Dec 2025 03:54:13 +0530

Revert stylesheet to prompt generation

Diffstat:
Mlib/services/flask_service.dart | 316++++---------------------------------------------------------------------------
1 file changed, 14 insertions(+), 302 deletions(-)

diff --git a/lib/services/flask_service.dart b/lib/services/flask_service.dart @@ -22,7 +22,6 @@ class FlaskService { static String get _urlAsset => dotenv.env['URL_ASSET'] ?? ''; static String get _urlDescribe => dotenv.env['URL_DESCRIBE'] ?? ''; - static const Map<String, String> _headers = { 'Content-Type': 'application/json', }; @@ -80,271 +79,13 @@ class FlaskService { } // =========================================================================== - // 1.5. STYLE PROMPT GENERATION (From Stylesheet - New Feature) - // =========================================================================== - - /// Generates a style prompt from a stylesheet JSON following the notebook logic. - /// Returns a formatted style instruction string or a default fallback. - String generateStylePromptFromStylesheet(String? stylesheetJson) { - if (stylesheetJson == null || stylesheetJson.isEmpty) { - return "high quality, realistic"; - } - - try { - // Parse the JSON - Map<String, dynamic> sheet = jsonDecode(stylesheetJson); - - // Normalize: extract from "results" if present - Map<String, dynamic> results = - sheet.containsKey('results') ? sheet['results'] as Map<String, dynamic> : sheet; - - // Normalize the sheet structure - Map<String, Map<String, dynamic>> normalized = _normalizeStylesheet(results); - - // Extract style phrases - List<String> stylePhrases = _pickStylePhrases(normalized); - - // Extract top colors - List<String> topColors = _extractTopColors(results); - - // Build style instruction - String styleInstruction = ""; - if (stylePhrases.isNotEmpty) { - styleInstruction = - "User prefers these style cues (use as inspiration): ${stylePhrases.join('; ')}"; - } - if (topColors.isNotEmpty) { - String colorStr = topColors.join(", "); - if (styleInstruction.isNotEmpty) { - styleInstruction += "; color palette: $colorStr"; - } else { - styleInstruction = "color palette: $colorStr"; - } - } - - // Get tone hint from primary style - String primaryStyle = ""; - if (normalized.containsKey('Style')) { - dynamic primary = normalized['Style']!['Primary']; - primaryStyle = - primary is String ? primary : (primary is List && primary.isNotEmpty ? primary[0] : ""); - } - String toneHint = _styleToTone(primaryStyle); - - // Combine everything - List<String> parts = []; - if (styleInstruction.isNotEmpty) { - parts.add(styleInstruction); - } - parts.add("Tone: $toneHint"); - - return parts.join(". "); - } catch (e) { - debugPrint("❌ Error generating style prompt: $e"); - return "high quality, realistic"; - } - } - - /// Normalizes the stylesheet structure into a consistent format - Map<String, Map<String, dynamic>> _normalizeStylesheet(Map<String, dynamic> results) { - Map<String, Map<String, dynamic>> normalized = {}; - - // Categories to extract - List<String> categories = [ - "Style", - "Background/Texture", - "Lighting", - "Composition", - "Era/Cultural Reference", - "Material Look", - "Typography", - ]; - - // Also check for variations - Map<String, List<String>> categoryAliases = { - "Style": ["style", "Style"], - "Background/Texture": [ - "Background/Texture", - "background/texture", - "Texture", - "texture", - "Background", - "background" - ], - "Lighting": ["Lighting", "lighting"], - "Composition": ["Composition", "composition", "Compositions", "compositions"], - "Era/Cultural Reference": ["Era/Cultural Reference", "era / reference", "Era", "era"], - "Material Look": ["Material Look", "material look", "Material look"], - "Typography": ["Typography", "typography", "Fonts", "fonts"], - }; - - for (String category in categories) { - List<String> labels = []; - List<String> aliases = categoryAliases[category] ?? [category]; - - // Find the category in results - dynamic categoryData; - for (String alias in aliases) { - if (results.containsKey(alias)) { - categoryData = results[alias]; - break; - } - } - - if (categoryData == null) continue; - - // Extract labels from the category data - List<dynamic> items = []; - if (categoryData is List) { - items = categoryData; - } else if (categoryData is Map && categoryData.containsKey('scores')) { - items = categoryData['scores'] is List ? categoryData['scores'] : []; - } - - // Sort by score if available - try { - items.sort((a, b) { - double scoreA = 0.0, scoreB = 0.0; - if (a is Map) scoreA = (a['score'] ?? 0.0).toDouble(); - if (b is Map) scoreB = (b['score'] ?? 0.0).toDouble(); - return scoreB.compareTo(scoreA); - }); - } catch (_) {} - - // Extract labels - for (var item in items) { - String? label; - if (item is Map) { - label = item['label']?.toString(); - } else if (item is String) { - label = item; - } - if (label != null && label.isNotEmpty) { - labels.add(label.trim()); - } - } - - if (labels.isNotEmpty) { - normalized[category] = { - 'Primary': labels.first, - 'Secondary': labels.length > 1 ? labels.sublist(1) : [], - }; - } - } - - return normalized; - } - - /// Picks style phrases from normalized stylesheet (following notebook logic) - List<String> _pickStylePhrases(Map<String, Map<String, dynamic>> normalized, - {int maxSecondaries = 3}) { - Map<String, String> keyMap = { - "Style": "style", - "Background/Texture": "background/texture", - "Lighting": "lighting", - "Composition": "composition", - "Era/Cultural Reference": "era / reference", - "Material Look": "material look", - "Typography": "typography", - }; - - List<String> phrases = []; - - for (String category in normalized.keys) { - Map<String, dynamic>? entry = normalized[category]; - if (entry == null) continue; - - dynamic primaryValue = entry['Primary']; - List<String> primary = - primaryValue is String ? [primaryValue] : (primaryValue is List ? List<String>.from(primaryValue) : []); - List<String> secondary = entry['Secondary'] ?? []; - - List<String> chosen = []; - if (primary.isNotEmpty) { - chosen.addAll(primary); - } - for (String s in secondary) { - if (chosen.length < (1 + maxSecondaries)) { - chosen.add(s); - } - } - - if (chosen.isNotEmpty) { - String key = keyMap[category] ?? category.toLowerCase(); - phrases.add("$key: ${chosen.join(', ')}"); - } - } - - return phrases; - } - - /// Extracts top colors from stylesheet - List<String> _extractTopColors(Map<String, dynamic> results, {int maxColors = 5}) { - dynamic palette = results['Color Palette'] ?? - results['Colour Palette'] ?? - results['colors'] ?? - results['Colors']; - - if (palette == null) return []; - - List<dynamic> paletteList = []; - if (palette is List) { - paletteList = palette; - } else if (palette is Map && palette.containsKey('scores')) { - paletteList = palette['scores'] is List ? palette['scores'] : []; - } - - // Sort by score - try { - paletteList.sort((a, b) { - double scoreA = 0.0, scoreB = 0.0; - if (a is Map) scoreA = (a['score'] ?? 0.0).toDouble(); - if (b is Map) scoreB = (b['score'] ?? 0.0).toDouble(); - return scoreB.compareTo(scoreA); - }); - } catch (_) {} - - List<String> colors = []; - for (var item in paletteList) { - String? label; - if (item is Map) { - label = item['label']?.toString(); - } else if (item is String) { - label = item; - } - if (label != null && label.isNotEmpty) { - colors.add(label.trim()); - if (colors.length >= maxColors) break; - } - } - - return colors; - } - - /// Determines tone hint from primary style - String _styleToTone(String primary) { - String s = primary.toLowerCase(); - if (s.contains("retro") || - s.contains("collage") || - s.contains("poster") || - s.contains("pop-art") || - s.contains("surreal")) { - return "illustrative, textured, poster-like"; - } - if (s.contains("photoreal") || s.contains("realistic") || s.contains("film")) { - return "photorealistic, high-detail"; - } - return "high detail, sharp focus"; - } - - // =========================================================================== // 2. GENERATION SERVICES (Returns File Path) // =========================================================================== /// [Text-to-Image] Future<String?> generateAndSaveImage(String prompt) async { return _performImageOperation( - endpoint: '/generate', + fullUrl: _urlGenerate, logPrefix: '🎨 Text-to-Image', body: {'prompt': prompt}, filenamePrefix: prompt, @@ -363,7 +104,7 @@ class FlaskService { if (base64Image == null || base64Mask == null) return null; return _performImageOperation( - endpoint: '/inpainting', + fullUrl: _urlInpainting, logPrefix: '🖌️ Inpainting', body: { 'prompt': prompt, @@ -445,15 +186,8 @@ class FlaskService { // This returns the path to the no-background version return generateAsset(imagePath: generatedImagePath); - - - - - } - - /// [Background Removal] Future<String?> generateAsset({required String imagePath}) async { // 1. Prepare and Upload @@ -461,7 +195,7 @@ class FlaskService { if (base64Image == null) return null; final String? generatedAssetPath = await _performImageOperation( - endpoint: '/asset', + fullUrl: _urlAsset, logPrefix: '✂️ Asset Gen', body: {'image': base64Image}, filenamePrefix: 'asset', @@ -525,6 +259,8 @@ class FlaskService { required String imagePath, String prompt = '<MORE_DETAILED_CAPTION>', }) async { + debugPrint("👁️ [Describe] Preparing request..."); + final String? base64Image = await _encodeFile(imagePath); if (base64Image == null) return null; @@ -550,47 +286,22 @@ class FlaskService { // =========================================================================== Future<String?> _performImageOperation({ - String? endpoint, - String? fullUrl, + required String fullUrl, required String logPrefix, required Map<String, dynamic> body, required String filenamePrefix, }) async { - String url; - if (fullUrl != null) { - url = fullUrl; - } else if (endpoint != null) { - // Map endpoints to their corresponding URL getters - switch (endpoint) { - case '/generate': - url = _urlGenerate; - break; - case '/inpainting': - url = _urlInpainting; - break; - case '/asset': - url = _urlAsset; - break; - case '/describe': - url = _urlDescribe; - break; - default: - url = ''; - } - } else { - return null; - } + debugPrint("$logPrefix Sending request to $fullUrl..."); - if (url.isEmpty) { - return null; - } - - final response = await _postRequest(fullUrl: url, body: body); + final response = await _postRequest(fullUrl: fullUrl, body: body); if (response != null && response.statusCode == 200) { return _saveImageFromResponse(response, filenamePrefix); } + debugPrint( + "❌ $logPrefix Failed: ${response?.statusCode ?? 'No Connection'}", + ); return null; } @@ -600,6 +311,7 @@ class FlaskService { }) async { try { if (fullUrl.isEmpty) { + debugPrint("❌ Config Error: URL is missing in .env"); return null; } return await http.post( @@ -608,6 +320,7 @@ class FlaskService { body: jsonEncode(body), ); } catch (e) { + debugPrint("❌ Network Error ($fullUrl): $e"); return null; } } @@ -660,4 +373,4 @@ class FlaskService { return null; } } -} -\ No newline at end of file +}