eubottura commited on
Commit
66039ae
·
verified ·
1 Parent(s): efe4ac2

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +700 -714
index.html CHANGED
@@ -1,721 +1,707 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>AI Prompt Generator | Variable Injection Specialist</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com">
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
- <style>
11
- :root {
12
- --bg-body: #0f172a;
13
- --bg-card: #1e293b;
14
- --bg-input: #334155;
15
- --text-main: #f8fafc;
16
- --text-muted: #94a3b8;
17
- --primary: #3b82f6;
18
- --primary-hover: #2563eb;
19
- --accent: #10b981;
20
- --border: #334155;
21
- --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
22
- --radius: 12px;
23
- }
24
-
25
- * {
26
- box-sizing: border-box;
27
- margin: 0;
28
- padding: 0;
29
- }
30
-
31
- body {
32
- font-family: 'Inter', sans-serif;
33
- background-color: var(--bg-body);
34
- color: var(--text-main);
35
- line-height: 1.6;
36
- min-height: 100vh;
37
- display: flex;
38
- flex-direction: column;
39
- }
40
-
41
- /* Header */
42
- header {
43
- background-color: var(--bg-card);
44
- border-bottom: 1px solid var(--border);
45
- padding: 1rem 2rem;
46
- display: flex;
47
- justify-content: space-between;
48
- align-items: center;
49
- position: sticky;
50
- top: 0;
51
- z-index: 100;
52
- }
53
-
54
- .brand {
55
- font-size: 1.25rem;
56
- font-weight: 700;
57
- background: linear-gradient(to right, var(--primary), var(--accent));
58
- -webkit-background-clip: text;
59
- -webkit-text-fill-color: transparent;
60
- letter-spacing: -0.025em;
61
- }
62
-
63
- .anycoder-link {
64
- font-size: 0.875rem;
65
- color: var(--text-muted);
66
- text-decoration: none;
67
- transition: color 0.2s;
68
- border: 1px solid var(--border);
69
- padding: 0.25rem 0.75rem;
70
- border-radius: 99px;
71
- }
72
 
73
- .anycoder-link:hover {
74
- color: var(--primary);
75
- border-color: var(--primary);
76
- }
77
-
78
- /* Main Layout */
79
- main {
80
- flex: 1;
81
- max-width: 1400px;
82
- margin: 0 auto;
83
- width: 100%;
84
- padding: 2rem;
85
- display: grid;
86
- grid-template-columns: 350px 1fr;
87
- gap: 2rem;
88
- }
89
-
90
- /* Sidebar / Inputs */
91
- aside {
92
- background-color: var(--bg-card);
93
- border-radius: var(--radius);
94
- padding: 1.5rem;
95
- border: 1px solid var(--border);
96
- height: fit-content;
97
- position: sticky;
98
- top: 6rem;
99
- box-shadow: var(--shadow);
100
- }
101
-
102
- h2 {
103
- font-size: 1.1rem;
104
- margin-bottom: 1.5rem;
105
- color: var(--text-main);
106
- font-weight: 600;
107
- display: flex;
108
- align-items: center;
109
- gap: 0.5rem;
110
- }
111
-
112
- .form-group {
113
- margin-bottom: 1.5rem;
114
- }
115
-
116
- label {
117
- display: block;
118
- font-size: 0.875rem;
119
- color: var(--text-muted);
120
- margin-bottom: 0.5rem;
121
- font-weight: 500;
122
- }
123
-
124
- input, textarea {
125
- width: 100%;
126
- background-color: var(--bg-input);
127
- border: 1px solid var(--border);
128
- border-radius: 8px;
129
- padding: 0.75rem 1rem;
130
- color: var(--text-main);
131
- font-family: inherit;
132
- font-size: 0.95rem;
133
- transition: all 0.2s ease;
134
- }
135
-
136
- textarea {
137
- resize: vertical;
138
- min-height: 100px;
139
- }
140
-
141
- input:focus, textarea:focus {
142
- outline: none;
143
- border-color: var(--primary);
144
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
145
- }
146
-
147
- .helper-text {
148
- font-size: 0.75rem;
149
- color: var(--text-muted);
150
- margin-top: 0.5rem;
151
- font-style: italic;
152
- }
153
-
154
- button.btn-primary {
155
- width: 100%;
156
- padding: 0.875rem;
157
- background-color: var(--primary);
158
- color: white;
159
- border: none;
160
- border-radius: 8px;
161
- font-weight: 600;
162
- font-size: 1rem;
163
- cursor: pointer;
164
- transition: background-color 0.2s, transform 0.1s;
165
- }
166
-
167
- button.btn-primary:hover {
168
- background-color: var(--primary-hover);
169
- }
170
-
171
- button.btn-primary:active {
172
- transform: scale(0.98);
173
- }
174
-
175
- /* Output Section */
176
- .output-container {
177
- display: flex;
178
- flex-direction: column;
179
- gap: 2rem;
180
- }
181
-
182
- .results-header {
183
- display: flex;
184
- justify-content: space-between;
185
- align-items: center;
186
- }
187
-
188
- .status-badge {
189
- font-size: 0.875rem;
190
- padding: 0.25rem 0.75rem;
191
- border-radius: 99px;
192
- background-color: var(--bg-input);
193
- color: var(--text-muted);
194
- }
195
-
196
- .grid {
197
- display: grid;
198
- grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
199
- gap: 1.5rem;
200
- }
201
-
202
- .card {
203
- background-color: var(--bg-card);
204
- border: 1px solid var(--border);
205
- border-radius: var(--radius);
206
- padding: 1.5rem;
207
- display: flex;
208
- flex-direction: column;
209
- transition: transform 0.2s, box-shadow 0.2s;
210
- position: relative;
211
- overflow: hidden;
212
- }
213
-
214
- .card:hover {
215
- transform: translateY(-2px);
216
- box-shadow: var(--shadow);
217
- border-color: var(--primary);
218
- }
219
-
220
- .card-header {
221
- display: flex;
222
- justify-content: space-between;
223
- align-items: flex-start;
224
- margin-bottom: 1rem;
225
- }
226
-
227
- .card-title {
228
- font-size: 0.9rem;
229
- font-weight: 700;
230
- color: var(--primary);
231
- text-transform: uppercase;
232
- letter-spacing: 0.05em;
233
- }
234
-
235
- .card-content {
236
- background-color: rgba(0, 0, 0, 0.2);
237
- padding: 1rem;
238
- border-radius: 8px;
239
- font-family: 'Courier New', Courier, monospace;
240
- font-size: 0.85rem;
241
- color: #e2e8f0;
242
- white-space: pre-wrap;
243
- word-break: break-word;
244
- max-height: 300px;
245
- overflow-y: auto;
246
- border: 1px solid rgba(255,255,255,0.05);
247
- }
248
-
249
- .card-actions {
250
- margin-top: 1rem;
251
- display: flex;
252
- justify-content: flex-end;
253
- }
254
-
255
- .btn-copy {
256
- background-color: transparent;
257
- border: 1px solid var(--border);
258
- color: var(--text-muted);
259
- padding: 0.4rem 0.8rem;
260
- border-radius: 6px;
261
- font-size: 0.8rem;
262
- cursor: pointer;
263
- transition: all 0.2s;
264
- display: flex;
265
- align-items: center;
266
- gap: 0.4rem;
267
- }
268
-
269
- .btn-copy:hover {
270
- background-color: var(--bg-input);
271
- color: var(--text-main);
272
- border-color: var(--text-muted);
273
- }
274
-
275
- /* Empty State */
276
- .empty-state {
277
- text-align: center;
278
- padding: 4rem 2rem;
279
- color: var(--text-muted);
280
- border: 2px dashed var(--border);
281
- border-radius: var(--radius);
282
- }
283
-
284
- .empty-icon {
285
- font-size: 3rem;
286
- margin-bottom: 1rem;
287
- opacity: 0.5;
288
- }
289
-
290
- /* Toast Notification */
291
- .toast {
292
- position: fixed;
293
- bottom: 2rem;
294
- right: 2rem;
295
- background-color: var(--accent);
296
- color: #fff;
297
- padding: 0.75rem 1.5rem;
298
- border-radius: 8px;
299
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
300
- transform: translateY(150%);
301
- transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
302
- z-index: 1000;
303
- font-weight: 500;
304
- }
305
-
306
- .toast.show {
307
- transform: translateY(0);
308
- }
309
-
310
- /* Responsive */
311
- @media (max-width: 1024px) {
312
- main {
313
- grid-template-columns: 1fr;
314
- }
315
-
316
- aside {
317
- position: static;
318
- margin-bottom: 2rem;
319
- }
320
- }
321
-
322
- @media (max-width: 600px) {
323
- .grid {
324
- grid-template-columns: 1fr;
325
- }
326
-
327
- header {
328
- padding: 1rem;
329
- }
330
-
331
- main {
332
- padding: 1rem;
333
- }
334
- }
335
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  </head>
337
- <body>
338
-
339
- <header>
340
- <div class="brand">PromptGen AI</div>
341
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with anycoder</a>
342
- </header>
343
-
344
- <main>
345
- <!-- Input Section -->
346
- <aside>
347
- <h2>
348
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
349
- Input Data
350
- </h2>
351
-
352
- <div class="form-group">
353
- <label for="productName">Product Name</label>
354
- <textarea id="productName" placeholder="e.g. Bermuda Viena Feminina em Algodão com Brinde"></textarea>
355
- <p class="helper-text">Include material, gender, and quantity hints.</p>
356
- </div>
357
-
358
- <div class="form-group">
359
- <label for="colorList">Color List</label>
360
- <input type="text" id="colorList" placeholder="e.g. Green, Khaki, Beige, White">
361
- <p class="helper-text">Separate colors with commas.</p>
362
- </div>
363
-
364
- <button class="btn-primary" id="generateBtn">Generate Prompts</button>
365
- </aside>
366
-
367
- <!-- Output Section -->
368
- <section class="output-container">
369
- <div class="results-header">
370
- <h2>Generated Templates</h2>
371
- <span class="status-badge" id="statusBadge">Waiting for input...</span>
372
- </div>
373
-
374
- <div id="resultsGrid" class="grid">
375
- <div class="empty-state" style="grid-column: 1 / -1;">
376
- <div class="empty-icon">⚡</div>
377
- <h3>Ready to Generate</h3>
378
- <p>Enter the product details and click the generate button to create your prompt set.</p>
379
- </div>
380
- </div>
381
- </section>
382
- </main>
383
-
384
- <div id="toast" class="toast">Copied to clipboard!</div>
385
-
386
- <script>
387
- // --- Constants & Templates ---
388
- const TEMPLATES = [
389
- {
390
- title: "DETALHE DO PRODUTO",
391
- content: `STRICT VISUAL MATCH TAKEN WITH IPHONE. 1:1 Square format. The subject is the EXACT "Bermuda Viena Feminina" in Linen and Cotton. The design must be IDENTICAL to the reference: high-waisted structure with precise vertical front seams (nervuras). A feminine hand is gently pinching the fabric on the front seam to display the exact ribbing detail and texture. Extreme close-up focus on the material weave and seam construction. Natural sunlight, casual Brazilian atmosphere, amateur raw photo style, beige color. DO NOT ALTER THE SILHOUETTE OR DETAILS. --ar 1:1 --style raw --v 6.0`
392
- },
393
- {
394
- title: "DETALHE DO PRODUTO 2",
395
- content: `Commercial macro photography, extreme close-up focus on fabric texture, women's high-waisted bermuda shorts made of premium linen and cotton blend, detailed visible front ribs/nervuras, natural beige color, soft diffused studio lighting to enhance fabric grain, tactile surface quality, shallow depth of field with bokeh neutral background, ultra-realistic, 8k resolution, shot on Sony A7R IV, 100mm macro lens, cinematic style, luxury e-commerce aesthetic, visible fiber details, no text, no watermark. --ar 4:5 --style raw --v 6.0`
396
- },
397
- {
398
- title: "PESSOA USANDO",
399
- content: `Square 1:1 photo taken on an iPhone, candid street photography style. A female model stands on a bright Brazilian street, neck down, wearing shorts that are an exact visual match to the reference image: High-waisted 'Bermuda Viena' made of Linen and Cotton. Key details: Prominent vertical front seams/ribs (nervuras frontais), structured high waist, and a textured natural fabric weave that looks like linen. The shorts fit is relaxed but tailored. The color is natural beige. The lighting is harsh natural sunlight typical of Brazil, emphasizing the fabric texture. Background is a blurred concrete wall or sidewalk. The shot is raw and unposed, focused on capturing the precise cut and design of the product.`
400
- },
401
- {
402
- title: "PESSOA USANDO 2",
403
- content: `A raw, candid square photo taken on an iPhone 15 Pro Max. STRICT VISUAL REPLICATION REQUIRED. The subject is wearing the EXACT 'Bermuda Viena Feminina' shorts. MUST FEATURE: High-waisted cut, distinctive vertical front ribs/nervuras seams running down the front, straight leg silhouette made of premium linen and cotton blend. The fabric texture is clearly visible, highlighting the natural earthy beige color and coarse weave. DO NOT ALTER THE DESIGN OR SILHOUETTE. Setting: Vibrant Brazilian urban street, textured white concrete wall, Portuguese stone sidewalk, intense midday sun casting sharp natural shadows. The vibe is effortless, tropical, and relaxed 'bem básico' style. High definition, authentic iPhone camera aesthetic, unposed, street photography style. --ar 1:1 --style raw --v 6.0`
404
- },
405
- {
406
- title: "AVALIAÇÃO ESPELHO",
407
- content: `Authentic Shopee-style customer review photo, raw vertical mirror selfie, 9:18 aspect ratio. A female model is standing in front of a bathroom mirror, framed from the neck up (no face visible, hiding face). She is wearing the EXACT "Bermuda Viena" high-waisted shorts. STRICT VISUAL FIDELITY: The shorts must feature the specific vertical front ribs/nervuras seams, linen and cotton fabric texture, beige color. The setting is a typical Brazilian home bathroom with natural ambient light. The image must look like a real amateur photo taken by a customer, realistic phone camera grain, showing the fit of the high waist and the straight leg cut. Candid, unposed, honest product demonstration. --ar 9:16 --style raw --v 6.0`
408
- },
409
- {
410
- title: "AVALIAÇÃO CAMA",
411
- content: `Authentic Brazilian Shopee customer review photo, raw vertical mobile shot, 9:16 aspect ratio. THE SCENE: A messy, unorganized pile of exactly 5 pairs of 'Bermuda Viena' shorts thrown casually across a bedroom bed. The shorts are fully unfolded and lying in a heap. COLOR CRITICAL: The pile must display the specific 5 colors from the product listing: Green, Khaki, White, Black, and Bege. STICT FIDELITY: Exact 'Bermuda Viena' model, high-waisted, vertical front ribs/nervuras, linen texture. LOGO: Brand tag visible. VIBE: Honest amateur photo, natural daylight, typical home setting, showing the variety of the purchase. --ar 9:16 --style raw --v 6.0`
412
- },
413
- {
414
- title: "FOTO PRINCIPAL",
415
- content: `Professional 1:1 square e-commerce product photograph featuring a charismatic, confident Brazilian woman standing behind a horizontal clothing rail. She has a warm, sun-kissed Brazilian complexion, natural hair texture, and distinctively Brazilian facial features. Her expression is authentic, engaging, and confident, with realistic human skin texture to avoid an artificial look. The rail displays exactly 5 identical pairs of "Bermuda Viena Feminina" shorts hanging perfectly flat, straight side-by-side, and FULLY UNCROPPED. STRICT DESIGN SPECIFICATIONS: The shorts must be exact replicas: high-waisted structure, distinctive vertical front ribs/nervuras seams, premium linen and cotton blend texture, straight leg cut. LOGO: A visible brand logo is present on the waistband or label of the shorts. COLOR PALETTE: Verde (Green), Cáqui (Khaki), Branco (White), Preto (Black), Beige. COMPOSITION: Hands gripping the rail, clear full view of all 5 shorts in the foreground without obstruction. BACKGROUND: Minimalist white studio backdrop, soft uniform lighting. Hyper-realistic texture focus, commercial catalog quality. --ar 1:1 --v 6.0 --style raw`
416
- },
417
- {
418
- title: "TEXTO FOTO PRINCIPAL",
419
- content: `Ensure the promotional text "Pague 3, Leve 5". Preserve exact original image position. Maintain flat lighting, no visible JPEG artifacts, Ultra-realistic, 8k resolution, photographic clarity, sharp focus, high fidelity textures, crisp details. Simple composition, use of symmetry, shallow depth of field, background blurred, leading lines created by clothes rack, no other objects, minimalist style. Add bold, sharp graphic text overlays seamlessly integrated without altering the photography subject.`
420
- },
421
- {
422
- title: "SOMENTE O PRODUTO (PNG)",
423
- content: `Professional product photography of exactly 5 identical copies of the source image product arranged in a tight horizontal row. COLOR PALETTE EXACT: The five shorts must display the following distinct colors: Green (Verde), Khaki (Cáki), White (Branco), Black (Preto), and Beige. The shorts are suspended in mid-air as if on invisible hangers, maintaining a stiff, perfectly straight structured shape. The legs are pressing tightly against each other with zero space or gaps between the items. NO HARDWARE: Absolutely no visible hangers, hooks, or strings, only the shorts floating. CRITICAL: The texture, fabric, stitching nervuras, and logo must be an exact duplication of the reference image provided. Product photography, pure white background, studio light. --ar 5:1 --style raw --iw 3`
424
- }
425
- ];
426
-
427
- // --- Logic Functions ---
428
-
429
- /**
430
- * Step 1: Analyze Input 1 (INPUT_PRODUCT_NAME)
431
- */
432
- function analyzeProductName(input) {
433
- // 1. PRODUCT_NAME: Clean input
434
- // Remove common promotional suffixes for the core name if needed, but usually, the whole title is fine.
435
- // The prompt implies extracting the core name. Let's clean trailing/leading whitespace.
436
- const productName = input.trim();
437
-
438
- // 2. MATERIAL: Identify keywords and translate
439
- const materialMap = {
440
- 'algodão': 'Cotton',
441
- 'linho': 'Linen',
442
- 'poliéster': 'Polyester',
443
- 'seda': 'Silk',
444
- 'jeans': 'Denim',
445
- 'denim': 'Denim'
446
- };
447
-
448
- let material = 'Cotton'; // Default fallback or original if not found
449
- const lowerInput = input.toLowerCase();
450
-
451
- for (const [key, value] of Object.entries(materialMap)) {
452
- if (lowerInput.includes(key)) {
453
- material = value;
454
- break;
455
- }
456
- }
457
-
458
- // Construct blend string (e.g., "Linen and Cotton")
459
- // Assuming simple detection. If multiple found, join them.
460
- // For this specific task, the template asks for "Linen and Cotton".
461
- // We will return the translated material. If "Algodão" is found -> "Cotton".
462
- // The template has "Linen and Cotton". We will replace that specific string.
463
- // If only one material is found, we might need to decide if we keep "and Cotton" or just use the found material.
464
- // Constraint: "Replace 'Linen and Cotton' ... with {MATERIAL}".
465
- // So if user enters "Bermuda de Seda", MATERIAL becomes "Silk".
466
-
467
- // 3. TARGET_AUDIENCE
468
- let gender = 'Female'; // Default
469
- if (/masculina|masculino|homem|menino/i.test(input)) {
470
- gender = 'Male';
471
- }
472
-
473
- // 4. GIVEAWAYS
474
- const hasGiveaways = /brinde|bônus|presente|kit/i.test(input);
475
-
476
- // 5. TOTAL_QUANTITY
477
- let quantity = 0;
478
- const explicitNumberMatch = input.match(/(?:pack|pack de|conjunto|kit)?\s*(\d+)\s*(?:unidades|pares|pcs|units)?/i);
479
-
480
- if (explicitNumberMatch) {
481
- quantity = parseInt(explicitNumberMatch[1], 10);
482
- }
483
- // If no explicit number, we rely on the color list length later.
484
- // But we can store the logic here.
485
-
486
- // Extract "Pay X Take Y" text for the overlay
487
- // Pattern: "Pague X Leve Y"
488
- const promoMatch = input.match(/pague\s*\d+\s*leve\s*\d+/i);
489
- const promoText = promoMatch ? promoMatch[0] : null;
490
-
491
- return {
492
- productName,
493
- material,
494
- gender,
495
- hasGiveaways,
496
- quantity, // explicit number found
497
- promoText
498
- };
499
- }
500
-
501
- /**
502
- * Step 2: Analyze Input 2 (INPUT_COLORS)
503
- */
504
- function analyzeColors(input) {
505
- if (!input) return { list: [], first: '', formattedList: '' };
506
-
507
- const colors = input.split(',').map(c => c.trim()).filter(c => c.length > 0);
508
-
509
- // Format list: "Green, Khaki, White, Black, and Bege"
510
- let formattedList = '';
511
- if (colors.length === 1) {
512
- formattedList = colors[0];
513
- } else if (colors.length === 2) {
514
- formattedList = `${colors[0]} and ${colors[1]}`;
515
- } else {
516
- const last = colors[colors.length - 1];
517
- const rest = colors.slice(0, -1).join(', ');
518
- formattedList = `${rest}, and ${last}`;
519
- }
520
-
521
- return {
522
- list: colors,
523
- first: colors[0] || '',
524
- formattedList
525
- };
526
- }
527
-
528
- /**
529
- * Step 3: Substitution Execution & Step 4: Output Generation
530
- */
531
- function generatePrompts(productData, colorData) {
532
- // Determine final quantity
533
- let totalQuantity = productData.quantity;
534
- if (totalQuantity === 0) {
535
- totalQuantity = colorData.list.length > 0 ? colorData.list.length : 1;
536
- }
537
-
538
- // Quantity string replacements
539
- const quantityString = totalQuantity.toString();
540
- const pluralPairs = `${totalQuantity} pairs`;
541
- const pluralCopies = `${totalQuantity} identical copies`;
542
-
543
- const results = TEMPLATES.map(template => {
544
- let text = template.content;
545
-
546
- // 1. Replace Product Name
547
- // Target: "Bermuda Viena Feminina"
548
- text = text.replace(/Bermuda Viena Feminina/g, productData.productName);
549
- text = text.replace(/'Bermuda Viena'/g, `'${productData.productName}'`); // Handle quotes in some templates
550
- text = text.replace(/Bermuda Viena/g, productData.productName); // Fallback for partial matches
551
-
552
- // 2. Replace Material
553
- // Targets: "Linen and Cotton", "linen and cotton blend", "linen and cotton fabric"
554
- // Strategy: Replace the main phrase "Linen and Cotton" and "linen and cotton"
555
- // If the productData.material is just one word (e.g. "Silk"), replacing "Linen and Cotton" with "Silk" works.
556
-
557
- const matRegex = /linen and cotton/gi;
558
- text = text.replace(matRegex, productData.material);
559
-
560
- // Handle "linen texture" or specific fabric mentions if strictly necessary,
561
- // but the prompt specifically asked to replace "Linen and Cotton" (and variants like "linen and cotton blend").
562
- const blendRegex = /linen and cotton blend/gi;
563
- text = text.replace(blendRegex, `${productData.material} blend`);
564
-
565
- // 3. Replace Color List
566
- // Target: "Green, Khaki, White, Black, and Bege"
567
- text = text.replace(/Green, Khaki, White, Black, and Bege/g, colorData.formattedList);
568
-
569
- // Also replace the palette list in "FOTO PRINCIPAL": "Verde (Green), Cáqui (Khaki)..."
570
- // This is harder to match generically, but we can try to replace the English part of that specific string.
571
- // Or just rely on the main replacement. Let's try to be smart about the "COLOR PALETTE" line.
572
- // Template: "Verde (Green), Cáqui (Khaki), Branco (White), Preto (Black), Beige."
573
- // We don't have the Portuguese translation of user colors.
574
- // Constraint: "Zero External Info". We cannot translate "Red" to "Vermelho".
575
- // So we must replace the English list part or the whole line if we can.
576
- // However, the prompt specifically said: "Replace the specific list 'Green, Khaki, White, Black, and Bege' with {COLOR_LIST}".
577
- // This specific string appears in DETALHE DO PRODUTO and PESSOA USANDO 2.
578
- // In FOTO PRINCIPAL, the list is "Verde (Green)...". We will replace the English portion inside it if possible,
579
- // or just leave the non-specified parts as per "Strict Fidelity" to original formatting.
580
- // Let's stick to the explicit replacement requested.
581
-
582
- // 4. Replace Singular Color
583
- // Target: "beige"
584
- // This is tricky because "beige" might be inside "Verde (Green)...".
585
- // We should use word boundaries or context.
586
- // The prompt says: Replace the singular "beige" with {COLOR}.
587
- // We'll replace " natural beige color" -> " natural [Color] color".
588
- text = text.replace(/\bbeige\b/g, colorData.first);
589
-
590
- // 5. Replace Quantity References
591
- // Targets: "5 pairs", "5 identical copies", "Leve 5"
592
- text = text.replace(/5 pairs/g, pluralPairs);
593
- text = text.replace(/5 identical copies/g, pluralCopies);
594
-
595
- // Specific replacement for "TEXTO FOTO PRINCIPAL"
596
- if (template.title === "TEXTO FOTO PRINCIPAL") {
597
- // If we found a specific promo text like "Pague 3 Leve 5"
598
- if (productData.promoText) {
599
- text = text.replace(/"Pague 3, Leve 5"/g, `"${productData.promoText}"`);
600
- text = text.replace(/Pague 3, Leve 5/g, productData.promoText);
601
- } else {
602
- // Fallback if no specific promo text found but we have quantity
603
- // The prompt says: "Replace 'Leve 5' in 'TEXTO FOTO PRINCIPAL' with 'Leve {TOTAL_QUANTITY}'"
604
- text = text.replace(/Leve 5/g, `Leve ${quantityString}`);
605
- // Also update the "Pague X" part? If not found, we might keep it or assume Pague 3.
606
- // Let's update the "Pague 3" part to match the quantity logic if it's a "Pay X Take Y" deal implies quantity Y.
607
- // If user said "Pack de 10", promo might be "Pague 8 Leve 10". We can't invent the '8'.
608
- // So we only strictly follow "Replace Leve 5".
609
- }
610
- } else {
611
- // General replacement for other templates
612
- text = text.replace(/Leve 5/g, `Leve ${quantityString}`);
613
- }
614
-
615
- // Replace "exactly 5 pairs"
616
- text = text.replace(/exactly 5 pairs/g, `exactly ${pluralPairs}`);
617
- text = text.replace(/exactly 5 identical/g, `exactly ${totalQuantity} identical`);
618
-
619
- // 6. Gender Adaptation
620
- if (productData.gender === 'Male') {
621
- text = text.replace(/female model/g, 'male model');
622
- text = text.replace(/women's/g, "men's");
623
- text = text.replace(/\bwomen\b/g, 'men'); // careful with "women's" already handled
624
- text = text.replace(/\bwoman\b/g, 'man');
625
- text = text.replace(/feminine hand/g, 'masculine hand');
626
- text = text.replace(/Brazilian woman/g, 'Brazilian man');
627
- text = text.replace(/A female model/g, 'A male model');
628
- }
629
-
630
- return {
631
- title: template.title,
632
- content: text
633
- };
634
- });
635
-
636
- return results;
637
- }
638
 
639
- // --- UI Interactions ---
640
-
641
- const generateBtn = document.getElementById('generateBtn');
642
- const productNameInput = document.getElementById('productName');
643
- const colorListInput = document.getElementById('colorList');
644
- const resultsGrid = document.getElementById('resultsGrid');
645
- const statusBadge = document.getElementById('statusBadge');
646
- const toast = document.getElementById('toast');
647
-
648
- function showToast(message) {
649
- toast.textContent = message;
650
- toast.classList.add('show');
651
- setTimeout(() => {
652
- toast.classList.remove('show');
653
- }, 3000);
654
- }
655
-
656
- function copyToClipboard(text) {
657
- navigator.clipboard.writeText(text).then(() => {
658
- showToast('Prompt copied to clipboard!');
659
- }).catch(err => {
660
- showToast('Failed to copy');
661
- });
662
- }
663
 
664
- function renderResults(results) {
665
- resultsGrid.innerHTML = '';
666
-
667
- if (results.length === 0) {
668
- resultsGrid.innerHTML = `<div class="empty-state" style="grid-column: 1 / -1;"><p>No results generated.</p></div>`;
669
- return;
670
- }
671
-
672
- results.forEach((item, index) => {
673
- const card = document.createElement('div');
674
- card.className = 'card';
675
- card.style.animation = `fadeIn 0.5s ease forwards ${index * 0.1}s`;
676
- card.style.opacity = '0'; // For animation
677
-
678
- const header = document.createElement('div');
679
- header.className = 'card-header';
680
-
681
- const title = document.createElement('div');
682
- title.className = 'card-title';
683
- title.textContent = item.title;
684
-
685
- header.appendChild(title);
686
-
687
- const content = document.createElement('div');
688
- content.className = 'card-content';
689
- content.textContent = item.content;
690
-
691
- const actions = document.createElement('div');
692
- actions.className = 'card-actions';
693
-
694
- const copyBtn = document.createElement('button');
695
- copyBtn.className = 'btn-copy';
696
- copyBtn.innerHTML = `
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
698
- Copy
699
  `;
700
- copyBtn.onclick = () => copyToClipboard(item.content);
701
-
702
- actions.appendChild(copyBtn);
703
- card.appendChild(header);
704
- card.appendChild(content);
705
- card.appendChild(actions);
706
-
707
- resultsGrid.appendChild(card);
708
- });
709
- }
710
-
711
- generateBtn.addEventListener('click', () => {
712
- const pName = productNameInput.value;
713
- const cList = colorListInput.value;
714
-
715
- if (!pName || !cList) {
716
- showToast('Please fill in both fields.');
717
- return;
718
- }
719
-
720
- // Generate
721
- const pData = analyzeProductName(pName);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <!DOCTYPE html>
2
+ <html lang="pt-BR">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>AI Prompt Generator | Variable Injection Specialist</title>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
11
+ <style>
12
+ :root {
13
+ --bg-body: #0f172a;
14
+ --bg-card: #1e293b;
15
+ --bg-input: #334155;
16
+ --text-main: #f8fafc;
17
+ --text-muted: #94a3b8;
18
+ --primary: #3b82f6;
19
+ --primary-hover: #2563eb;
20
+ --accent: #10b981;
21
+ --border: #334155;
22
+ --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
23
+ --radius: 12px;
24
+ }
25
+
26
+ * {
27
+ box-sizing: border-box;
28
+ margin: 0;
29
+ padding: 0;
30
+ }
31
+
32
+ body {
33
+ font-family: 'Inter', sans-serif;
34
+ background-color: var(--bg-body);
35
+ color: var(--text-main);
36
+ line-height: 1.6;
37
+ min-height: 100vh;
38
+ display: flex;
39
+ flex-direction: column;
40
+ }
41
+
42
+ /* Header */
43
+ header {
44
+ background-color: var(--bg-card);
45
+ border-bottom: 1px solid var(--border);
46
+ padding: 1rem 2rem;
47
+ display: flex;
48
+ justify-content: space-between;
49
+ align-items: center;
50
+ position: sticky;
51
+ top: 0;
52
+ z-index: 100;
53
+ }
54
+
55
+ .brand {
56
+ font-size: 1.25rem;
57
+ font-weight: 700;
58
+ background: linear-gradient(to right, var(--primary), var(--accent));
59
+ -webkit-background-clip: text;
60
+ -webkit-text-fill-color: transparent;
61
+ letter-spacing: -0.025em;
62
+ }
63
+
64
+ .anycoder-link {
65
+ font-size: 0.875rem;
66
+ color: var(--text-muted);
67
+ text-decoration: none;
68
+ transition: color 0.2s;
69
+ border: 1px solid var(--border);
70
+ padding: 0.25rem 0.75rem;
71
+ border-radius: 99px;
72
+ }
73
+
74
+ .anycoder-link:hover {
75
+ color: var(--primary);
76
+ border-color: var(--primary);
77
+ }
78
+
79
+ /* Main Layout */
80
+ main {
81
+ flex: 1;
82
+ max-width: 1400px;
83
+ margin: 0 auto;
84
+ width: 100%;
85
+ padding: 2rem;
86
+ display: grid;
87
+ grid-template-columns: 350px 1fr;
88
+ gap: 2rem;
89
+ }
90
+
91
+ /* Sidebar / Inputs */
92
+ aside {
93
+ background-color: var(--bg-card);
94
+ border-radius: var(--radius);
95
+ padding: 1.5rem;
96
+ border: 1px solid var(--border);
97
+ height: fit-content;
98
+ position: sticky;
99
+ top: 6rem;
100
+ box-shadow: var(--shadow);
101
+ }
102
+
103
+ h2 {
104
+ font-size: 1.1rem;
105
+ margin-bottom: 1.5rem;
106
+ color: var(--text-main);
107
+ font-weight: 600;
108
+ display: flex;
109
+ align-items: center;
110
+ gap: 0.5rem;
111
+ }
112
+
113
+ .form-group {
114
+ margin-bottom: 1.5rem;
115
+ }
116
+
117
+ label {
118
+ display: block;
119
+ font-size: 0.875rem;
120
+ color: var(--text-muted);
121
+ margin-bottom: 0.5rem;
122
+ font-weight: 500;
123
+ }
124
+
125
+ input,
126
+ textarea {
127
+ width: 100%;
128
+ background-color: var(--bg-input);
129
+ border: 1px solid var(--border);
130
+ border-radius: 8px;
131
+ padding: 0.75rem 1rem;
132
+ color: var(--text-main);
133
+ font-family: inherit;
134
+ font-size: 0.95rem;
135
+ transition: all 0.2s ease;
136
+ }
137
+
138
+ textarea {
139
+ resize: vertical;
140
+ min-height: 100px;
141
+ }
142
+
143
+ input:focus,
144
+ textarea:focus {
145
+ outline: none;
146
+ border-color: var(--primary);
147
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
148
+ }
149
+
150
+ .helper-text {
151
+ font-size: 0.75rem;
152
+ color: var(--text-muted);
153
+ margin-top: 0.5rem;
154
+ font-style: italic;
155
+ }
156
+
157
+ button.btn-primary {
158
+ width: 100%;
159
+ padding: 0.875rem;
160
+ background-color: var(--primary);
161
+ color: white;
162
+ border: none;
163
+ border-radius: 8px;
164
+ font-weight: 600;
165
+ font-size: 1rem;
166
+ cursor: pointer;
167
+ transition: background-color 0.2s, transform 0.1s;
168
+ }
169
+
170
+ button.btn-primary:hover {
171
+ background-color: var(--primary-hover);
172
+ }
173
+
174
+ button.btn-primary:active {
175
+ transform: scale(0.98);
176
+ }
177
+
178
+ /* Output Section */
179
+ .output-container {
180
+ display: flex;
181
+ flex-direction: column;
182
+ gap: 2rem;
183
+ }
184
+
185
+ .results-header {
186
+ display: flex;
187
+ justify-content: space-between;
188
+ align-items: center;
189
+ }
190
+
191
+ .status-badge {
192
+ font-size: 0.875rem;
193
+ padding: 0.25rem 0.75rem;
194
+ border-radius: 99px;
195
+ background-color: var(--bg-input);
196
+ color: var(--text-muted);
197
+ }
198
+
199
+ .grid {
200
+ display: grid;
201
+ grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
202
+ gap: 1.5rem;
203
+ }
204
+
205
+ .card {
206
+ background-color: var(--bg-card);
207
+ border: 1px solid var(--border);
208
+ border-radius: var(--radius);
209
+ padding: 1.5rem;
210
+ display: flex;
211
+ flex-direction: column;
212
+ transition: transform 0.2s, box-shadow 0.2s;
213
+ position: relative;
214
+ overflow: hidden;
215
+ }
216
+
217
+ .card:hover {
218
+ transform: translateY(-2px);
219
+ box-shadow: var(--shadow);
220
+ border-color: var(--primary);
221
+ }
222
+
223
+ .card-header {
224
+ display: flex;
225
+ justify-content: space-between;
226
+ align-items: flex-start;
227
+ margin-bottom: 1rem;
228
+ }
229
+
230
+ .card-title {
231
+ font-size: 0.9rem;
232
+ font-weight: 700;
233
+ color: var(--primary);
234
+ text-transform: uppercase;
235
+ letter-spacing: 0.05em;
236
+ }
237
+
238
+ .card-content {
239
+ background-color: rgba(0, 0, 0, 0.2);
240
+ padding: 1rem;
241
+ border-radius: 8px;
242
+ font-family: 'Courier New', Courier, monospace;
243
+ font-size: 0.85rem;
244
+ color: #e2e8f0;
245
+ white-space: pre-wrap;
246
+ word-break: break-word;
247
+ max-height: 300px;
248
+ overflow-y: auto;
249
+ border: 1px solid rgba(255, 255, 255, 0.05);
250
+ }
251
+
252
+ .card-actions {
253
+ margin-top: 1rem;
254
+ display: flex;
255
+ justify-content: flex-end;
256
+ }
257
+
258
+ .btn-copy {
259
+ background-color: transparent;
260
+ border: 1px solid var(--border);
261
+ color: var(--text-muted);
262
+ padding: 0.4rem 0.8rem;
263
+ border-radius: 6px;
264
+ font-size: 0.8rem;
265
+ cursor: pointer;
266
+ transition: all 0.2s;
267
+ display: flex;
268
+ align-items: center;
269
+ gap: 0.4rem;
270
+ }
271
+
272
+ .btn-copy:hover {
273
+ background-color: var(--bg-input);
274
+ color: var(--text-main);
275
+ border-color: var(--text-muted);
276
+ }
277
+
278
+ /* Empty State */
279
+ .empty-state {
280
+ text-align: center;
281
+ padding: 4rem 2rem;
282
+ color: var(--text-muted);
283
+ border: 2px dashed var(--border);
284
+ border-radius: var(--radius);
285
+ }
286
+
287
+ .empty-icon {
288
+ font-size: 3rem;
289
+ margin-bottom: 1rem;
290
+ opacity: 0.5;
291
+ }
292
+
293
+ /* Toast Notification */
294
+ .toast {
295
+ position: fixed;
296
+ bottom: 2rem;
297
+ right: 2rem;
298
+ background-color: var(--accent);
299
+ color: #fff;
300
+ padding: 0.75rem 1.5rem;
301
+ border-radius: 8px;
302
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
303
+ transform: translateY(150%);
304
+ transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
305
+ z-index: 1000;
306
+ font-weight: 500;
307
+ }
308
+
309
+ .toast.show {
310
+ transform: translateY(0);
311
+ }
312
+
313
+ /* Animation */
314
+ @keyframes fadeIn {
315
+ from {
316
+ opacity: 0;
317
+ transform: translateY(10px);
318
+ }
319
+ to {
320
+ opacity: 1;
321
+ transform: translateY(0);
322
+ }
323
+ }
324
+
325
+ /* Responsive */
326
+ @media (max-width: 1024px) {
327
+ main {
328
+ grid-template-columns: 1fr;
329
+ }
330
+
331
+ aside {
332
+ position: static;
333
+ margin-bottom: 2rem;
334
+ }
335
+ }
336
+
337
+ @media (max-width: 600px) {
338
+ .grid {
339
+ grid-template-columns: 1fr;
340
+ }
341
+
342
+ header {
343
+ padding: 1rem;
344
+ }
345
+
346
+ main {
347
+ padding: 1rem;
348
+ }
349
+ }
350
+ </style>
351
  </head>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
 
353
+ <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
 
355
+ <header>
356
+ <div class="brand">PromptGen AI</div>
357
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with
358
+ anycoder</a>
359
+ </header>
360
+
361
+ <main>
362
+ <!-- Input Section -->
363
+ <aside>
364
+ <h2>
365
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
366
+ stroke-linecap="round" stroke-linejoin="round">
367
+ <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
368
+ <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
369
+ </svg>
370
+ Dados de Entrada
371
+ </h2>
372
+
373
+ <div class="form-group">
374
+ <label for="productName">Nome do Produto</label>
375
+ <textarea id="productName" placeholder="e.g. Bermuda Viena Feminina em Algodão com Brinde"></textarea>
376
+ <p class="helper-text">Inclua material, gênero e dicas de quantidade.</p>
377
+ </div>
378
+
379
+ <div class="form-group">
380
+ <label for="colorList">Lista de Cores</label>
381
+ <input type="text" id="colorList" placeholder="e.g. Green, Khaki, Beige, White">
382
+ <p class="helper-text">Separe as cores com vírgulas.</p>
383
+ </div>
384
+
385
+ <button class="btn-primary" id="generateBtn">Gerar Prompts</button>
386
+ </aside>
387
+
388
+ <!-- Output Section -->
389
+ <section class="output-container">
390
+ <div class="results-header">
391
+ <h2>Templates Gerados</h2>
392
+ <span class="status-badge" id="statusBadge">Aguardando entrada...</span>
393
+ </div>
394
+
395
+ <div id="resultsGrid" class="grid">
396
+ <div class="empty-state" style="grid-column: 1 / -1;">
397
+ <div class="empty-icon">⚡</div>
398
+ <h3>Pronto para Gerar</h3>
399
+ <p>Insira os detalhes do produto e clique no botão para criar seu conjunto de prompts.</p>
400
+ </div>
401
+ </div>
402
+ </section>
403
+ </main>
404
+
405
+ <div id="toast" class="toast">Copiado para a área de transferência!</div>
406
+
407
+ <script>
408
+ // --- Constants & Templates ---
409
+ const TEMPLATES = [
410
+ {
411
+ title: "DETALHE DO PRODUTO",
412
+ content: `STRICT VISUAL MATCH TAKEN WITH IPHONE. 1:1 Square format. The subject is the EXACT "Bermuda Viena Feminina" in Linen and Cotton. The design must be IDENTICAL to the reference: high-waisted structure with precise vertical front seams (nervuras). A feminine hand is gently pinching the fabric on the front seam to display the exact ribbing detail and texture. Extreme close-up focus on the material weave and seam construction. Natural sunlight, casual Brazilian atmosphere, amateur raw photo style, beige color. DO NOT ALTER THE SILHOUETTE OR DETAILS. --ar 1:1 --style raw --v 6.0`
413
+ },
414
+ {
415
+ title: "DETALHE DO PRODUTO 2",
416
+ content: `Commercial macro photography, extreme close-up focus on fabric texture, women's high-waisted bermuda shorts made of premium linen and cotton blend, detailed visible front ribs/nervuras, natural beige color, soft diffused studio lighting to enhance fabric grain, tactile surface quality, shallow depth of field with bokeh neutral background, ultra-realistic, 8k resolution, shot on Sony A7R IV, 100mm macro lens, cinematic style, luxury e-commerce aesthetic, visible fiber details, no text, no watermark. --ar 4:5 --style raw --v 6.0`
417
+ },
418
+ {
419
+ title: "PESSOA USANDO",
420
+ content: `Square 1:1 photo taken on an iPhone, candid street photography style. A female model stands on a bright Brazilian street, neck down, wearing shorts that are an exact visual match to the reference image: High-waisted 'Bermuda Viena' made of Linen and Cotton. Key details: Prominent vertical front seams/ribs (nervuras frontais), structured high waist, and a textured natural fabric weave that looks like linen. The shorts fit is relaxed but tailored. The color is natural beige. The lighting is harsh natural sunlight typical of Brazil, emphasizing the fabric texture. Background is a blurred concrete wall or sidewalk. The shot is raw and unposed, focused on capturing the precise cut and design of the product.`
421
+ },
422
+ {
423
+ title: "PESSOA USANDO 2",
424
+ content: `A raw, candid square photo taken on an iPhone 15 Pro Max. STRICT VISUAL REPLICATION REQUIRED. The subject is wearing the EXACT 'Bermuda Viena Feminina' shorts. MUST FEATURE: High-waisted cut, distinctive vertical front ribs/nervuras seams running down the front, straight leg silhouette made of premium linen and cotton blend. The fabric texture is clearly visible, highlighting the natural earthy beige color and coarse weave. DO NOT ALTER THE DESIGN OR SILHOUETTE. Setting: Vibrant Brazilian urban street, textured white concrete wall, Portuguese stone sidewalk, intense midday sun casting sharp natural shadows. The vibe is effortless, tropical, and relaxed 'bem básico' style. High definition, authentic iPhone camera aesthetic, unposed, street photography style. --ar 1:1 --style raw --v 6.0`
425
+ },
426
+ {
427
+ title: "AVALIAÇÃO ESPELHO",
428
+ content: `Authentic Shopee-style customer review photo, raw vertical mirror selfie, 9:18 aspect ratio. A female model is standing in front of a bathroom mirror, framed from the neck up (no face visible, hiding face). She is wearing the EXACT "Bermuda Viena" high-waisted shorts. STRICT VISUAL FIDELITY: The shorts must feature the specific vertical front ribs/nervuras seams, linen and cotton fabric texture, beige color. The setting is a typical Brazilian home bathroom with natural ambient light. The image must look like a real amateur photo taken by a customer, realistic phone camera grain, showing the fit of the high waist and the straight leg cut. Candid, unposed, honest product demonstration. --ar 9:16 --style raw --v 6.0`
429
+ },
430
+ {
431
+ title: "AVALIAÇÃO CAMA",
432
+ content: `Authentic Brazilian Shopee customer review photo, raw vertical mobile shot, 9:16 aspect ratio. THE SCENE: A messy, unorganized pile of exactly 5 pairs of 'Bermuda Viena' shorts thrown casually across a bedroom bed. The shorts are fully unfolded and lying in a heap. COLOR CRITICAL: The pile must display the specific 5 colors from the product listing: Green, Khaki, White, Black, and Bege. STICT FIDELITY: Exact 'Bermuda Viena' model, high-waisted, vertical front ribs/nervuras, linen texture. LOGO: Brand tag visible. VIBE: Honest amateur photo, natural daylight, typical home setting, showing the variety of the purchase. --ar 9:16 --style raw --v 6.0`
433
+ },
434
+ {
435
+ title: "FOTO PRINCIPAL",
436
+ content: `Professional 1:1 square e-commerce product photograph featuring a charismatic, confident Brazilian woman standing behind a horizontal clothing rail. She has a warm, sun-kissed Brazilian complexion, natural hair texture, and distinctively Brazilian facial features. Her expression is authentic, engaging, and confident, with realistic human skin texture to avoid an artificial look. The rail displays exactly 5 identical pairs of "Bermuda Viena Feminina" shorts hanging perfectly flat, straight side-by-side, and FULLY UNCROPPED. STRICT DESIGN SPECIFICATIONS: The shorts must be exact replicas: high-waisted structure, distinctive vertical front ribs/nervuras seams, premium linen and cotton blend texture, straight leg cut. LOGO: A visible brand logo is present on the waistband or label of the shorts. COLOR PALETTE: Verde (Green), Cáqui (Khaki), Branco (White), Preto (Black), Beige. COMPOSITION: Hands gripping the rail, clear full view of all 5 shorts in the foreground without obstruction. BACKGROUND: Minimalist white studio backdrop, soft uniform lighting. Hyper-realistic texture focus, commercial catalog quality. --ar 1:1 --v 6.0 --style raw`
437
+ },
438
+ {
439
+ title: "TEXTO FOTO PRINCIPAL",
440
+ content: `Ensure the promotional text "Pague 3, Leve 5". Preserve exact original image position. Maintain flat lighting, no visible JPEG artifacts, Ultra-realistic, 8k resolution, photographic clarity, sharp focus, high fidelity textures, crisp details. Simple composition, use of symmetry, shallow depth of field, background blurred, leading lines created by clothes rack, no other objects, minimalist style. Add bold, sharp graphic text overlays seamlessly integrated without altering the photography subject.`
441
+ },
442
+ {
443
+ title: "SOMENTE O PRODUTO (PNG)",
444
+ content: `Professional product photography of exactly 5 identical copies of the source image product arranged in a tight horizontal row. COLOR PALETTE EXACT: The five shorts must display the following distinct colors: Green (Verde), Khaki (Cáki), White (Branco), Black (Preto), and Beige. The shorts are suspended in mid-air as if on invisible hangers, maintaining a stiff, perfectly straight structured shape. The legs are pressing tightly against each other with zero space or gaps between the items. NO HARDWARE: Absolutely no visible hangers, hooks, or strings, only the shorts floating. CRITICAL: The texture, fabric, stitching nervuras, and logo must be an exact duplication of the reference image provided. Product photography, pure white background, studio light. --ar 5:1 --style raw --iw 3`
445
+ }
446
+ ];
447
+
448
+ // --- Logic Functions ---
449
+
450
+ /**
451
+ * Step 1: Analyze Input 1 (INPUT_PRODUCT_NAME)
452
+ */
453
+ function analyzeProductName(input) {
454
+ // 1. PRODUCT_NAME: Clean input
455
+ const productName = input.trim();
456
+
457
+ // 2. MATERIAL: Identify keywords and translate
458
+ const materialMap = {
459
+ 'algodão': 'Cotton',
460
+ 'linho': 'Linen',
461
+ 'poliéster': 'Polyester',
462
+ 'seda': 'Silk',
463
+ 'jeans': 'Denim',
464
+ 'denim': 'Denim'
465
+ };
466
+
467
+ let material = 'Cotton'; // Default fallback or original if not found
468
+ const lowerInput = input.toLowerCase();
469
+
470
+ for (const [key, value] of Object.entries(materialMap)) {
471
+ if (lowerInput.includes(key)) {
472
+ material = value;
473
+ break;
474
+ }
475
+ }
476
+
477
+ // 3. TARGET_AUDIENCE
478
+ let gender = 'Female'; // Default
479
+ if (/masculina|masculino|homem|menino/i.test(input)) {
480
+ gender = 'Male';
481
+ }
482
+
483
+ // 4. GIVEAWAYS
484
+ const hasGiveaways = /brinde|bônus|presente|kit/i.test(input);
485
+
486
+ // 5. TOTAL_QUANTITY
487
+ let quantity = 0;
488
+ const explicitNumberMatch = input.match(/(?:pack|pack de|conjunto|kit)?\s*(\d+)\s*(?:unidades|pares|pcs|units)?/i);
489
+
490
+ if (explicitNumberMatch) {
491
+ quantity = parseInt(explicitNumberMatch[1], 10);
492
+ }
493
+
494
+ // Extract "Pay X Take Y" text for the overlay
495
+ const promoMatch = input.match(/pague\s*\d+\s*leve\s*\d+/i);
496
+ const promoText = promoMatch ? promoMatch[0] : null;
497
+
498
+ return {
499
+ productName,
500
+ material,
501
+ gender,
502
+ hasGiveaways,
503
+ quantity,
504
+ promoText
505
+ };
506
+ }
507
+
508
+ /**
509
+ * Step 2: Analyze Input 2 (INPUT_COLORS)
510
+ */
511
+ function analyzeColors(input) {
512
+ if (!input) return { list: [], first: '', formattedList: '' };
513
+
514
+ const colors = input.split(',').map(c => c.trim()).filter(c => c.length > 0);
515
+
516
+ // Format list: "Green, Khaki, White, Black, and Bege"
517
+ let formattedList = '';
518
+ if (colors.length === 1) {
519
+ formattedList = colors[0];
520
+ } else if (colors.length === 2) {
521
+ formattedList = `${colors[0]} and ${colors[1]}`;
522
+ } else {
523
+ const last = colors[colors.length - 1];
524
+ const rest = colors.slice(0, -1).join(', ');
525
+ formattedList = `${rest}, and ${last}`;
526
+ }
527
+
528
+ return {
529
+ list: colors,
530
+ first: colors[0] || '',
531
+ formattedList
532
+ };
533
+ }
534
+
535
+ /**
536
+ * Step 3: Substitution Execution & Step 4: Output Generation
537
+ */
538
+ function generatePrompts(productData, colorData) {
539
+ // Determine final quantity
540
+ let totalQuantity = productData.quantity;
541
+ if (totalQuantity === 0) {
542
+ totalQuantity = colorData.list.length > 0 ? colorData.list.length : 1;
543
+ }
544
+
545
+ // Quantity string replacements
546
+ const quantityString = totalQuantity.toString();
547
+ const pluralPairs = `${totalQuantity} pairs`;
548
+ const pluralCopies = `${totalQuantity} identical copies`;
549
+
550
+ const results = TEMPLATES.map(template => {
551
+ let text = template.content;
552
+
553
+ // 1. Replace Product Name
554
+ text = text.replace(/Bermuda Viena Feminina/g, productData.productName);
555
+ text = text.replace(/'Bermuda Viena'/g, `'${productData.productName}'`);
556
+ text = text.replace(/Bermuda Viena/g, productData.productName);
557
+
558
+ // 2. Replace Material
559
+ const matRegex = /linen and cotton/gi;
560
+ text = text.replace(matRegex, productData.material);
561
+
562
+ const blendRegex = /linen and cotton blend/gi;
563
+ text = text.replace(blendRegex, `${productData.material} blend`);
564
+
565
+ // 3. Replace Color List
566
+ text = text.replace(/Green, Khaki, White, Black, and Bege/g, colorData.formattedList);
567
+
568
+ // 4. Replace Singular Color
569
+ text = text.replace(/\bbeige\b/g, colorData.first);
570
+
571
+ // 5. Replace Quantity References
572
+ text = text.replace(/5 pairs/g, pluralPairs);
573
+ text = text.replace(/5 identical copies/g, pluralCopies);
574
+
575
+ if (template.title === "TEXTO FOTO PRINCIPAL") {
576
+ if (productData.promoText) {
577
+ text = text.replace(/"Pague 3, Leve 5"/g, `"${productData.promoText}"`);
578
+ text = text.replace(/Pague 3, Leve 5/g, productData.promoText);
579
+ } else {
580
+ text = text.replace(/Leve 5/g, `Leve ${quantityString}`);
581
+ }
582
+ } else {
583
+ text = text.replace(/Leve 5/g, `Leve ${quantityString}`);
584
+ }
585
+
586
+ text = text.replace(/exactly 5 pairs/g, `exactly ${pluralPairs}`);
587
+ text = text.replace(/exactly 5 identical/g, `exactly ${totalQuantity} identical`);
588
+
589
+ // 6. Gender Adaptation
590
+ if (productData.gender === 'Male') {
591
+ text = text.replace(/female model/g, 'male model');
592
+ text = text.replace(/women's/g, "men's");
593
+ text = text.replace(/\bwomen\b/g, 'men');
594
+ text = text.replace(/\bwoman\b/g, 'man');
595
+ text = text.replace(/feminine hand/g, 'masculine hand');
596
+ text = text.replace(/Brazilian woman/g, 'Brazilian man');
597
+ text = text.replace(/A female model/g, 'A male model');
598
+ }
599
+
600
+ return {
601
+ title: template.title,
602
+ content: text
603
+ };
604
+ });
605
+
606
+ return results;
607
+ }
608
+
609
+ // --- UI Interactions ---
610
+
611
+ const generateBtn = document.getElementById('generateBtn');
612
+ const productNameInput = document.getElementById('productName');
613
+ const colorListInput = document.getElementById('colorList');
614
+ const resultsGrid = document.getElementById('resultsGrid');
615
+ const statusBadge = document.getElementById('statusBadge');
616
+ const toast = document.getElementById('toast');
617
+
618
+ function showToast(message) {
619
+ toast.textContent = message;
620
+ toast.classList.add('show');
621
+ setTimeout(() => {
622
+ toast.classList.remove('show');
623
+ }, 3000);
624
+ }
625
+
626
+ function copyToClipboard(text) {
627
+ navigator.clipboard.writeText(text).then(() => {
628
+ showToast('Copiado para a área de transferência!');
629
+ }).catch(err => {
630
+ showToast('Falha ao copiar');
631
+ });
632
+ }
633
+
634
+ function renderResults(results) {
635
+ resultsGrid.innerHTML = '';
636
+
637
+ if (results.length === 0) {
638
+ resultsGrid.innerHTML = `<div class="empty-state" style="grid-column: 1 / -1;"><p>Nenhum resultado gerado.</p></div>`;
639
+ return;
640
+ }
641
+
642
+ results.forEach((item, index) => {
643
+ const card = document.createElement('div');
644
+ card.className = 'card';
645
+ card.style.animation = `fadeIn 0.5s ease forwards ${index * 0.1}s`;
646
+ card.style.opacity = '0'; // For animation
647
+
648
+ const header = document.createElement('div');
649
+ header.className = 'card-header';
650
+
651
+ const title = document.createElement('div');
652
+ title.className = 'card-title';
653
+ title.textContent = item.title;
654
+
655
+ header.appendChild(title);
656
+
657
+ const content = document.createElement('div');
658
+ content.className = 'card-content';
659
+ content.textContent = item.content;
660
+
661
+ const actions = document.createElement('div');
662
+ actions.className = 'card-actions';
663
+
664
+ const copyBtn = document.createElement('button');
665
+ copyBtn.className = 'btn-copy';
666
+ copyBtn.innerHTML = `
667
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
668
+ Copiar
669
  `;
670
+ copyBtn.onclick = () => copyToClipboard(item.content);
671
+
672
+ actions.appendChild(copyBtn);
673
+ card.appendChild(header);
674
+ card.appendChild(content);
675
+ card.appendChild(actions);
676
+
677
+ resultsGrid.appendChild(card);
678
+ });
679
+ }
680
+
681
+ generateBtn.addEventListener('click', () => {
682
+ const pName = productNameInput.value;
683
+ const cList = colorListInput.value;
684
+
685
+ if (!pName || !cList) {
686
+ showToast('Por favor, preencha ambos os campos.');
687
+ return;
688
+ }
689
+
690
+ // 1. Analyze Data
691
+ const pData = analyzeProductName(pName);
692
+ const cData = analyzeColors(cList);
693
+
694
+ // 2. Generate Prompts
695
+ const results = generatePrompts(pData, cData);
696
+
697
+ // 3. Render Results
698
+ renderResults(results);
699
+
700
+ // 4. Update Status Badge
701
+ statusBadge.textContent = `${results.length} prompts gerados`;
702
+ statusBadge.style.color = 'var(--accent)';
703
+ statusBadge.style.backgroundColor = 'rgba(16, 185, 129, 0.1)';
704
+ });
705
+ </script>
706
+ </body>
707
+ </html>