Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- index.html +114 -260
index.html
CHANGED
|
@@ -1,14 +1,13 @@
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
-
<html lang="
|
| 3 |
|
| 4 |
<head>
|
| 5 |
<meta charset="UTF-8">
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
-
<title>AI Prompt Gen | E-Commerce
|
| 8 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
-
<link
|
| 11 |
-
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
|
| 12 |
rel="stylesheet">
|
| 13 |
<style>
|
| 14 |
:root {
|
|
@@ -90,7 +89,7 @@
|
|
| 90 |
width: 100%;
|
| 91 |
padding: 2rem;
|
| 92 |
display: grid;
|
| 93 |
-
grid-template-columns:
|
| 94 |
gap: 2rem;
|
| 95 |
}
|
| 96 |
|
|
@@ -143,7 +142,7 @@
|
|
| 143 |
|
| 144 |
textarea {
|
| 145 |
resize: vertical;
|
| 146 |
-
min-height:
|
| 147 |
}
|
| 148 |
|
| 149 |
input:focus,
|
|
@@ -185,7 +184,7 @@
|
|
| 185 |
.output-container {
|
| 186 |
display: flex;
|
| 187 |
flex-direction: column;
|
| 188 |
-
gap:
|
| 189 |
}
|
| 190 |
|
| 191 |
.results-header {
|
|
@@ -207,7 +206,7 @@
|
|
| 207 |
|
| 208 |
.grid {
|
| 209 |
display: grid;
|
| 210 |
-
grid-template-columns: repeat(auto-fill, minmax(
|
| 211 |
gap: 1.5rem;
|
| 212 |
}
|
| 213 |
|
|
@@ -408,7 +407,7 @@
|
|
| 408 |
<path d="M12 12l-2-2"></path>
|
| 409 |
<path d="M12 16l-2-2"></path>
|
| 410 |
</svg>
|
| 411 |
-
PromptGen AI
|
| 412 |
</div>
|
| 413 |
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with
|
| 414 |
anycoder</a>
|
|
@@ -423,52 +422,51 @@
|
|
| 423 |
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
| 424 |
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
| 425 |
</svg>
|
| 426 |
-
|
| 427 |
</h2>
|
| 428 |
|
| 429 |
<div class="form-group">
|
| 430 |
-
<label for="productName">
|
| 431 |
-
<textarea id="productName" placeholder="e.g. Bermuda Viena Feminina
|
| 432 |
-
<p class="helper-text">
|
| 433 |
</div>
|
| 434 |
|
| 435 |
<div class="form-group">
|
| 436 |
-
<label for="colorList">
|
| 437 |
<input type="text" id="colorList" placeholder="e.g. Black/Grey/Blue/Green">
|
| 438 |
-
<p class="helper-text">Use
|
| 439 |
</div>
|
| 440 |
|
| 441 |
-
<button class="btn-primary" id="generateBtn">
|
| 442 |
</aside>
|
| 443 |
|
| 444 |
<!-- Output Section -->
|
| 445 |
<section class="output-container">
|
| 446 |
<div class="results-header">
|
| 447 |
-
<h2>
|
| 448 |
-
<span class="status-badge" id="statusBadge">
|
| 449 |
</div>
|
| 450 |
|
| 451 |
<div id="resultsGrid" class="grid">
|
| 452 |
<div class="empty-state">
|
| 453 |
<div class="empty-icon">⚡</div>
|
| 454 |
-
<h3>
|
| 455 |
-
<p>
|
| 456 |
</div>
|
| 457 |
</div>
|
| 458 |
</section>
|
| 459 |
</main>
|
| 460 |
|
| 461 |
-
<div id="toast" class="toast">
|
| 462 |
|
| 463 |
<script>
|
| 464 |
-
// ---
|
| 465 |
-
|
| 466 |
const TEMPLATES = [
|
| 467 |
{
|
| 468 |
id: "detail_1",
|
| 469 |
title: "DETALHE DO PRODUTO",
|
| 470 |
type: "SINGLE",
|
| 471 |
-
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,
|
| 472 |
},
|
| 473 |
{
|
| 474 |
id: "detail_2",
|
|
@@ -492,13 +490,13 @@
|
|
| 492 |
id: "mirror",
|
| 493 |
title: "AVALIAÇÃO ESPELHO",
|
| 494 |
type: "SINGLE",
|
| 495 |
-
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,
|
| 496 |
},
|
| 497 |
{
|
| 498 |
id: "bed",
|
| 499 |
title: "AVALIAÇÃO CAMA",
|
| 500 |
type: "GLOBAL",
|
| 501 |
-
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
|
| 502 |
},
|
| 503 |
{
|
| 504 |
id: "main",
|
|
@@ -520,51 +518,32 @@
|
|
| 520 |
}
|
| 521 |
];
|
| 522 |
|
| 523 |
-
// ---
|
| 524 |
-
|
| 525 |
-
/**
|
| 526 |
-
* Step 1: Advanced Variable Extraction
|
| 527 |
-
*/
|
| 528 |
function extractData(productNameInput, colorsInput) {
|
| 529 |
-
// 1. PRODUCT_NAME: Clean input
|
| 530 |
let rawName = productNameInput.trim();
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
// Extract Promotional Offer (e.g., "Pague 3, Leve 5")
|
| 535 |
const promoRegex = /pague\s*\d+\s*(?:,|e)?\s*leve\s*\d+/i;
|
| 536 |
const promoMatch = rawName.match(promoRegex);
|
|
|
|
| 537 |
|
| 538 |
-
|
| 539 |
-
promoOffer = promoMatch[0];
|
| 540 |
-
// Extract Quantity from offer
|
| 541 |
-
const qtyMatch = promoOffer.match(/leve\s*(\d+)/i);
|
| 542 |
-
if (qtyMatch) {
|
| 543 |
-
totalQuantity = parseInt(qtyMatch[1], 10);
|
| 544 |
-
}
|
| 545 |
-
}
|
| 546 |
-
|
| 547 |
-
// Clean Product Name (Remove promo text)
|
| 548 |
let productName = rawName.replace(promoRegex, '').trim();
|
| 549 |
-
// Remove trailing
|
| 550 |
productName = productName.replace(/,\s*$/, '').trim();
|
| 551 |
|
| 552 |
-
// 2. MATERIAL
|
| 553 |
const materialMap = {
|
| 554 |
-
'algodão': 'Cotton',
|
| 555 |
-
'algodon': 'Cotton',
|
| 556 |
'linho': 'Linen',
|
| 557 |
-
'poliéster': 'Polyester',
|
| 558 |
-
'poliester': 'Polyester',
|
| 559 |
'seda': 'Silk',
|
| 560 |
-
'jeans': 'Denim',
|
| 561 |
-
'denim': 'Denim',
|
| 562 |
'couro': 'Leather'
|
| 563 |
};
|
| 564 |
-
|
| 565 |
let material = 'Cotton'; // Default
|
| 566 |
const lowerInput = rawName.toLowerCase();
|
| 567 |
-
|
| 568 |
for (const [key, value] of Object.entries(materialMap)) {
|
| 569 |
if (lowerInput.includes(key)) {
|
| 570 |
material = value;
|
|
@@ -578,41 +557,31 @@
|
|
| 578 |
gender = 'Male';
|
| 579 |
}
|
| 580 |
|
| 581 |
-
// 4.
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
|
|
|
|
|
|
| 587 |
}
|
| 588 |
|
| 589 |
-
// 6.
|
|
|
|
|
|
|
|
|
|
| 590 |
const hasHighWaisted = /(?:high-waisted|alta cintura|cintura alta)/i.test(rawName);
|
| 591 |
const hasRibs = /(?:nervuras|ribs|costuras)/i.test(rawName);
|
| 592 |
|
| 593 |
-
//
|
| 594 |
-
// Split delimiters (/, ,, or +)
|
| 595 |
const rawColors = colorsInput.split(/[\/,+]/).map(c => c.trim()).filter(c => c.length > 0);
|
| 596 |
|
| 597 |
-
// Color Translation Map
|
| 598 |
const colorMap = {
|
| 599 |
-
'verde': 'Green',
|
| 600 |
-
'
|
| 601 |
-
'
|
| 602 |
-
'
|
| 603 |
-
'laranja': 'Orange',
|
| 604 |
-
'roxo': 'Purple',
|
| 605 |
-
'rosa': 'Pink',
|
| 606 |
-
'marrom': 'Brown',
|
| 607 |
-
'preto': 'Black',
|
| 608 |
-
'branco': 'White',
|
| 609 |
-
'cinza': 'Grey',
|
| 610 |
-
'grey': 'Grey',
|
| 611 |
-
'bege': 'Beige',
|
| 612 |
-
'khaki': 'Khaki',
|
| 613 |
-
'cáqui': 'Khaki',
|
| 614 |
-
'dourado': 'Gold',
|
| 615 |
-
'prata': 'Silver'
|
| 616 |
};
|
| 617 |
|
| 618 |
const colorArray = rawColors.map(c => {
|
|
@@ -620,12 +589,12 @@
|
|
| 620 |
return colorMap[lowerC] || c;
|
| 621 |
});
|
| 622 |
|
| 623 |
-
//
|
| 624 |
if (totalQuantity === 0) {
|
| 625 |
totalQuantity = colorArray.length > 0 ? colorArray.length : 1;
|
| 626 |
}
|
| 627 |
|
| 628 |
-
// Format List String (e.g., "Green, Khaki, White, Black, and Beige")
|
| 629 |
let colorListString = '';
|
| 630 |
if (colorArray.length === 0) {
|
| 631 |
colorListString = 'Assorted Colors';
|
|
@@ -653,202 +622,87 @@
|
|
| 653 |
};
|
| 654 |
}
|
| 655 |
|
| 656 |
-
/
|
| 657 |
-
|
| 658 |
-
*/
|
| 659 |
-
function generateFinalPrompts(data) {
|
| 660 |
let singleColorIndex = 0;
|
| 661 |
|
| 662 |
return TEMPLATES.map(template => {
|
| 663 |
let text = template.content;
|
| 664 |
|
| 665 |
-
// ---
|
| 666 |
let assignedColor = '';
|
| 667 |
if (template.type === "GLOBAL") {
|
| 668 |
-
// Use Color List String
|
| 669 |
assignedColor = data.colorListString;
|
| 670 |
} else {
|
| 671 |
-
//
|
| 672 |
assignedColor = data.colorArray[singleColorIndex % data.colorArray.length] || 'Assorted';
|
| 673 |
singleColorIndex++;
|
| 674 |
}
|
| 675 |
|
| 676 |
-
// ---
|
| 677 |
-
|
| 678 |
-
//
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
/
|
| 682 |
-
|
| 683 |
-
/
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
|
| 689 |
-
// 2. Replace Material
|
| 690 |
-
// "Linen and Cotton", "linen and cotton", "Linen", "Cotton"
|
| 691 |
-
// We replace the specific blend mention first, then individual if needed
|
| 692 |
-
const matRegex = /linen and cotton/gi;
|
| 693 |
-
text = text.replace(matRegex, data.material);
|
| 694 |
-
|
| 695 |
-
// Also catch standalone "linen" or "cotton" if the input is just one
|
| 696 |
-
if (data.material === 'Linen') {
|
| 697 |
-
text = text.replace(/\bcotton\b/gi, 'Linen');
|
| 698 |
-
} else if (data.material === 'Cotton') {
|
| 699 |
-
text = text.replace(/\blinen\b/gi, 'Cotton');
|
| 700 |
}
|
| 701 |
|
| 702 |
-
|
| 703 |
-
|
| 704 |
-
|
| 705 |
-
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
text = text.replace(/specific 5 colors/g, `specific ${qStr} colors`);
|
| 709 |
-
text = text.replace(/5 identical copies/g, `${qStr} identical copies`);
|
| 710 |
-
text = text.replace(/display the following distinct colors: Green \(Verde\), Khaki \(Cáki\), White \(Branco\), Black \(Preto\), and Beige/g, `display the distinct colors: ${data.colorListString}`);
|
| 711 |
-
|
| 712 |
-
// 4. Replace Gender
|
| 713 |
-
// Replace "female model", "women's", "feminine hand"
|
| 714 |
-
if (data.gender === 'Male') {
|
| 715 |
-
text = text.replace(/female model/gi, 'male model');
|
| 716 |
-
text = text.replace(/women's/gi, "men's");
|
| 717 |
-
text = text.replace(/feminine hand/gi, 'masculine hand');
|
| 718 |
}
|
| 719 |
|
| 720 |
-
//
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
const oldColorList3 = /Verde \(Green\), Cáqui \(Khaki\), Branco \(White\), Preto \(Black\), Beige/g;
|
| 729 |
-
// Create a mapped string for the Portuguese/English mix in the template
|
| 730 |
-
const mappedColorString = data.colorArray.map(c => {
|
| 731 |
-
// Simple mapping back for the specific template format or just use the list
|
| 732 |
-
return c;
|
| 733 |
-
}).join(', ');
|
| 734 |
-
text = text.replace(oldColorList3, mappedColorString);
|
| 735 |
-
|
| 736 |
-
// Replace single color mentions for single items
|
| 737 |
-
if (template.type !== 'GLOBAL') {
|
| 738 |
-
text = text.replace(/natural beige color/g, `${assignedColor.toLowerCase()} color`);
|
| 739 |
-
text = text.replace(/beige color/g, `${assignedColor.toLowerCase()} color`);
|
| 740 |
-
// Replace standalone "beige" at end of sentence or description
|
| 741 |
-
text = text.replace(/, beige\./g, `, ${assignedColor}.`);
|
| 742 |
-
text = text.replace(/ is beige\./g, ` is ${assignedColor}.`);
|
| 743 |
}
|
| 744 |
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
const statusBadge = document.getElementById('statusBadge');
|
| 755 |
-
|
| 756 |
-
// Clear previous results
|
| 757 |
-
grid.innerHTML = '';
|
| 758 |
-
|
| 759 |
-
if (prompts.length === 0) {
|
| 760 |
-
grid.innerHTML = `
|
| 761 |
-
<div class="empty-state" style="grid-column: 1 / -1;">
|
| 762 |
-
<div class="empty-icon">⚡</div>
|
| 763 |
-
<h3>Sem resultados</h3>
|
| 764 |
-
<p>Não foi possível gerar prompts com os dados fornecidos.</p>
|
| 765 |
-
</div>`;
|
| 766 |
-
statusBadge.textContent = 'Erro';
|
| 767 |
-
return;
|
| 768 |
-
}
|
| 769 |
-
|
| 770 |
-
statusBadge.textContent = `${prompts.length} Prompts Gerados`;
|
| 771 |
-
|
| 772 |
-
prompts.forEach((prompt, index) => {
|
| 773 |
-
const card = document.createElement('div');
|
| 774 |
-
card.className = 'card';
|
| 775 |
-
card.style.animationDelay = `${index * 0.05}s`;
|
| 776 |
-
|
| 777 |
-
card.innerHTML = `
|
| 778 |
-
<div class="card-header">
|
| 779 |
-
<div class="card-title">
|
| 780 |
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
| 781 |
-
${prompt.title}
|
| 782 |
-
</div>
|
| 783 |
-
<span class="card-type">${prompt.type}</span>
|
| 784 |
-
</div>
|
| 785 |
-
<div class="card-content">${prompt.content}</div>
|
| 786 |
-
<div class="card-actions">
|
| 787 |
-
<button class="btn-copy" onclick="copyToClipboard(this)">
|
| 788 |
-
<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-2"></path></svg>
|
| 789 |
-
Copiar
|
| 790 |
-
</button>
|
| 791 |
-
</div>
|
| 792 |
-
`;
|
| 793 |
-
grid.appendChild(card);
|
| 794 |
-
});
|
| 795 |
-
}
|
| 796 |
-
|
| 797 |
-
// Copy functionality
|
| 798 |
-
window.copyToClipboard = function(button) {
|
| 799 |
-
const card = button.closest('.card');
|
| 800 |
-
const content = card.querySelector('.card-content').innerText;
|
| 801 |
-
|
| 802 |
-
navigator.clipboard.writeText(content).then(() => {
|
| 803 |
-
showToast();
|
| 804 |
-
const originalHTML = button.innerHTML;
|
| 805 |
-
button.innerHTML = `
|
| 806 |
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
| 807 |
-
Copiado!
|
| 808 |
-
`;
|
| 809 |
-
button.classList.add('copied');
|
| 810 |
-
|
| 811 |
-
setTimeout(() => {
|
| 812 |
-
button.innerHTML = originalHTML;
|
| 813 |
-
button.classList.remove('copied');
|
| 814 |
-
}, 2000);
|
| 815 |
-
}).catch(err => {
|
| 816 |
-
console.error('Erro ao copiar: ', err);
|
| 817 |
-
alert('Não foi possível copiar o texto automaticamente.');
|
| 818 |
-
});
|
| 819 |
-
};
|
| 820 |
-
|
| 821 |
-
function showToast() {
|
| 822 |
-
const toast = document.getElementById('toast');
|
| 823 |
-
toast.classList.add('show');
|
| 824 |
-
setTimeout(() => {
|
| 825 |
-
toast.classList.remove('show');
|
| 826 |
-
}, 3000);
|
| 827 |
-
}
|
| 828 |
-
|
| 829 |
-
// --- Initialization ---
|
| 830 |
-
document.addEventListener('DOMContentLoaded', () => {
|
| 831 |
-
const generateBtn = document.getElementById('generateBtn');
|
| 832 |
-
|
| 833 |
-
generateBtn.addEventListener('click', () => {
|
| 834 |
-
const productNameInput = document.getElementById('productName').value;
|
| 835 |
-
const colorsInput = document.getElementById('colorList').value;
|
| 836 |
|
| 837 |
-
if (
|
| 838 |
-
|
| 839 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 840 |
}
|
| 841 |
|
| 842 |
-
//
|
| 843 |
-
|
| 844 |
-
|
| 845 |
-
|
| 846 |
-
//
|
| 847 |
-
|
| 848 |
-
});
|
| 849 |
-
});
|
| 850 |
|
| 851 |
-
|
| 852 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 853 |
|
| 854 |
-
|
|
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
|
| 4 |
<head>
|
| 5 |
<meta charset="UTF-8">
|
| 6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
+
<title>AI Prompt Gen | E-Commerce Asset System v4.7</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&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
|
|
| 11 |
rel="stylesheet">
|
| 12 |
<style>
|
| 13 |
:root {
|
|
|
|
| 89 |
width: 100%;
|
| 90 |
padding: 2rem;
|
| 91 |
display: grid;
|
| 92 |
+
grid-template-columns: 380px 1fr;
|
| 93 |
gap: 2rem;
|
| 94 |
}
|
| 95 |
|
|
|
|
| 142 |
|
| 143 |
textarea {
|
| 144 |
resize: vertical;
|
| 145 |
+
min-height: 100px;
|
| 146 |
}
|
| 147 |
|
| 148 |
input:focus,
|
|
|
|
| 184 |
.output-container {
|
| 185 |
display: flex;
|
| 186 |
flex-direction: column;
|
| 187 |
+
gap: 1rem;
|
| 188 |
}
|
| 189 |
|
| 190 |
.results-header {
|
|
|
|
| 206 |
|
| 207 |
.grid {
|
| 208 |
display: grid;
|
| 209 |
+
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
|
| 210 |
gap: 1.5rem;
|
| 211 |
}
|
| 212 |
|
|
|
|
| 407 |
<path d="M12 12l-2-2"></path>
|
| 408 |
<path d="M12 16l-2-2"></path>
|
| 409 |
</svg>
|
| 410 |
+
PromptGen AI v4.7
|
| 411 |
</div>
|
| 412 |
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with
|
| 413 |
anycoder</a>
|
|
|
|
| 422 |
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
| 423 |
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
| 424 |
</svg>
|
| 425 |
+
Input Data
|
| 426 |
</h2>
|
| 427 |
|
| 428 |
<div class="form-group">
|
| 429 |
+
<label for="productName">INPUT_PRODUCT_NAME</label>
|
| 430 |
+
<textarea id="productName" placeholder="e.g. Bermuda Viena Feminina Algodão Pague 3 Leve 5"></textarea>
|
| 431 |
+
<p class="helper-text">Include material, gender, features (High-waisted, Nervuras), and offer.</p>
|
| 432 |
</div>
|
| 433 |
|
| 434 |
<div class="form-group">
|
| 435 |
+
<label for="colorList">INPUT_COLORS</label>
|
| 436 |
<input type="text" id="colorList" placeholder="e.g. Black/Grey/Blue/Green">
|
| 437 |
+
<p class="helper-text">Use commas, slashes, or + to separate (e.g., Verde, Cáqui, White).</p>
|
| 438 |
</div>
|
| 439 |
|
| 440 |
+
<button class="btn-primary" id="generateBtn">Generate Prompts</button>
|
| 441 |
</aside>
|
| 442 |
|
| 443 |
<!-- Output Section -->
|
| 444 |
<section class="output-container">
|
| 445 |
<div class="results-header">
|
| 446 |
+
<h2>Generated Results</h2>
|
| 447 |
+
<span class="status-badge" id="statusBadge">Waiting for input...</span>
|
| 448 |
</div>
|
| 449 |
|
| 450 |
<div id="resultsGrid" class="grid">
|
| 451 |
<div class="empty-state">
|
| 452 |
<div class="empty-icon">⚡</div>
|
| 453 |
+
<h3>Ready to Generate</h3>
|
| 454 |
+
<p>Enter product details and click the button to execute the logic engine.</p>
|
| 455 |
</div>
|
| 456 |
</div>
|
| 457 |
</section>
|
| 458 |
</main>
|
| 459 |
|
| 460 |
+
<div id="toast" class="toast">Copied to clipboard!</div>
|
| 461 |
|
| 462 |
<script>
|
| 463 |
+
// --- TEMPLATES (BLOCKS 1-9) ---
|
|
|
|
| 464 |
const TEMPLATES = [
|
| 465 |
{
|
| 466 |
id: "detail_1",
|
| 467 |
title: "DETALHE DO PRODUTO",
|
| 468 |
type: "SINGLE",
|
| 469 |
+
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`
|
| 470 |
},
|
| 471 |
{
|
| 472 |
id: "detail_2",
|
|
|
|
| 490 |
id: "mirror",
|
| 491 |
title: "AVALIAÇÃO ESPELHO",
|
| 492 |
type: "SINGLE",
|
| 493 |
+
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`
|
| 494 |
},
|
| 495 |
{
|
| 496 |
id: "bed",
|
| 497 |
title: "AVALIAÇÃO CAMA",
|
| 498 |
type: "GLOBAL",
|
| 499 |
+
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. STRICT 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`
|
| 500 |
},
|
| 501 |
{
|
| 502 |
id: "main",
|
|
|
|
| 518 |
}
|
| 519 |
];
|
| 520 |
|
| 521 |
+
// --- STEP 1: VARIABLE EXTRACTION & PARSING ---
|
|
|
|
|
|
|
|
|
|
|
|
|
| 522 |
function extractData(productNameInput, colorsInput) {
|
|
|
|
| 523 |
let rawName = productNameInput.trim();
|
| 524 |
+
|
| 525 |
+
// 1. PRODUCT_NAME (Cleaned)
|
| 526 |
+
// Remove promo text for the clean name, but keep it for PROMOTIONAL_OFFER extraction
|
|
|
|
| 527 |
const promoRegex = /pague\s*\d+\s*(?:,|e)?\s*leve\s*\d+/i;
|
| 528 |
const promoMatch = rawName.match(promoRegex);
|
| 529 |
+
let promoOffer = promoMatch ? promoMatch[0] : null;
|
| 530 |
|
| 531 |
+
// Clean Name: Remove promo offer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 532 |
let productName = rawName.replace(promoRegex, '').trim();
|
| 533 |
+
// Remove trailing punctuation artifacts
|
| 534 |
productName = productName.replace(/,\s*$/, '').trim();
|
| 535 |
|
| 536 |
+
// 2. MATERIAL (Extract & Translate)
|
| 537 |
const materialMap = {
|
| 538 |
+
'algodão': 'Cotton', 'algodon': 'Cotton',
|
|
|
|
| 539 |
'linho': 'Linen',
|
| 540 |
+
'poliéster': 'Polyester', 'poliester': 'Polyester',
|
|
|
|
| 541 |
'seda': 'Silk',
|
| 542 |
+
'jeans': 'Denim', 'denim': 'Denim',
|
|
|
|
| 543 |
'couro': 'Leather'
|
| 544 |
};
|
|
|
|
| 545 |
let material = 'Cotton'; // Default
|
| 546 |
const lowerInput = rawName.toLowerCase();
|
|
|
|
| 547 |
for (const [key, value] of Object.entries(materialMap)) {
|
| 548 |
if (lowerInput.includes(key)) {
|
| 549 |
material = value;
|
|
|
|
| 557 |
gender = 'Male';
|
| 558 |
}
|
| 559 |
|
| 560 |
+
// 4. PROMOTIONAL_OFFER (Already extracted above)
|
| 561 |
+
|
| 562 |
+
// 5. TOTAL_QUANTITY
|
| 563 |
+
let totalQuantity = 0;
|
| 564 |
+
// Priority A: Extract from "Leve [X]"
|
| 565 |
+
if (promoOffer) {
|
| 566 |
+
const qtyMatch = promoOffer.match(/leve\s*(\d+)/i);
|
| 567 |
+
if (qtyMatch) totalQuantity = parseInt(qtyMatch[1], 10);
|
| 568 |
}
|
| 569 |
|
| 570 |
+
// 6. HAS_GIFTS
|
| 571 |
+
const hasGifts = /brinde|bônus|presente|bonus/i.test(rawName);
|
| 572 |
+
|
| 573 |
+
// 7. STRUCTURAL_FEATURES
|
| 574 |
const hasHighWaisted = /(?:high-waisted|alta cintura|cintura alta)/i.test(rawName);
|
| 575 |
const hasRibs = /(?:nervuras|ribs|costuras)/i.test(rawName);
|
| 576 |
|
| 577 |
+
// 8. COLORS (Split & Translate)
|
|
|
|
| 578 |
const rawColors = colorsInput.split(/[\/,+]/).map(c => c.trim()).filter(c => c.length > 0);
|
| 579 |
|
|
|
|
| 580 |
const colorMap = {
|
| 581 |
+
'verde': 'Green', 'vermelho': 'Red', 'azul': 'Blue', 'amarelo': 'Yellow',
|
| 582 |
+
'laranja': 'Orange', 'roxo': 'Purple', 'rosa': 'Pink', 'marrom': 'Brown',
|
| 583 |
+
'preto': 'Black', 'branco': 'White', 'cinza': 'Grey', 'grey': 'Grey',
|
| 584 |
+
'bege': 'Beige', 'khaki': 'Khaki', 'cáqui': 'Khaki', 'dourado': 'Gold', 'prata': 'Silver'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
};
|
| 586 |
|
| 587 |
const colorArray = rawColors.map(c => {
|
|
|
|
| 589 |
return colorMap[lowerC] || c;
|
| 590 |
});
|
| 591 |
|
| 592 |
+
// Priority B: Count from colors if quantity is 0
|
| 593 |
if (totalQuantity === 0) {
|
| 594 |
totalQuantity = colorArray.length > 0 ? colorArray.length : 1;
|
| 595 |
}
|
| 596 |
|
| 597 |
+
// Format Color List String (e.g., "Green, Khaki, White, Black, and Beige")
|
| 598 |
let colorListString = '';
|
| 599 |
if (colorArray.length === 0) {
|
| 600 |
colorListString = 'Assorted Colors';
|
|
|
|
| 622 |
};
|
| 623 |
}
|
| 624 |
|
| 625 |
+
// --- STEP 2, 3, & 4: LOGIC, DISTRIBUTION & INJECTION ---
|
| 626 |
+
function processTemplates(data) {
|
|
|
|
|
|
|
| 627 |
let singleColorIndex = 0;
|
| 628 |
|
| 629 |
return TEMPLATES.map(template => {
|
| 630 |
let text = template.content;
|
| 631 |
|
| 632 |
+
// --- STEP 3: COLOR DISTRIBUTION ---
|
| 633 |
let assignedColor = '';
|
| 634 |
if (template.type === "GLOBAL") {
|
|
|
|
| 635 |
assignedColor = data.colorListString;
|
| 636 |
} else {
|
| 637 |
+
// Single Display Rotation
|
| 638 |
assignedColor = data.colorArray[singleColorIndex % data.colorArray.length] || 'Assorted';
|
| 639 |
singleColorIndex++;
|
| 640 |
}
|
| 641 |
|
| 642 |
+
// --- STEP 2: CONDITIONAL LOGIC ENGINE ---
|
| 643 |
+
|
| 644 |
+
// Rule A: Feature Adaptation (Deletion)
|
| 645 |
+
if (!data.hasHighWaisted) {
|
| 646 |
+
// Remove "high-waisted", "high waist", "alta cintura"
|
| 647 |
+
// Using regex to handle word boundaries and spacing roughly
|
| 648 |
+
text = text.replace(/\bhigh-waisted\b/gi, '');
|
| 649 |
+
text = text.replace(/\bhigh waist\b/gi, 'waist');
|
| 650 |
+
text = text.replace(/\balta cintura\b/gi, '');
|
| 651 |
+
|
| 652 |
+
// Cleanup double spaces resulting from removal
|
| 653 |
+
text = text.replace(/\s+/g, ' ').trim();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 654 |
}
|
| 655 |
|
| 656 |
+
if (!data.hasRibs) {
|
| 657 |
+
// Remove "vertical front seams", "nervuras", "ribs"
|
| 658 |
+
text = text.replace(/vertical front seams/gi, '');
|
| 659 |
+
text = text.replace(/nervuras/gi, '');
|
| 660 |
+
text = text.replace(/ribs/gi, '');
|
| 661 |
+
text = text.replace(/\s+/g, ' ').trim();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 662 |
}
|
| 663 |
|
| 664 |
+
// Rule B: Gender Adaptation
|
| 665 |
+
if (data.gender === 'Male') {
|
| 666 |
+
text = text.replace(/female model/gi, 'male model');
|
| 667 |
+
text = text.replace(/woman/gi, 'man');
|
| 668 |
+
text = text.replace(/women's/gi, "men's");
|
| 669 |
+
text = text.replace(/feminine hand/gi, 'masculine hand');
|
| 670 |
+
text = text.replace(/Brazilian woman/gi, 'Brazilian man');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 671 |
}
|
| 672 |
|
| 673 |
+
// Rule C: Text Overlay & Gift Logic
|
| 674 |
+
if (template.id === 'text_overlay') {
|
| 675 |
+
if (data.promoOffer) {
|
| 676 |
+
text = `Ensure the promotional text "${data.promoOffer}". 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.`;
|
| 677 |
+
} else {
|
| 678 |
+
// If no promo, display Product Name
|
| 679 |
+
text = `Ensure the product name "${data.productName}" is clearly visible. 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.`;
|
| 680 |
+
}
|
| 681 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 682 |
|
| 683 |
+
if (template.id === 'detail_2' && data.hasGifts) {
|
| 684 |
+
// Logic: DELETE PROMOTIONAL_OFFER logic (use Product Name)
|
| 685 |
+
// Since Block 2 is a macro shot, it doesn't inherently have text.
|
| 686 |
+
// We ensure the prompt focuses purely on the product material/name context.
|
| 687 |
+
// The prompt instruction says "Display only Product Name".
|
| 688 |
+
// We will append a clarification to ensure no promo text is implied if it were.
|
| 689 |
+
// (Since the base template doesn't have promo text, we leave it mostly as is,
|
| 690 |
+
// but we ensure we don't inject promo text later).
|
| 691 |
}
|
| 692 |
|
| 693 |
+
// --- STEP 4: TEMPLATE INJECTION ---
|
| 694 |
+
|
| 695 |
+
// 1. Product Name
|
| 696 |
+
text = text.replace(/Bermuda Viena Feminina/g, data.productName);
|
| 697 |
+
text = text.replace(/'Bermuda Viena'/g, `'${data.productName}'`);
|
| 698 |
+
text = text.replace(/Bermuda Viena/g, data.productName);
|
|
|
|
|
|
|
| 699 |
|
| 700 |
+
// 2. Material
|
| 701 |
+
// Replace "Linen and Cotton" first
|
| 702 |
+
text = text.replace(/Linen and Cotton/gi, data.material);
|
| 703 |
+
// Handle individual fallbacks if material is singular
|
| 704 |
+
if (data.material === 'Linen') text = text.replace(/\bCotton\b/gi, 'Linen');
|
| 705 |
+
if (data.material === 'Cotton') text = text.replace(/\bLinen\b/gi, 'Cotton');
|
| 706 |
|
| 707 |
+
// 3. Quantity
|
| 708 |
+
const
|