eubottura commited on
Commit
3b58a4a
·
verified ·
1 Parent(s): 41f1882

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +1021 -19
index.html CHANGED
@@ -1,19 +1,1021 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pt-BR" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PromptCraft Studio v2.0</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
9
+ <style>
10
+ body {
11
+ font-family: 'Inter', system-ui, sans-serif;
12
+ background-color: #050505;
13
+ color: #ffffff;
14
+ }
15
+ .step {
16
+ display: none;
17
+ opacity: 0;
18
+ transform: translateY(10px);
19
+ transition: opacity 0.4s ease, transform 0.4s ease;
20
+ }
21
+ .step.active {
22
+ display: block;
23
+ opacity: 1;
24
+ transform: translateY(0);
25
+ }
26
+ .drop-zone {
27
+ transition: all 0.2s ease;
28
+ border-color: #333;
29
+ }
30
+ .drop-zone.drag-active {
31
+ border-color: #fff;
32
+ background-color: rgba(255, 255, 255, 0.05);
33
+ transform: scale(1.01);
34
+ }
35
+ .glass-panel {
36
+ background: rgba(20, 20, 20, 0.6);
37
+ backdrop-filter: blur(12px);
38
+ border: 1px solid rgba(255, 255, 255, 0.08);
39
+ }
40
+ .btn-primary {
41
+ background: white;
42
+ color: black;
43
+ transition: all 0.2s;
44
+ }
45
+ .btn-primary:hover {
46
+ transform: translateY(-1px);
47
+ box-shadow: 0 4px 12px rgba(255, 255, 255, 0.15);
48
+ }
49
+ .loader {
50
+ border: 3px solid rgba(255, 255, 255, 0.1);
51
+ }
52
+ @keyframes spin {
53
+ 0% { transform: rotate(0deg); }
54
+ 100% { transform: rotate(360deg); }
55
+ }
56
+ ::-webkit-scrollbar {
57
+ width: 8px;
58
+ height: 8px;
59
+ }
60
+ ::-webkit-scrollbar-track {
61
+ background: #111;
62
+ }
63
+ ::-webkit-scrollbar-thumb {
64
+ background: #333;
65
+ border-radius: 4px;
66
+ }
67
+ ::-webkit-scrollbar-thumb:hover {
68
+ background: #444;
69
+ }
70
+ .concept-card {
71
+ transition: all 0.3s ease;
72
+ }
73
+ .concept-card.selected {
74
+ border-color: white;
75
+ background-color: rgba(255, 255, 255, 0.05);
76
+ }
77
+ .fade-in {
78
+ animation: fadeIn 0.5s ease-in;
79
+ }
80
+ @keyframes fadeIn {
81
+ from { opacity: 0; }
82
+ to { opacity: 1; }
83
+ }
84
+ .color-chip {
85
+ display: inline-flex;
86
+ align-items: center;
87
+ gap: 0.5rem;
88
+ padding: 0.5rem 1rem;
89
+ background-color: rgba(55, 65, 81, 0.5);
90
+ border-radius: 9999px;
91
+ border: 1px solid rgba(55, 65, 81, 0.7);
92
+ }
93
+ .toast {
94
+ position: fixed;
95
+ bottom: 1rem;
96
+ right: 1rem;
97
+ background-color: rgba(31, 41, 55, 0.9);
98
+ color: white;
99
+ padding: 0.75rem 1rem;
100
+ border-radius: 0.5rem;
101
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
102
+ animation: pulse 2s infinite;
103
+ }
104
+ .toast.success {
105
+ background-color: rgba(34, 197, 94, 0.9);
106
+ }
107
+ .toast.error {
108
+ background-color: rgba(239, 68, 68, 0.9);
109
+ }
110
+ </style>
111
+ </head>
112
+ <body class="min-h-screen flex flex-col antialiased">
113
+ <!-- Header -->
114
+ <header class="border-b border-gray-800 glass-panel sticky top-0 z-50">
115
+ <div class="container mx-auto px-6 py-4 flex justify-between items-center">
116
+ <div class="flex items-center gap-3">
117
+ <div class="w-8 h-8 bg-white rounded flex items-center justify-center text-black font-bold text-lg">P</div>
118
+ <span class="font-bold text-xl tracking-tight">PromptCraft Studio</span>
119
+ </div>
120
+ <div class="text-xs text-gray-500 font-mono">v2.0 Optimized</div>
121
+ </div>
122
+ </header>
123
+
124
+ <!-- Main Content -->
125
+ <main class="flex-grow container mx-auto px-4 py-8">
126
+ <div class="max-w-5xl mx-auto">
127
+ <!-- Hero Section -->
128
+ <section class="text-center mb-10">
129
+ <h1 class="text-4xl md:text-5xl font-extrabold mb-4 bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-400">Product Photography AI</h1>
130
+ <p class="text-gray-400 text-lg max-w-2xl mx-auto">
131
+ Transforme produtos simples em campanhas editoriais de alta performance com Inteligência Artificial.
132
+ </p>
133
+ </section>
134
+
135
+ <!-- App Container -->
136
+ <div class="glass-panel rounded-2xl p-6 md:p-10 shadow-2xl relative overflow-hidden">
137
+ <!-- Progress Bar -->
138
+ <div class="absolute top-0 left-0 h-1 bg-gradient-to-r from-blue-500 to-purple-500 transition-all duration-500" id="progress-bar" style="width: 25%"></div>
139
+
140
+ <!-- NOTIFICATIONS / TOASTS -->
141
+ <div id="toast-container" class="fixed top-24 right-6 flex flex-col gap-2 z-50 pointer-events-none"></div>
142
+
143
+ <!-- STEP 1: PRODUCT & IMAGES -->
144
+ <section id="step-product" class="step active" aria-label="Passo 1: Produto e Imagens">
145
+ <h2 class="text-2xl font-bold mb-6 text-center">O que vamos fotografar hoje?</h2>
146
+
147
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
148
+ <div>
149
+ <label class="block text-sm font-medium text-gray-300 mb-2">Imagens de Referência</label>
150
+ <div id="drop-area"
151
+ class="drop-zone border-2 border-dashed border-gray-700 rounded-xl p-8 text-center cursor-pointer hover:border-gray-500 transition-all h-64 flex flex-col items-center justify-center relative bg-gray-900/50">
152
+ <i data-feather="upload-cloud" class="w-10 h-10 mb-3 text-gray-400"></i>
153
+ <p class="text-lg font-medium text-white">Arraste fotos aqui</p>
154
+ <p class="text-sm text-gray-500 mt-1">ou clique para selecionar</p>
155
+ <input type="file" id="image-upload" accept="image/*" multiple class="hidden">
156
+ <div id="uploading-overlay" class="hidden absolute inset-0 bg-black/80 flex flex-col items-center justify-center rounded-xl z-10">
157
+ <div class="loader border-blue-500 mb-2"></div>
158
+ <span class="text-xs text-gray-300">Enviando para Imgur...</span>
159
+ </div>
160
+ </div>
161
+
162
+ <!-- Preview Container -->
163
+ <div id="image-preview-container" class="hidden mt-4 grid grid-cols-3 gap-2"></div>
164
+ </div>
165
+
166
+ <div class="flex flex-col gap-6">
167
+ <div>
168
+ <label for="product-input" class="block text-sm font-medium text-gray-300 mb-2">Nome do Produto</label>
169
+ <input type="text" id="product-input"
170
+ class="w-full bg-gray-800 border border-gray-700 rounded-xl px-4 py-3 focus:ring-2 focus:ring-white focus:border-transparent outline-none transition-all placeholder-gray-600"
171
+ placeholder="Ex: Camiseta Oversized Algodão Egípcio 100%">
172
+ </div>
173
+ </div>
174
+ </div>
175
+
176
+ <div class="mt-8 flex justify-end">
177
+ <button id="btn-next-1" onclick="nextStep('colors')"
178
+ class="btn-primary px-8 py-3 rounded-xl font-bold flex items-center gap-2">
179
+ Continuar <i data-feather="arrow-right" class="w-4 h-4"></i>
180
+ </button>
181
+ </div>
182
+ </section>
183
+
184
+ <!-- STEP 2: COLORS -->
185
+ <section id="step-colors" class="step hidden" aria-label="Passo 2: Cores e Detalhes">
186
+ <h2 class="text-2xl font-bold mb-6 text-center">Paleta de Cores</h2>
187
+
188
+ <div class="bg-gray-800 rounded-xl p-6 mb-6 border border-gray-700">
189
+ <h3 class="text-sm font-medium text-gray-400 mb-3 uppercase tracking-wider">Cores Detectadas</h3>
190
+ <div id="detected-colors" class="flex flex-wrap gap-3 min-h-[40px]">
191
+ <span class="text-sm text-gray-400 italic">Nenhuma cor detectada</span>
192
+ </div>
193
+ </div>
194
+
195
+ <div class="mb-8">
196
+ <label for="colors-input" class="block text-sm font-medium text-gray-300 mb-2">Cores Adicionais</label>
197
+ <input type="text" id="colors-input"
198
+ class="w-full bg-gray-800 border border-gray-700 rounded-xl px-4 py-3 focus:ring-2 focus:ring-white focus:border-transparent outline-none placeholder-gray-600"
199
+ placeholder="Ex: Off-White, Bege, Navy (separadas por vírgula)">
200
+ </div>
201
+
202
+ <div class="flex justify-between items-center">
203
+ <button class="text-gray-400 hover:text-white px-4 py-2 font-medium transition-colors" onclick="prevStep('product')">
204
+ Voltar
205
+ </button>
206
+ <button id="btn-next-2" onclick="nextStep('concept')"
207
+ class="btn-primary px-8 py-3 rounded-xl font-bold flex items-center gap-2">
208
+ Definir Conceito <i data-feather="image" class="w-4 h-4"></i>
209
+ </button>
210
+ </div>
211
+ </section>
212
+
213
+ <!-- STEP 3: CONCEPTS -->
214
+ <section id="step-concept" class="step hidden" aria-label="Passo 3: Conceito Visual">
215
+ <h2 class="text-2xl font-bold mb-8 text-center">Escolha o Estilo</h2>
216
+
217
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8" role="radiogroup">
218
+ <button class="concept-card w-full text-left p-6 rounded-xl border border-gray-700 hover:border-white hover:bg-white/5 transition-all group"
219
+ data-concept="FOTO PRINCIPAL" onclick="selectConcept('FOTO PRINCIPAL')">
220
+ <i data-feather="aperture" class="w-8 h-8 mb-4 text-gray-400 group-hover:text-white"></i>
221
+ <h3 class="font-bold text-lg mb-1">Foto Principal (Catálogo)</h3>
222
+ <p class="text-sm text-gray-500">Fundo infinito, iluminação de estúdio perfeita, foco 100% no produto.</p>
223
+ </button>
224
+
225
+ <button class="concept-card w-full text-left p-6 rounded-xl border border-gray-700 hover:border-white hover:bg-white/5 transition-all group"
226
+ data-concept="FOTO DESCRIÇÃO" onclick="selectConcept('FOTO DESCRIÇÃO')">
227
+ <i data-feather="zoom-in" class="w-8 h-8 mb-4 text-gray-400 group-hover:text-white"></i>
228
+ <h3 class="font-bold text-lg mb-1">Close-up Textura</h3>
229
+ <p class="text-sm text-gray-500">Detalhe macro do tecido e acabamento. Sensação tátil.</p>
230
+ </button>
231
+
232
+ <button class="concept-card w-full text-left p-6 rounded-xl border border-gray-700 hover:border-white hover:bg-white/5 transition-all group"
233
+ data-concept="FOTO AVALIAÇÃO" onclick="selectConcept('FOTO AVALIAÇÃO')">
234
+ <i data-feather="smartphone" class="w-8 h-8 mb-4 text-gray-400 group-hover:text-white"></i>
235
+ <h3 class="font-bold text-lg mb-1">Estilo "Review Real"</h3>
236
+ <p class="text-sm text-gray-500">Foto orgânica, estilo UGC (User Generated Content), luz natural.</p>
237
+ </button>
238
+
239
+ <button class="concept-card w-full text-left p-6 rounded-xl border border-gray-700 hover:border-white hover:bg-white/5 transition-all group"
240
+ data-concept="FOTO DESCRIÇÃO 2" onclick="selectConcept('FOTO DESCRIÇÃO 2')">
241
+ <i data-feather="user" class="w-8 h-8 mb-4 text-gray-400 group-hover:text-white"></i>
242
+ <h3 class="font-bold text-lg mb-1">Lifestyle / Modelo</h3>
243
+ <p class="text-sm text-gray-500">Produto sendo usado em contexto urbano ou estúdio.</p>
244
+ </button>
245
+ </div>
246
+
247
+ <div class="flex justify-between items-center">
248
+ <button class="text-gray-400 hover:text-white px-4 py-2 font-medium transition-colors" onclick="prevStep('colors')">Voltar</button>
249
+ </div>
250
+ </section>
251
+
252
+ <!-- STEP 4: GENERATION -->
253
+ <section id="step-result" class="step hidden" aria-label="Passo 4: Geração">
254
+ <h2 class="text-2xl font-bold mb-6 text-center">Seu Prompt Otimizado</h2>
255
+
256
+ <!-- API Key Management -->
257
+ <div class="mb-4">
258
+ <div class="flex justify-between items-center mb-2">
259
+ <label class="text-xs font-bold text-gray-500 uppercase tracking-widest">Google Gemini API Key</label>
260
+ </div>
261
+ <input type="password" id="api-key-input"
262
+ value="AIzaSyDGL3xn15bIkEHkI-24OMO9Ub2G_pDnQ-M"
263
+ class="w-full bg-black/30 border border-gray-800 rounded px-3 py-2 text-xs text-gray-400 focus:text-white transition-colors">
264
+ </div>
265
+
266
+ <div class="bg-gray-900 p-6 rounded-lg mb-6 relative group">
267
+ <div class="absolute top-4 right-4 opacity-0 group-hover:opacity-100 transition-opacity">
268
+ <button id="btn-copy-prompt" class="bg-white text-black px-3 py-1 text-xs font-bold rounded shadow hover:bg-gray-200 transition-colors">Copiar</button>
269
+ </div>
270
+ <pre id="prompt-output" class="p-6 text-xs md:text-sm font-mono text-gray-300 whitespace-pre-wrap max-h-64 overflow-y-auto custom-scrollbar"></pre>
271
+ </div>
272
+
273
+ <div class="mt-8 flex justify-center">
274
+ <button class="text-gray-500 hover:text-white text-sm" onclick="resetForm()">Começar Novo Projeto</button>
275
+ </div>
276
+ </section>
277
+ </div>
278
+ </div>
279
+ </main>
280
+
281
+ <!-- Footer -->
282
+ <footer class="border-t border-gray-900 mt-auto py-8">
283
+ <div class="container mx-auto px-4 text-center text-gray-600 text-sm">
284
+ PromptCraft Studio v2.0 • Powered by Google Gemini & Imagen
285
+ </div>
286
+ </footer>
287
+
288
+ <!-- Image Modal -->
289
+ <div id="image-modal" class="fixed inset-0 bg-black bg-opacity-90 flex items-center justify-center z-50 hidden">
290
+ <div class="relative max-w-4xl max-h-screen">
291
+ <button id="close-modal" class="absolute -top-10 right-0 text-white text-3xl cursor-pointer hover:text-gray-300">×</button>
292
+ <img id="modal-image" src="" alt="Full size preview" class="max-w-full max-h-screen object-contain">
293
+ </div>
294
+ </div>
295
+
296
+ <!-- APPLICATION LOGIC -->
297
+ <script>
298
+ // Sistema Imgur GARANTIDO - Upload Direto + Detecção Melhorada
299
+ // Gemini Flash Integration - Geração Interna de Imagens
300
+
301
+ // Store user inputs
302
+ let userData = {
303
+ product: '',
304
+ colors: '',
305
+ concept: '',
306
+ images: [], // Armazena objetos {file, dataUrl, uploadedUrl}
307
+ additionalColors: [],
308
+ hostedImageUrls: [],
309
+ detectedColors: []
310
+ };
311
+
312
+ // Store image URLs for sharing
313
+ let imageUrls = [];
314
+
315
+ // Variáveis de controle
316
+ let useImgurDirect = true; // SEMPRE usar Imgur diretamente
317
+ let uploadMethod = 'Imgur Direto'; // Uploader Imgur Direto (GARANTIDO)
318
+
319
+ class ImgurDirectUploader {
320
+ constructor() {
321
+ // Client ID público do Imgur (funciona sem configuração)
322
+ this.clientId = '546c25a59c58ad7';
323
+ }
324
+
325
+ async uploadImage(file, onProgress) {
326
+ return new Promise((resolve, reject) => {
327
+ const formData = new FormData();
328
+ formData.append('image', file);
329
+ formData.append('type', 'file');
330
+ formData.append('name', file.name);
331
+
332
+ const xhr = new XMLHttpRequest();
333
+
334
+ xhr.upload.addEventListener('progress', (e) => {
335
+ if (e.lengthComputable) {
336
+ const percentComplete = (e.loaded / e.total) * 100;
337
+ onProgress && onProgress(Math.round(percentComplete));
338
+ }});
339
+
340
+ xhr.addEventListener('load', () => {
341
+ if (xhr.status === 200) {
342
+ try {
343
+ const response = JSON.parse(xhr.responseText);
344
+ if (response.success && response.data) {
345
+ resolve({
346
+ id: response.data.id,
347
+ url: response.data.link,
348
+ deletehash: response.data.deletehash,
349
+ size: response.data.size,
350
+ name: file.name,
351
+ originalSize: file.size,
352
+ type: response.data.type || 'image/jpeg',
353
+ width: response.data.width,
354
+ height: response.data.height
355
+ });
356
+ } else {
357
+ reject(new Error('Resposta Imgur inválida'));
358
+ }
359
+ } catch (e) {
360
+ reject(new Error('Erro ao processar resposta Imgur'));
361
+ }
362
+ } else {
363
+ reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`));
364
+ }});
365
+
366
+ xhr.addEventListener('error', () => {
367
+ reject(new Error('Erro de rede Imgur'));
368
+ }});
369
+
370
+ xhr.addEventListener('timeout', () => {
371
+ reject(new Error('Timeout Imgur'));
372
+ }});
373
+
374
+ xhr.open('POST', 'https://api.imgur.com/3/image');
375
+ xhr.timeout = 30000;
376
+ xhr.setRequestHeader('Authorization', `Client-ID ${this.clientId}`));
377
+ xhr.send(formData);
378
+ }});
379
+ }
380
+
381
+ async uploadMultiple(files, onProgress) {
382
+ const results = [];
383
+ const totalFiles = files.length;
384
+
385
+ for (let i = 0; i < files.length; i++) {
386
+ try {
387
+ const file = files[i];
388
+ const result = await this.uploadImage(file, (progress) => {
389
+ const overallProgress = ((i + progress/100) / totalFiles) * 100;
390
+ onProgress && onProgress(Math.round(overallProgress), i + 1, totalFiles));
391
+ }});
392
+ results.push({ ...result, success: true });
393
+ } catch (error) {
394
+ console.warn(`Erro upload Imgur ${files[i].name}:`, error);
395
+ results.push({ success: false, error: error.message, name: files[i].name });
396
+ }
397
+ }
398
+
399
+ return results;
400
+ }
401
+ }
402
+
403
+ // Inicialização
404
+ let imgurUploader = null;
405
+
406
+ // Inicializar uploader Imgur
407
+ function initializeImgurUploader() {
408
+ imgurUploader = new ImgurDirectUploader();
409
+ console.log('✅ Uploader Imgur inicializado - Upload garantido!');
410
+ }
411
+
412
+ // Upload para Imgur (GARANTIDO)
413
+ async function uploadToImgur(imageFiles) {
414
+ if (!imgurUploader) {
415
+ initializeImgurUploader();
416
+ }
417
+
418
+ uploadMethod = 'Imgur Direto';
419
+ showUploadingState(true, uploadMethod);
420
+
421
+ try {
422
+ const results = await imgurUploader.uploadMultiple(
423
+ imageFiles.map(img => img.file),
424
+ (progress, current, total) => updateUploadProgress(progress, current, total)
425
+ );
426
+ const successful = results.filter(r => r.success);
427
+ if (successful.length > 0) {
428
+ userData.hostedImageUrls = successful.map(r => r.url);
429
+ console.log(`✅ Upload Imgur: ${successful.length}/${results.length} imagens');
430
+
431
+ // Atualizar status das imagens
432
+ results.forEach((result, index) => {
433
+ updateImageStatus(index + 1, result.success ? '✓ Imgur OK' : '❌ Falhou'));
434
+ }}});
435
+ return results;
436
+ } else {
437
+ throw new Error('Nenhuma imagem foi enviada com sucesso'));
438
+ }
439
+ } catch (error) {
440
+ console.error('Erro upload Imgur:', error);
441
+ throw error;
442
+ }
443
+ }
444
+
445
+ // Fallback Local (só se Imgur falhar completamente)
446
+ async function uploadToLocalFallback(imageFiles) {
447
+ uploadMethod = 'Local (Fallback)';
448
+ showUploadingState(true, uploadMethod);
449
+
450
+ const results = [];
451
+ for (let i = 0; i < imageFiles.length; i++) {
452
+ const imgData = imageFiles[i];
453
+ updateImageStatus(i + 1, '⚙️ Processando...'));
454
+ try {
455
+ await new Promise(resolve => setTimeout(resolve, 300));
456
+ userData.hostedImageUrls.push(imgData.dataUrl);
457
+ results.push({ success: true, url: imgData.dataUrl });
458
+ updateImageStatus(i + 1, '✓ Local OK'));
459
+ } catch (error) {
460
+ results.push({ success: false, error: error.message });
461
+ updateImageStatus(i + 1, '❌ Erro'));
462
+ }
463
+ const progress = Math.round(((i + 1) / imageFiles.length) * 100;
464
+ updateUploadProgress(progress, i + 1, imageFiles.length));
465
+ }}
466
+
467
+ return results;
468
+ }
469
+
470
+ // Navegação entre steps
471
+ async function nextStep(nextStepId) {
472
+ const currentStep = document.querySelector('.step.active'));
473
+ const nextStepElement = document.getElementById(`step-${nextStepId}`));
474
+ if (!currentStep || !nextStepElement) {
475
+ console.error('Step não encontrado'));
476
+ return;
477
+ }
478
+
479
+ if (currentStep.id === 'step-product') {
480
+ userData.product = document.getElementById('product-input').value.trim());
481
+ if (!userData.product) {
482
+ alert('Por favor, descreva o produto.'));
483
+ return;
484
+ }
485
+
486
+ // Inicializar uploader Imgur
487
+ initializeImgurUploader();
488
+
489
+ // Processar imagens
490
+ if (userData.images.length > 0) {
491
+ try {
492
+ // Tentar upload Imgur PRIMEIRO
493
+ try {
494
+ await uploadToImgur(userData.images);
495
+ } catch (imgurError) {
496
+ console.warn('⚠️ Imgur falhou, tentando fallback local:', imgurError);
497
+ await uploadToLocalFallback(userData.images);
498
+ }
499
+
500
+ // Detectar cores
501
+ const detectedColors = await detectColorsFromImages(userData.hostedImageUrls);
502
+ displayDetectedColors(detectedColors);
503
+ } catch (error) {
504
+ console.error('Erro crítico no processamento:', error);
505
+ alert('Erro ao processar imagens. Tente novamente.'));
506
+ return;
507
+ } finally {
508
+ showUploadingState(false));
509
+ }
510
+ }
511
+
512
+ // Navegar
513
+ currentStep.classList.remove('active'));
514
+ currentStep.classList.add('hidden'));
515
+ nextStepElement.classList.remove('hidden'));
516
+ nextStepElement.classList.add('active'));
517
+ } else if (currentStep.id === 'step-colors') {
518
+ const additionalColorsInput = document.getElementById('colors-input').value.trim());
519
+ if (additionalColorsInput) {
520
+ userData.additionalColors = additionalColorsInput.split(',').map(color => color.trim()));
521
+ } else {
522
+ userData.additionalColors = [];
523
+ }
524
+
525
+ const allColors = [...new Set([...(userData.detectedColors || []), ...userData.additionalColors])];
526
+ userData.colors = allColors.join(', '));
527
+ if (!userData.colors && userData.detectedColors.length === 0) {
528
+ alert('Por favor, informe as cores disponíveis.'));
529
+ return;
530
+ }
531
+
532
+ currentStep.classList.remove('active'));
533
+ currentStep.classList.add('hidden'));
534
+ nextStepElement.classList.remove('hidden'));
535
+ nextStepElement.classList.add('active'));
536
+ } else if (currentStep.id === 'step-concept') {
537
+ if (!userData.concept) {
538
+ alert('Por favor, selecione um conceito.'));
539
+ return;
540
+ }
541
+
542
+ await generatePrompt());
543
+ currentStep.classList.remove('active'));
544
+ currentStep.classList.add('hidden'));
545
+ nextStepElement.classList.remove('hidden'));
546
+ nextStepElement.classList.add('active'));
547
+ }
548
+ }
549
+
550
+ function prevStep(prevStepId) {
551
+ const currentStep = document.querySelector('.step.active'));
552
+ const prevStepElement = document.getElementById(`step-${prevStepId}`));
553
+ if (!currentStep || !prevStepElement) {
554
+ console.error('Step não encontrado'));
555
+ return;
556
+ }
557
+
558
+ currentStep.classList.remove('active'));
559
+ currentStep.classList.add('hidden'));
560
+ prevStepElement.classList.remove('hidden'));
561
+ prevStepElement.classList.add('active'));
562
+ }
563
+
564
+ // Estado de upload
565
+ function showUploadingState(show, mode = 'Imgur Direto') {
566
+ let loadingDiv = document.getElementById('uploading-overlay');
567
+ if (show) {
568
+ loadingDiv = document.createElement('div');
569
+ loadingDiv.id = 'uploading-overlay';
570
+ loadingDiv.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
571
+ loadingDiv.innerHTML = `
572
+ <div class="bg-gray-900 rounded-lg p-6 flex flex-col items-center gap-4 max-w-md w-full mx-4">
573
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 ${mode.includes('Imgur') ? 'border-blue-500' : 'border-green-500'}"></div>
574
+ <span class="text-white text-center font-medium">
575
+ ${mode.includes('Imgur') ? 'Enviando para Imgur...' : 'Processando localmente...'}
576
+ </span>
577
+ <div class="w-full">
578
+ <div class="flex justify-between text-xs text-gray-400 mb-1">
579
+ <span id="upload-status">Preparando...</span>
580
+ <span id="upload-progress">0%</span>
581
+ </div>
582
+ <div class="w-full bg-gray-800 rounded-full h-2">
583
+ <div id="upload-bar" class="${mode.includes('Imgur') ? 'bg-blue-500' : 'bg-green-500'} h-2 rounded-full transition-all duration-300" style="width: 0%"></div>
584
+ </div>
585
+ </div>
586
+ <div class="text-xs text-gray-500 text-center">
587
+ ${mode.includes('Imgur') ? 'Upload rápido e persistente' : 'Processamento no navegador'}
588
+ </div>
589
+ </div>
590
+ `;
591
+ document.body.appendChild(loadingDiv);
592
+ } else if (loadingDiv) {
593
+ loadingDiv.remove());
594
+ }
595
+ }
596
+
597
+ function updateUploadProgress(percent, current, total) {
598
+ const statusEl = document.getElementById('upload-status'));
599
+ const progressEl = document.getElementById('upload-progress'));
600
+ const barEl = document.getElementById('upload-bar'));
601
+ if (statusEl && progressEl && barEl) {
602
+ statusEl.textContent = `Enviando ${current || '?'} de ${total || '?'}';
603
+ progressEl.textContent = `${percent}%`;
604
+ barEl.style.width = `${percent}%`;
605
+ }
606
+ }
607
+
608
+ function updateImageStatus(imageIndex, status) {
609
+ const previews = document.querySelectorAll('#image-preview-container > div');
610
+ if (previews[imageIndex - 1]) {
611
+ const statusEl = previews[imageIndex - 1].querySelector('.upload-status'));
612
+ if (statusEl) {
613
+ statusEl.textContent = status;
614
+ const parentEl = statusEl.parentElement;
615
+ parentEl.classList.remove('bg-green-600', 'bg-blue-600', 'bg-red-600', 'bg-yellow-600', 'bg-gray-600'));
616
+ if (status.includes('✓ Imgur')) {
617
+ parentEl.classList.add('bg-blue-600'));
618
+ } else if (status.includes('✓ Local')) {
619
+ parentEl.classList.add('bg-green-600'));
620
+ } else if (status.includes('Enviando') || status.includes('Processando')) {
621
+ parentEl.classList.add('bg-yellow-600'));
622
+ } else if (status.includes('Erro')) {
623
+ parentEl.classList.add('bg-red-600'));
624
+ } else {
625
+ parentEl.classList.add('bg-gray-600'));
626
+ }
627
+ }
628
+ }
629
+
630
+ // Detecção de cores
631
+ async function detectColorsFromImages(imageSources) {
632
+ const detectedColors = [];
633
+ for (const source of imageSources) {
634
+ try {
635
+ const colors = await extractColorsFromImageSource(source));
636
+ detectedColors.push(...colors));
637
+ } catch (error) {
638
+ console.error('Erro ao processar imagem:', error);
639
+ }
640
+ }
641
+
642
+ const uniqueColors = [...new Set(detectedColors)].slice(0, 10);
643
+ return uniqueColors;
644
+ }
645
+
646
+ function extractColorsFromImageSource(imageSource) {
647
+ return new Promise((resolve) => {
648
+ const img = new Image();
649
+ if (imageSource.startsWith('http')) {
650
+ img.crossOrigin = 'anonymous';
651
+ }
652
+ img.onload = function() {
653
+ const canvas = document.createElement('canvas');
654
+ const ctx = canvas.getContext('2d'));
655
+ const maxSize = 100;
656
+ let { width, height } = img;
657
+ if (width > height) {
658
+ if (width > maxSize) {
659
+ height = (height * maxSize) / width;
660
+ width = maxSize;
661
+ }
662
+ } else {
663
+ if (height > maxSize) {
664
+ width = (width * maxSize) / height;
665
+ height = maxSize;
666
+ }
667
+ }
668
+ canvas.width = width;
669
+ canvas.height = height;
670
+ ctx.drawImage(img, 0, 0, width, height);
671
+ const imageData = ctx.getImageData(0, 0, width, height);
672
+ const data = imageData.data;
673
+ const colors = [];
674
+ const sampleRate = 5;
675
+ for (let i = 0; i < data.length; i += 4 * sampleRate) {
676
+ const r = data[i];
677
+ const g = data[i + 1];
678
+ const b = data[i + 2];
679
+ const a = data[i + 3];
680
+ if (a < 128) continue;
681
+ const hex = rgbToHex(r, g, b);
682
+ colors.push(hex);
683
+ }
684
+ const dominantColors = getDominantColors(colors, 3);
685
+ resolve(dominantColors);
686
+ };
687
+ img.onerror = () => {
688
+ console.warn('Não foi possível carregar imagem para análise de cores'));
689
+ resolve([]));
690
+ }};
691
+ img.src = imageSource;
692
+ }});
693
+ }
694
+
695
+ // Funções de cor
696
+ function rgbToHex(r, g, b) {
697
+ return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase());
698
+ }
699
+
700
+ function getDominantColors(colors, count) {
701
+ const colorCount = {};
702
+ colors.forEach(color => {
703
+ colorCount[color] = (colorCount[color] || 0) + 1;
704
+ }});
705
+ const sortedColors = Object.entries(colorCount)
706
+ .sort(([,a], [,b]) => b - a)
707
+ .map(([color]) => color);
708
+ const filteredColors = filterSimilarColors(sortedColors);
709
+ return filteredColors.slice(0, count);
710
+ }
711
+
712
+ function filterSimilarColors(colors) {
713
+ const filtered = [];
714
+ const threshold = 30;
715
+ for (const color of colors) {
716
+ let isSimilar = false;
717
+ for (const filteredColor of filtered) {
718
+ if (colorDifference(color, filteredColor) < threshold) {
719
+ isSimilar = true;
720
+ break;
721
+ }
722
+ }
723
+ if (!isSimilar) {
724
+ filtered.push(color));
725
+ }
726
+ }
727
+ return filtered;
728
+ }
729
+
730
+ function colorDifference(color1, color2) {
731
+ const rgb1 = hexToRgb(color1));
732
+ const rgb2 = hexToRgb(color2));
733
+ return Math.sqrt(
734
+ Math.pow(rgb1.r - rgb2.r, 2) +
735
+ Math.pow(rgb1.g - rgb2.g, 2) +
736
+ Math.pow(rgb1.b - rgb2.b, 2)
737
+ );
738
+ }
739
+
740
+ function hexToRgb(hex) {
741
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex));
742
+ return result ? {
743
+ r: parseInt(result[1], 16),
744
+ g: parseInt(result[2], 16),
745
+ b: parseInt(result[3], 16)
746
+ } : {r: 0, g: 0, b: 0};
747
+ }
748
+
749
+ function displayDetectedColors(colors) {
750
+ const container = document.getElementById('detected-colors'));
751
+ userData.detectedColors = colors;
752
+ if (colors.length === 0) {
753
+ container.innerHTML = '<span class="text-sm text-gray-400">Nenhuma cor detectada</span>';
754
+ return;
755
+ }
756
+ container.innerHTML = '';
757
+ colors.forEach((color, index) => {
758
+ const colorElement = document.createElement('div'));
759
+ colorElement.className = 'flex items-center gap-2 px-3 py-2 bg-gray-800 rounded-lg relative';
760
+ colorElement.innerHTML = `
761
+ <div class="w-4 h-4 rounded-full" style="background-color: ${color}"></div>
762
+ <span class="text-sm">${color}</span>
763
+ <button class="remove-color ml-2 text-red-500 hover:text-red-300" data-index="${index}">
764
+ <i data-feather="x" class="w-4 h-4"></i>
765
+ </button>
766
+ `;
767
+ container.appendChild(colorElement));
768
+ }});
769
+ container.querySelectorAll('.remove-color').forEach(button => {
770
+ button.addEventListener('click', function() {
771
+ const index = parseInt(this.getAttribute('data-index')));
772
+ removeDetectedColor(index));
773
+ }});
774
+ feather.replace());
775
+ }
776
+
777
+ function removeDetectedColor(index) {
778
+ if (userData.detectedColors && index >= 0 && index < userData.detectedColors.length) {
779
+ userData.detectedColors.splice(index, 1);
780
+ displayDetectedColors(userData.detectedColors));
781
+ const allColors = [...new Set([...userData.detectedColors, ...userData.additionalColors])];
782
+ userData.colors = allColors.join(', '));
783
+ }
784
+ }
785
+
786
+ // Seleção de conceito
787
+ async function selectConcept(concept) {
788
+ console.log('selectConcept() chamado com:', concept);
789
+ userData.concept = concept;
790
+ await nextStep('result'));
791
+ }
792
+
793
+ // Gerar prompt
794
+ async function generatePrompt() {
795
+ console.log('generatePrompt() iniciado');
796
+ showLoadingState(true));
797
+ try {
798
+ updatePromptDisplay(userData.concept));
799
+ createConceptSelector());
800
+ console.log('Prompt gerado com sucesso'));
801
+ } catch (error) {
802
+ console.error('Erro ao gerar prompt:', error);
803
+ alert('⚠️ Erro ao gerar prompt. Tente novamente.'));
804
+ } finally {
805
+ showLoadingState(false));
806
+ }
807
+ }
808
+
809
+ // Estado de loading
810
+ function showLoadingState(show) {
811
+ let loadingDiv = document.getElementById('loading-overlay'));
812
+ if (show) {
813
+ loadingDiv = document.createElement('div'));
814
+ loadingDiv.id = 'loading-overlay';
815
+ loadingDiv.className = 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
816
+ loadingDiv.innerHTML = `
817
+ <div class="bg-gray-900 rounded-lg p-6 flex flex-col items-center gap-4">
818
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-white"></div>
819
+ <span class="text-white">Processando...</span>
820
+ </div>
821
+ `;
822
+ document.body.appendChild(loadingDiv));
823
+ } else if (loadingDiv) {
824
+ loadingDiv.remove());
825
+ }
826
+ }
827
+
828
+ // Validador Técnico Imgur - Garantia de Integridade Visual
829
+ class ImgurTechnicalValidator {
830
+ constructor() {
831
+ this.validationResults = [];
832
+ }
833
+
834
+ async validateImageUrls(urls) {
835
+ this.validationResults = [];
836
+ const results = [];
837
+ for (let i = 0; i < urls.length; i++) {
838
+ const url = urls[i];
839
+ const result = await this.validateSingleImage(url, i + 1));
840
+ results.push(result);
841
+ this.validationResults.push(result));
842
+ }
843
+ return results;
844
+ }
845
+
846
+ async validateSingleImage(url, index) {
847
+ const isImgur = url.includes('imgur.com'));
848
+ try {
849
+ if (!isImgur) {
850
+ return {
851
+ index,
852
+ url,
853
+ status: 'WARNING',
854
+ message: 'URL não é do Imgur - integridade não garantida',
855
+ format: this.getFormatFromUrl(url),
856
+ size: 'N/A',
857
+ availability: 'Unknown'
858
+ };
859
+ }
860
+
861
+ // Validação técnica Imgur
862
+ const img = new Image();
863
+ img.crossOrigin = 'anonymous';
864
+ return new Promise((resolve) => {
865
+ const timeout = setTimeout(() => {
866
+ resolve({
867
+ index,
868
+ url,
869
+ status: 'ERROR',
870
+ message: 'Timeout na validação',
871
+ format: 'Unknown',
872
+ size: 'N/A',
873
+ availability: 'Failed'
874
+ }}, 10000));
875
+
876
+ img.onload = () => {
877
+ clearTimeout(timeout);
878
+ resolve({
879
+ index,
880
+ url,
881
+ status: 'VALIDATED',
882
+ message: 'URL Imgur validada com sucesso',
883
+ format: img.naturalWidth > 0 ? `${img.naturalWidth}x${img.naturalHeight}` : 'Unknown',
884
+ size: `${(img.naturalWidth * img.naturalHeight * 3 / 1024 / 1024).toFixed(2)}MB estimado',
885
+ availability: 'Online'
886
+ }};
887
+ };
888
+ img.onerror = () => {
889
+ clearTimeout(timeout);
890
+ resolve({
891
+ index,
892
+ url,
893
+ status: 'ERROR',
894
+ message: 'Imagem não disponível',
895
+ format: 'Unknown',
896
+ size: 'N/A',
897
+ availability: 'Offline'
898
+ }};
899
+ };
900
+ img.src = url;
901
+ }});
902
+ } catch (error) {
903
+ return {
904
+ index,
905
+ url,
906
+ status: 'ERROR',
907
+ message: error.message,
908
+ format: 'Unknown',
909
+ size: 'N/A',
910
+ availability: 'Failed'
911
+ };
912
+ }
913
+ }
914
+
915
+ getFormatFromUrl(url) {
916
+ if (url.startsWith('data:')) {
917
+ return 'Base64';
918
+ }
919
+ const extension = url.split('.').pop().split('?')[0];
920
+ return extension ? extension.toUpperCase() : 'Unknown';
921
+ }
922
+
923
+ generateValidationReport() {
924
+ const valid = this.validationResults.filter(r => r.status === 'VALIDATED').length;
925
+ const total = this.validationResults.length;
926
+ const hasImgur = this.validationResults.some(r => r.url.includes('imgur.com')));
927
+ return {
928
+ total,
929
+ valid,
930
+ invalid: total - valid,
931
+ hasImgur,
932
+ integrity: hasImgur && valid === total ? 'GARANTIDA' : 'PARCIAL',
933
+ report: this.validationResults
934
+ };
935
+ }
936
+ }
937
+
938
+ // Master V3.0 Prompt Structure - FINAL REFORCED
939
+ function generateMasterPromptV3(concept) {
940
+ console.log('🚀 GERANDO MASTER PROMPT v3.0 - FINAL REFOrCED');
941
+
942
+ // Validação técnica das imagens
943
+ const validator = new ImgurTechnicalValidator());
944
+ let validationReport = {
945
+ total: 0,
946
+ valid: 0,
947
+ invalid: 0,
948
+ hasImgur: false,
949
+ integrity: 'NENHUMA',
950
+ report: []
951
+ };
952
+ if (userData.hostedImageUrls && userData.hostedImageUrls.length > 0) {
953
+ validationReport = validator.generateValidationReport());
954
+ }
955
+
956
+ const masterPromptConfig = {
957
+ "prompt_version": "FINAL_REFORCED_v3.0",
958
+ "ai_role": "GEMINI - MASTER E-COMMERCE PHOTOGRAPHIC RENDERING AI WITH ULTRA-REALISM CAPABILITIES",
959
+ "objective": "GENERATE ULTRA-REALISTIC CINEMATIC IMAGE FOR PREMIUM E-COMMERCE",
960
+ "product_main": {
961
+ "name": userData.product,
962
+ "material": "High Quality Material (Texture focus)",
963
+ "quantity": "As specified in product name",
964
+ "colors": userData.colors,
965
+ "bonuses": "Include if mentioned in product name",
966
+ "reference_image": "Use input images as strict reference"
967
+ },
968
+ "imgur_validation": {
969
+ "total_images": validationReport.total,
970
+ "validated_images": validationReport.valid,
971
+ "integrity_status": validationReport.integrity,
972
+ "has_imgur_urls": validationReport.hasImgur,
973
+ "validation_details": validationReport.report.map(r => ({
974
+ index: r.index,
975
+ url: r.url,
976
+ status: r.status,
977
+ format: r.format,
978
+ availability: r.availability
979
+ }))
980
+ },
981
+ "imgur_urls": userData.hostedImageUrls,
982
+ "photo_blocks": {
983
+ "FOTO PRINCIPAL": {
984
+ "concept_name": "FOTO_PRINCIPAL",
985
+ "prompt_structure": {
986
+ "image_specifications": {
987
+ "format": "1:1 (square)",
988
+ "perspective": "completely realistic - front and aligned",
989
+ "prohibited_elements": ["promotional texts", "arrows", "seals", "watermarks", "graphic elements"]
990
+ },
991
+ "scene_composition": {
992
+ "background": "#F0F0F0 (very light gray)",
993
+ "base": "minimalist white marble pedestal with subtle veins",
994
+ "product_arrangement": {
995
+ "shirts": "OPEN AND ALIGNED HORIZONTALLY - NOT STACKED",
996
+ "spacing": "uniform spacing between each item",
997
+ "presentation": "each item fully opened/visible to show entire surface and texture"
998
+ },
999
+ "layout": "aesthetic and balanced arrangement - HORIZONTAL AND ORGANIZED",
1000
+ "visibility_rule": "all items fully visible, no overlaps hiding details"
1001
+ },
1002
+ "lighting_style": {
1003
+ "type": "professional studio lighting - MAXIMUM HIGHLIGHT",
1004
+ "equipment": "softboxes + strategic spotlights",
1005
+ "shadows": "soft, diffused and realistic - no distractions",
1006
+ "contrast": "HIGH contrast for maximum outline and volume emphasis"
1007
+ },
1008
+ "visual_quality": {
1009
+ "style": "hyper-realistic cinematic - MAXIMUM PROFESSIONALISM",
1010
+ "reference_quality": "premium fashion magazine cover quality",
1011
+ "depth_of_field": {
1012
+ "main_focus": "ALL ITEMS SHARP FOCUS",
1013
+ "accessories": "slight blur, but recognizable and sharp"
1014
+ }
1015
+ },
1016
+ "strict_prohibitions": [
1017
+ "DO NOT add promotional texts",
1018
+ "DO NOT distort perspective",
1019
+ "DO NOT create items not present",
1020
+ "DO NOT hide important parts",
1021
+ "DO NOT use