Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- index.html +1021 -19
index.html
CHANGED
|
@@ -1,19 +1,1021 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|