Only show T-Shirt Compositor and garment controls when product is a T-Shirt
This commit is contained in:
1 parent
6c6867b49c
commit
ff0fe0990e
3 files changed
+124
-25
No files matched your search
+104
-14
@@ -780,13 +780,81 @@ function renderTriageStage() {
|
||||
});
|
||||
}
|
||||
|
||||
// --- SLIDE-OVER RICH WORKBENCH & NANO BANANA STUDIO ---
|
||||
// --- SLIDE-OVER RICH WORKBENCH & IMAGE MOCKUP STUDIO ---
|
||||
function detectDefaultProductTemplate(idea) {
|
||||
if (!idea) return "app-ui";
|
||||
const textBlob = [
|
||||
idea.title || "",
|
||||
idea.elevator_pitch || "",
|
||||
...(idea.tags || [])
|
||||
].join(" ");
|
||||
|
||||
if (/\b(left[- ]chest|pocket\s+tee|pocket\s+emblem)\b/i.test(textBlob)) {
|
||||
return "tshirt-left-chest";
|
||||
}
|
||||
if (/\b(t-?shirt|tee\b|shirt|apparel|crewneck|garment|hoodie|merch|wearable)\b/i.test(textBlob)) {
|
||||
return "tshirt-front";
|
||||
}
|
||||
const latestMockupTemplate = idea.mockups?.[0]?.template;
|
||||
if (latestMockupTemplate === "tshirt-front" || latestMockupTemplate === "tshirt-left-chest") {
|
||||
return latestMockupTemplate;
|
||||
}
|
||||
if (/\b(sticker|decal|patch|badge)\b/i.test(textBlob) || latestMockupTemplate === "sticker") {
|
||||
return "sticker";
|
||||
}
|
||||
if (/\b(poster|print|wall\s*art|framed)\b/i.test(textBlob) || latestMockupTemplate === "poster") {
|
||||
return "poster";
|
||||
}
|
||||
if (idea.category === "creative") {
|
||||
return "isolated-graphic";
|
||||
}
|
||||
return "app-ui";
|
||||
}
|
||||
|
||||
function isCurrentProductTShirt() {
|
||||
const templateSel = document.getElementById("nb-template-select");
|
||||
const templateVal = templateSel ? templateSel.value : detectDefaultProductTemplate(state.activeIdea);
|
||||
return templateVal === "tshirt-front" || templateVal === "tshirt-left-chest";
|
||||
}
|
||||
|
||||
function syncTShirtGeneratorVisibility() {
|
||||
const isTShirt = isCurrentProductTShirt();
|
||||
const tshirtCard = document.getElementById("tshirt-compositor-card");
|
||||
const garmentColorGroup = document.getElementById("nb-garment-color-group");
|
||||
|
||||
if (tshirtCard) {
|
||||
tshirtCard.classList.toggle("hidden", !isTShirt);
|
||||
}
|
||||
if (garmentColorGroup) {
|
||||
garmentColorGroup.classList.toggle("hidden", !isTShirt);
|
||||
}
|
||||
|
||||
if (isTShirt) {
|
||||
const surfaceSel = document.getElementById("comp-surface-select");
|
||||
if (surfaceSel) surfaceSel.value = "tshirt";
|
||||
const templateVal = document.getElementById("nb-template-select")?.value;
|
||||
const placementSel = document.getElementById("comp-placement-select");
|
||||
if (placementSel && templateVal === "tshirt-left-chest") {
|
||||
placementSel.value = "left-chest";
|
||||
} else if (placementSel && templateVal === "tshirt-front" && placementSel.value === "left-chest") {
|
||||
placementSel.value = "center-chest";
|
||||
}
|
||||
renderCompositorCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
async function openIdeaDrawer(id, initialTab = "details") {
|
||||
const data = await apiFetch(`/api/ideas/${id}`);
|
||||
state.activeIdeaId = id;
|
||||
state.activeIdea = data.idea;
|
||||
state.nbEditParentMockup = null;
|
||||
|
||||
const templateSel = document.getElementById("nb-template-select");
|
||||
if (templateSel) {
|
||||
templateSel.value = detectDefaultProductTemplate(state.activeIdea);
|
||||
}
|
||||
syncTShirtGeneratorVisibility();
|
||||
|
||||
document.getElementById("drawer-backdrop").classList.remove("hidden");
|
||||
document.getElementById("idea-drawer").classList.remove("hidden");
|
||||
|
||||
@@ -796,13 +864,15 @@ async function openIdeaDrawer(id, initialTab = "details") {
|
||||
// Pre-populate compositor text from idea title
|
||||
const topInput = document.getElementById("comp-top-text");
|
||||
const bottomInput = document.getElementById("comp-bottom-text");
|
||||
if (topInput && !topInput.value) {
|
||||
if (topInput) {
|
||||
topInput.value = state.activeIdea.title.slice(0, 26).toUpperCase();
|
||||
}
|
||||
if (bottomInput && !bottomInput.value) {
|
||||
if (bottomInput) {
|
||||
bottomInput.value = state.activeIdea.tags[0] ? `#${state.activeIdea.tags[0].toUpperCase()}` : "SPARKVAULT STUDIO";
|
||||
}
|
||||
renderCompositorCanvas();
|
||||
if (isCurrentProductTShirt()) {
|
||||
renderCompositorCanvas();
|
||||
}
|
||||
|
||||
if (initialTab === "whack" && !state.drawerWhackCard) {
|
||||
await drawCardForActiveIdea();
|
||||
@@ -854,9 +924,13 @@ async function requestNameSuggestionsForActiveIdea() {
|
||||
<button type="button" class="btn btn-primary btn-xs btn-use-suggested-title" data-name="${escapeHtml(s.name)}" data-tagline="${escapeHtml(s.tagline || "")}">
|
||||
✓ Use Title
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-xs btn-use-suggested-tee" data-name="${escapeHtml(s.name)}" data-tagline="${escapeHtml(s.tagline || "")}">
|
||||
👕 Send to Tee Canvas
|
||||
</button>
|
||||
${
|
||||
isCurrentProductTShirt()
|
||||
? `<button type="button" class="btn btn-ghost btn-xs btn-use-suggested-tee" data-name="${escapeHtml(s.name)}" data-tagline="${escapeHtml(s.tagline || "")}">
|
||||
👕 Send to Tee Canvas
|
||||
</button>`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
@@ -1064,9 +1138,13 @@ function renderDrawerAttachments(idea) {
|
||||
<button type="button" class="btn btn-primary btn-xs btn-att-to-mockup" data-att-id="${escapeHtml(att.id)}">
|
||||
🎨 Send to Mockup Studio
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-xs btn-att-to-canvas" data-att-url="${escapeHtml(att.imageUrl)}">
|
||||
👕 Tee Canvas
|
||||
</button>
|
||||
${
|
||||
isCurrentProductTShirt()
|
||||
? `<button type="button" class="btn btn-ghost btn-xs btn-att-to-canvas" data-att-url="${escapeHtml(att.imageUrl)}">
|
||||
👕 Tee Canvas
|
||||
</button>`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
!isCover
|
||||
? `<button type="button" class="btn btn-ghost btn-xs btn-att-cover" data-att-id="${escapeHtml(att.id)}">⭐ Cover</button>`
|
||||
@@ -1177,7 +1255,7 @@ function renderDrawerMockupGallery(idea) {
|
||||
if (!container) return;
|
||||
|
||||
if (!idea.mockups || idea.mockups.length === 0) {
|
||||
container.innerHTML = `<p class="muted-label">No mockups saved for this idea yet. Generate one in the Image Mockup Studio or save a layout from the T-Shirt Compositor!</p>`;
|
||||
container.innerHTML = `<p class="muted-label">No mockups saved for this idea yet. Generate one in the Image Mockup Studio!</p>`;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1196,9 +1274,13 @@ function renderDrawerMockupGallery(idea) {
|
||||
<button type="button" class="btn btn-primary btn-xs btn-mk-refine" data-mk-id="${escapeHtml(m.id)}">
|
||||
✨ Refine Mockup
|
||||
</button>
|
||||
<button type="button" class="btn btn-ghost btn-xs btn-mk-load-canvas" data-mk-url="${escapeHtml(m.imageUrl)}">
|
||||
👕 Place on Tee Canvas
|
||||
</button>
|
||||
${
|
||||
isCurrentProductTShirt()
|
||||
? `<button type="button" class="btn btn-ghost btn-xs btn-mk-load-canvas" data-mk-url="${escapeHtml(m.imageUrl)}">
|
||||
👕 Place on Tee Canvas
|
||||
</button>`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
!isCover
|
||||
? `<button type="button" class="btn btn-ghost btn-xs btn-mk-set-cover" data-mk-id="${escapeHtml(m.id)}">⭐ Set Cover</button>`
|
||||
@@ -2109,6 +2191,14 @@ function bindGlobalEvents() {
|
||||
document.getElementById("nb-edit-instruction-input").value = "";
|
||||
});
|
||||
|
||||
document.getElementById("nb-template-select")?.addEventListener("change", () => {
|
||||
syncTShirtGeneratorVisibility();
|
||||
if (state.activeIdea) {
|
||||
renderDrawerMockupGallery(state.activeIdea);
|
||||
renderDrawerAttachments(state.activeIdea);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("nb-generator-select")?.addEventListener("change", async (e) => {
|
||||
const chosenModel = e.target.value;
|
||||
state.settings.nanoBananaModel = chosenModel;
|
||||
|
||||
+7
-7
@@ -573,9 +573,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Garment / Surface Color Swatches -->
|
||||
<div class="field-group">
|
||||
<label>Garment / Surface Color: <strong id="nb-selected-color-label">Vintage Cream (#F5E8D3)</strong></label>
|
||||
<!-- Garment / Surface Color Swatches (Only shown when product is a T-Shirt) -->
|
||||
<div id="nb-garment-color-group" class="field-group hidden">
|
||||
<label>T-Shirt Garment Color: <strong id="nb-selected-color-label">Vintage Cream (#F5E8D3)</strong></label>
|
||||
<div class="color-swatch-row" id="nb-color-swatches">
|
||||
<button type="button" class="color-swatch active" data-color="#F5E8D3" data-name="Vintage Cream" style="background:#F5E8D3" title="Vintage Cream (#F5E8D3)"></button>
|
||||
<button type="button" class="color-swatch" data-color="#1C1B1A" data-name="Vintage Black" style="background:#1C1B1A" title="Vintage Black (#1C1B1A)"></button>
|
||||
@@ -589,7 +589,7 @@
|
||||
|
||||
<div class="field-group">
|
||||
<label for="nb-prompt-input">Visual Description / Artwork Prompt</label>
|
||||
<textarea id="nb-prompt-input" rows="3" class="textarea-input" placeholder="Describe the graphic, emblem, ink colors, typography, or mockup scene..."></textarea>
|
||||
<textarea id="nb-prompt-input" rows="3" class="textarea-input" placeholder="Describe the visual concept, product UI, illustration, or mockup scene..."></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Iterative Image-to-Image Edit Banner (Active when a mockup is selected for refinement) -->
|
||||
@@ -602,7 +602,7 @@
|
||||
type="text"
|
||||
id="nb-edit-instruction-input"
|
||||
class="text-input"
|
||||
placeholder="Describe what to change (e.g. 'Move graphic to left-chest pocket on a forest green shirt')"
|
||||
placeholder="Describe what to change (e.g. 'Switch to dark mode layout' or 'Use bolder vintage typography')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -614,8 +614,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Interactive T-Shirt & Product Canvas Compositor -->
|
||||
<div class="studio-card">
|
||||
<!-- Interactive T-Shirt & Emblem Compositor (Only shown when product is a T-Shirt) -->
|
||||
<div id="tshirt-compositor-card" class="studio-card hidden">
|
||||
<div class="studio-card-header">
|
||||
<h3>👕 Interactive T-Shirt & Emblem Compositor</h3>
|
||||
<span class="muted-label">Instant apparel layout & print proof</span>
|
||||
|
||||
@@ -321,18 +321,27 @@ export async function generateNameSuggestions({
|
||||
userBackground = DEFAULT_USER_BACKGROUND,
|
||||
geminiApiKey = ""
|
||||
}) {
|
||||
const ideaTextBlob = [idea.title || "", idea.elevator_pitch || "", ...(idea.tags || [])].join(" ");
|
||||
const isTShirt = /\b(t-?shirt|tee\b|shirt|apparel|crewneck|garment|hoodie|merch|wearable)\b/i.test(ideaTextBlob);
|
||||
|
||||
const angleGuidance = isTShirt
|
||||
? "Generate 6 distinct, memorable name/title suggestions for this apparel/t-shirt idea across different naming angles (e.g., Vintage Club/Crest, Punchy Brand Name, Witty/Clever, Minimalist, Screenprint Headline + Sub-tagline, Outfitter Badge)."
|
||||
: "Generate 6 distinct, memorable name/title suggestions for this idea across different naming angles (e.g., Punchy Brand Name, Maker/Project Codename, Witty/Clever, Minimalist Editorial, Descriptive Product Name, Bold Hook). Do NOT frame it as a t-shirt or apparel slogan.";
|
||||
|
||||
const prompt = [
|
||||
"You are a sharp creative director, brand namer, and apparel/product copywriter.",
|
||||
"You are a sharp creative director, product namer, and brand strategist.",
|
||||
`IDEA TO NAME:\n- Current Title: ${idea.title}\n- Elevator Pitch: ${idea.elevator_pitch || ""}\n- Category: ${idea.category}\n- Tags: ${(idea.tags || []).join(", ")}\n- Notes: ${(idea.notes_markdown || "").slice(0, 600)}`,
|
||||
`USER BACKGROUND & TASTES:\n${userBackground || DEFAULT_USER_BACKGROUND}`,
|
||||
"Generate 6 distinct, memorable name/title suggestions for this idea across different naming angles (e.g., Vintage Club/Crest, Punchy Brand Name, Witty/Clever, Minimalist, Screenprint Headline + Sub-tagline, Maker/Project Codename).",
|
||||
angleGuidance,
|
||||
"Do NOT run any shell commands or tools. Reply ONLY with a single raw JSON object (no markdown fences) matching this exact schema:",
|
||||
JSON.stringify({
|
||||
suggestions: [
|
||||
{
|
||||
name: "Suggested Idea Title or Brand Name",
|
||||
tagline: "Short punchy subtitle, est. line, or one-line hook",
|
||||
style: "Angle badge (e.g. Vintage Club, Punchy Brand, Witty, Minimalist, Tee Slogan, Codename)"
|
||||
tagline: "Short punchy subtitle or one-line hook",
|
||||
style: isTShirt
|
||||
? "Angle badge (e.g. Vintage Club, Punchy Brand, Witty, Minimalist, Tee Slogan, Outfitter)"
|
||||
: "Angle badge (e.g. Punchy Brand, Codename, Witty, Minimalist, Product Name, Bold Hook)"
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user