Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- index.html +108 -17
index.html
CHANGED
|
@@ -7,7 +7,9 @@
|
|
| 7 |
<title>AI Prompt Gen | E-Commerce Specialist</title>
|
| 8 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
-
<link
|
|
|
|
|
|
|
| 11 |
<style>
|
| 12 |
:root {
|
| 13 |
--bg-body: #0f172a;
|
|
@@ -219,6 +221,7 @@
|
|
| 219 |
transition: transform 0.2s, box-shadow 0.2s;
|
| 220 |
position: relative;
|
| 221 |
overflow: hidden;
|
|
|
|
| 222 |
}
|
| 223 |
|
| 224 |
.card:hover {
|
|
@@ -304,6 +307,7 @@
|
|
| 304 |
color: var(--text-muted);
|
| 305 |
border: 2px dashed var(--border);
|
| 306 |
border-radius: var(--radius);
|
|
|
|
| 307 |
}
|
| 308 |
|
| 309 |
.empty-icon {
|
|
@@ -370,18 +374,21 @@
|
|
| 370 |
padding: 1rem;
|
| 371 |
}
|
| 372 |
}
|
| 373 |
-
|
| 374 |
/* Scrollbar styling */
|
| 375 |
::-webkit-scrollbar {
|
| 376 |
width: 8px;
|
| 377 |
}
|
|
|
|
| 378 |
::-webkit-scrollbar-track {
|
| 379 |
background: transparent;
|
| 380 |
}
|
|
|
|
| 381 |
::-webkit-scrollbar-thumb {
|
| 382 |
background: var(--border);
|
| 383 |
border-radius: 4px;
|
| 384 |
}
|
|
|
|
| 385 |
::-webkit-scrollbar-thumb:hover {
|
| 386 |
background: var(--text-muted);
|
| 387 |
}
|
|
@@ -392,7 +399,15 @@
|
|
| 392 |
|
| 393 |
<header>
|
| 394 |
<div class="brand">
|
| 395 |
-
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 396 |
PromptGen AI
|
| 397 |
</div>
|
| 398 |
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with
|
|
@@ -434,7 +449,7 @@
|
|
| 434 |
</div>
|
| 435 |
|
| 436 |
<div id="resultsGrid" class="grid">
|
| 437 |
-
<div class="empty-state"
|
| 438 |
<div class="empty-icon">⚡</div>
|
| 439 |
<h3>Pronto para Gerar</h3>
|
| 440 |
<p>Insira os detalhes do produto e clique no botão para criar seu conjunto de prompts.</p>
|
|
@@ -453,7 +468,7 @@
|
|
| 453 |
id: "detail_1",
|
| 454 |
title: "DETALHE DO PRODUTO",
|
| 455 |
type: "SINGLE",
|
| 456 |
-
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`
|
| 457 |
},
|
| 458 |
{
|
| 459 |
id: "detail_2",
|
|
@@ -477,13 +492,13 @@
|
|
| 477 |
id: "mirror",
|
| 478 |
title: "AVALIAÇÃO ESPELHO",
|
| 479 |
type: "SINGLE",
|
| 480 |
-
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`
|
| 481 |
},
|
| 482 |
{
|
| 483 |
id: "bed",
|
| 484 |
title: "AVALIAÇÃO CAMA",
|
| 485 |
type: "GLOBAL",
|
| 486 |
-
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
|
| 487 |
},
|
| 488 |
{
|
| 489 |
id: "main",
|
|
@@ -512,16 +527,11 @@
|
|
| 512 |
*/
|
| 513 |
function extractData(productNameInput, colorsInput) {
|
| 514 |
// 1. PRODUCT_NAME: Clean input
|
| 515 |
-
// Remove promotional text for the core name, but keep it for PROMOTIONAL_OFFER extraction
|
| 516 |
-
// However, the prompt says "PRODUCT_NAME: The core product name (cleaned of promotional text)."
|
| 517 |
-
// We need to extract promo text first, then remove it.
|
| 518 |
-
|
| 519 |
let rawName = productNameInput.trim();
|
| 520 |
let promoOffer = null;
|
| 521 |
let totalQuantity = 0;
|
| 522 |
|
| 523 |
// Extract Promotional Offer (e.g., "Pague 3, Leve 5")
|
| 524 |
-
// Regex looks for "Pague X, Leve Y" variations
|
| 525 |
const promoRegex = /pague\s*\d+\s*(?:,|e)?\s*leve\s*\d+/i;
|
| 526 |
const promoMatch = rawName.match(promoRegex);
|
| 527 |
|
|
@@ -553,7 +563,7 @@
|
|
| 553 |
};
|
| 554 |
|
| 555 |
let material = 'Cotton'; // Default
|
| 556 |
-
const lowerInput = rawName.toLowerCase();
|
| 557 |
|
| 558 |
for (const [key, value] of Object.entries(materialMap)) {
|
| 559 |
if (lowerInput.includes(key)) {
|
|
@@ -607,7 +617,6 @@
|
|
| 607 |
|
| 608 |
const colorArray = rawColors.map(c => {
|
| 609 |
const lowerC = c.toLowerCase();
|
| 610 |
-
// If it's already in the map, translate. Otherwise keep as is (assuming English or proper noun).
|
| 611 |
return colorMap[lowerC] || c;
|
| 612 |
});
|
| 613 |
|
|
@@ -679,8 +688,7 @@
|
|
| 679 |
|
| 680 |
// 2. Replace Material
|
| 681 |
// "Linen and Cotton", "linen and cotton", "Linen", "Cotton"
|
| 682 |
-
// We replace the specific blend mention first, then individual if needed
|
| 683 |
-
// but usually, we map the input material to the blend.
|
| 684 |
const matRegex = /linen and cotton/gi;
|
| 685 |
text = text.replace(matRegex, data.material);
|
| 686 |
|
|
@@ -694,4 +702,87 @@
|
|
| 694 |
// 3. Replace Quantity
|
| 695 |
// "5 pairs", "5 identical copies", "exactly 5 pairs"
|
| 696 |
const qStr = data.totalQuantity.toString();
|
| 697 |
-
text = text.replace(/5 pairs/g,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
<title>AI Prompt Gen | E-Commerce Specialist</title>
|
| 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 {
|
| 15 |
--bg-body: #0f172a;
|
|
|
|
| 221 |
transition: transform 0.2s, box-shadow 0.2s;
|
| 222 |
position: relative;
|
| 223 |
overflow: hidden;
|
| 224 |
+
animation: fadeIn 0.5s ease-out;
|
| 225 |
}
|
| 226 |
|
| 227 |
.card:hover {
|
|
|
|
| 307 |
color: var(--text-muted);
|
| 308 |
border: 2px dashed var(--border);
|
| 309 |
border-radius: var(--radius);
|
| 310 |
+
grid-column: 1 / -1;
|
| 311 |
}
|
| 312 |
|
| 313 |
.empty-icon {
|
|
|
|
| 374 |
padding: 1rem;
|
| 375 |
}
|
| 376 |
}
|
| 377 |
+
|
| 378 |
/* Scrollbar styling */
|
| 379 |
::-webkit-scrollbar {
|
| 380 |
width: 8px;
|
| 381 |
}
|
| 382 |
+
|
| 383 |
::-webkit-scrollbar-track {
|
| 384 |
background: transparent;
|
| 385 |
}
|
| 386 |
+
|
| 387 |
::-webkit-scrollbar-thumb {
|
| 388 |
background: var(--border);
|
| 389 |
border-radius: 4px;
|
| 390 |
}
|
| 391 |
+
|
| 392 |
::-webkit-scrollbar-thumb:hover {
|
| 393 |
background: var(--text-muted);
|
| 394 |
}
|
|
|
|
| 399 |
|
| 400 |
<header>
|
| 401 |
<div class="brand">
|
| 402 |
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
| 403 |
+
stroke-linecap="round" stroke-linejoin="round">
|
| 404 |
+
<path d="M12 2a10 10 0 1 0 10 10 4 4 0 0 1-5-5 4 4 0 0 1-5-5"></path>
|
| 405 |
+
<path d="M8.5 8.5v.01"></path>
|
| 406 |
+
<path d="M16 12l-2-2"></path>
|
| 407 |
+
<path d="M16 16l-2-2"></path>
|
| 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
|
|
|
|
| 449 |
</div>
|
| 450 |
|
| 451 |
<div id="resultsGrid" class="grid">
|
| 452 |
+
<div class="empty-state">
|
| 453 |
<div class="empty-icon">⚡</div>
|
| 454 |
<h3>Pronto para Gerar</h3>
|
| 455 |
<p>Insira os detalhes do produto e clique no botão para criar seu conjunto de prompts.</p>
|
|
|
|
| 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, natural beige color. DO NOT ALTER THE SILHOUETTE OR DETAILS. --ar 1:1 --style raw --v 6.0`
|
| 472 |
},
|
| 473 |
{
|
| 474 |
id: "detail_2",
|
|
|
|
| 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, natural 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`
|
| 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 Beige. 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`
|
| 502 |
},
|
| 503 |
{
|
| 504 |
id: "main",
|
|
|
|
| 527 |
*/
|
| 528 |
function extractData(productNameInput, colorsInput) {
|
| 529 |
// 1. PRODUCT_NAME: Clean input
|
|
|
|
|
|
|
|
|
|
|
|
|
| 530 |
let rawName = productNameInput.trim();
|
| 531 |
let promoOffer = null;
|
| 532 |
let totalQuantity = 0;
|
| 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 |
|
|
|
|
| 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)) {
|
|
|
|
| 617 |
|
| 618 |
const colorArray = rawColors.map(c => {
|
| 619 |
const lowerC = c.toLowerCase();
|
|
|
|
| 620 |
return colorMap[lowerC] || c;
|
| 621 |
});
|
| 622 |
|
|
|
|
| 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 |
|
|
|
|
| 702 |
// 3. Replace Quantity
|
| 703 |
// "5 pairs", "5 identical copies", "exactly 5 pairs"
|
| 704 |
const qStr = data.totalQuantity.toString();
|
| 705 |
+
text = text.replace(/5 pairs/g, `${qStr} pairs`);
|
| 706 |
+
text = text.replace(/exactly 5 copies/g, `exactly ${qStr} copies`);
|
| 707 |
+
text = text.replace(/exactly 5 pairs/g, `exactly ${qStr} pairs`);
|
| 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 |
+
// 5. Replace Colors
|
| 721 |
+
// Replace specific color lists
|
| 722 |
+
const oldColorList = /Green, Khaki, White, Black, and Beige/g;
|
| 723 |
+
text = text.replace(oldColorList, data.colorListString);
|
| 724 |
+
|
| 725 |
+
const oldColorList2 = /Green, Khaki, White, Black, and Bege/g;
|
| 726 |
+
text = text.replace(oldColorList2, data.colorListString);
|
| 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 |
+
return { ...template, content: text, generatedColor: assignedColor };
|
| 746 |
+
});
|
| 747 |
+
}
|
| 748 |
+
|
| 749 |
+
/**
|
| 750 |
+
* UI Functions
|
| 751 |
+
*/
|
| 752 |
+
function renderResults(prompts) {
|
| 753 |
+
const grid = document.getElementById('resultsGrid');
|
| 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
|