feat(mockup): rename to Image Mockup Studio and add inline generator selector
This commit is contained in:
1 parent
3368d7201c
commit
25d24a53a6
2 files changed
+68
-34
No files matched your search
+35
-11
@@ -138,13 +138,17 @@ async function loadSettings() {
|
||||
const keyStatus = document.getElementById("settings-key-status");
|
||||
if (keyStatus) {
|
||||
keyStatus.textContent = data.hasApiKey
|
||||
? `Active key configured (${data.apiKeySource}: ${data.maskedKey})`
|
||||
: "No key stored yet — paste your Gemini API key to enable live Nano Banana 2.1 generation.";
|
||||
? `Active provider configured (${data.apiKeySource}: ${data.maskedKey})`
|
||||
: "No Gemini API key stored — OpenAI Codex works automatically via OAuth.";
|
||||
}
|
||||
const modelSel = document.getElementById("settings-model-select");
|
||||
if (modelSel && data.nanoBananaModel) {
|
||||
modelSel.value = data.nanoBananaModel;
|
||||
}
|
||||
const studioGenSel = document.getElementById("nb-generator-select");
|
||||
if (studioGenSel && data.nanoBananaModel) {
|
||||
studioGenSel.value = data.nanoBananaModel;
|
||||
}
|
||||
const bgEl = document.getElementById("settings-user-background");
|
||||
if (bgEl && data.userBackground !== undefined) {
|
||||
bgEl.value = data.userBackground;
|
||||
@@ -439,7 +443,7 @@ function renderColliderStage() {
|
||||
💾 Log Breakthrough to Idea
|
||||
</button>
|
||||
<button type="button" id="btn-collider-to-mockup" class="btn btn-secondary">
|
||||
🍌 Visualize This Twist in Nano Banana Studio
|
||||
🖼️ Visualize Twist in Image Mockup Studio
|
||||
</button>`
|
||||
: ""
|
||||
}
|
||||
@@ -728,8 +732,8 @@ function renderTriageStage() {
|
||||
<p style="color:var(--text-secondary);">${escapeHtml(item.elevator_pitch || "No elevator pitch yet — open Workbench or deal a Whack card to flesh it out.")}</p>
|
||||
<div style="display:flex;gap:0.5rem;flex-wrap:wrap;padding-top:0.5rem;">
|
||||
<button type="button" class="btn btn-primary" id="btn-triage-promote">🚀 Promote to ${nextStage.toUpperCase()}</button>
|
||||
<button type="button" class="btn btn-secondary" id="btn-triage-mockup">🍌 Mock Up Image</button>
|
||||
<button type="button" class="btn btn-ghost" id="btn-triage-whack">🃏 Whack It</button>
|
||||
<button type="button" class="btn btn-secondary" id="btn-triage-mockup">🖼️ Mock Up Image</button>
|
||||
<button type="button" class="btn btn-ghost" id="btn-triage-whack">💡 Prompt Twist</button>
|
||||
<button type="button" class="btn btn-ghost" id="btn-triage-keep">✓ Keep & Next</button>
|
||||
<button type="button" class="btn btn-danger-ghost" id="btn-triage-archive">📦 Archive</button>
|
||||
</div>
|
||||
@@ -1087,7 +1091,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 with 🍌 Nano Banana 2.1 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 or save a layout from the T-Shirt Compositor!</p>`;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1104,7 +1108,7 @@ function renderDrawerMockupGallery(idea) {
|
||||
${m.editInstruction ? `<small class="muted-label">Edited: "${escapeHtml(m.editInstruction)}"</small>` : ""}
|
||||
<div class="mockup-gallery-actions">
|
||||
<button type="button" class="btn btn-primary btn-xs btn-mk-refine" data-mk-id="${escapeHtml(m.id)}">
|
||||
🍌 Refine with Nano Banana
|
||||
✨ 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
|
||||
@@ -1458,7 +1462,7 @@ function renderDrawerActiveWhackStage() {
|
||||
</label>
|
||||
<div style="display:flex;gap:0.5rem;flex-wrap:wrap;">
|
||||
<button type="button" id="btn-drawer-save-whack" class="btn btn-primary btn-sm">💾 Save Breakthrough</button>
|
||||
<button type="button" id="btn-drawer-whack-to-nb" class="btn btn-secondary btn-sm">🍌 Send Twist to Nano Banana Mockup Studio</button>
|
||||
<button type="button" id="btn-drawer-whack-to-nb" class="btn btn-secondary btn-sm">🖼️ Send Twist to Image Mockup Studio</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2017,22 +2021,42 @@ function bindGlobalEvents() {
|
||||
document.getElementById("nb-edit-instruction-input").value = "";
|
||||
});
|
||||
|
||||
document.getElementById("nb-generator-select")?.addEventListener("change", async (e) => {
|
||||
const chosenModel = e.target.value;
|
||||
state.settings.nanoBananaModel = chosenModel;
|
||||
const settingsSel = document.getElementById("settings-model-select");
|
||||
if (settingsSel) settingsSel.value = chosenModel;
|
||||
await apiFetch("/api/settings", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ nanoBananaModel: chosenModel })
|
||||
});
|
||||
showToast(`Switched Image Mockup Generator to ${e.target.options[e.target.selectedIndex].text.split("—")[0].trim()}`);
|
||||
});
|
||||
|
||||
document.getElementById("btn-generate-nano-banana")?.addEventListener("click", async () => {
|
||||
if (!state.activeIdeaId) return;
|
||||
const btn = document.getElementById("btn-generate-nano-banana");
|
||||
const statusLabel = document.getElementById("nb-generation-status");
|
||||
const selectedModel =
|
||||
document.getElementById("nb-generator-select")?.value ||
|
||||
state.settings.nanoBananaModel ||
|
||||
"codex-image-gen";
|
||||
const template = document.getElementById("nb-template-select").value;
|
||||
const whackCardNumber = document.getElementById("nb-whack-twist-select").value;
|
||||
const prompt = document.getElementById("nb-prompt-input").value.trim();
|
||||
const editInstruction = document.getElementById("nb-edit-instruction-input").value.trim();
|
||||
|
||||
btn.disabled = true;
|
||||
statusLabel.textContent = "🎨 Generating high-res AI mockup via OpenAI Codex (takes ~15–25s)...";
|
||||
statusLabel.textContent =
|
||||
selectedModel === "codex-image-gen"
|
||||
? "🤖 Generating high-res PNG via OpenAI Codex (takes ~15–25s)..."
|
||||
: `✨ Generating mockup via ${selectedModel}...`;
|
||||
|
||||
try {
|
||||
const res = await apiFetch(`/api/ideas/${state.activeIdeaId}/mockups/nano-banana`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
model: selectedModel,
|
||||
template,
|
||||
garmentColor: state.nbGarmentColor,
|
||||
garmentColorName: state.nbGarmentColorName,
|
||||
@@ -2046,8 +2070,8 @@ function bindGlobalEvents() {
|
||||
state.nbEditParentMockup = null;
|
||||
document.getElementById("nb-edit-mode-box").classList.add("hidden");
|
||||
document.getElementById("nb-edit-instruction-input").value = "";
|
||||
statusLabel.textContent = "✓ Saved new Nano Banana mockup to gallery!";
|
||||
showToast("🍌 Nano Banana 2.1 generated & saved your mockup!");
|
||||
statusLabel.textContent = `✓ Saved new mockup (${res.mockup.modelUsed}) to gallery!`;
|
||||
showToast("🖼️ Generated & saved your new mockup!");
|
||||
await loadIdeas();
|
||||
} catch (err) {
|
||||
statusLabel.textContent = `⚠️ ${err.message}`;
|
||||
|
||||
+33
-23
@@ -132,7 +132,7 @@
|
||||
</select>
|
||||
<label class="filter-checkbox">
|
||||
<input type="checkbox" id="filter-has-mockup" />
|
||||
<span>🍌 Has Mockup (<span id="stat-mockups-count">0</span>)</span>
|
||||
<span>🖼️ Has Mockup (<span id="stat-mockups-count">0</span>)</span>
|
||||
</label>
|
||||
<select id="filter-sort" class="filter-select" aria-label="Sort ideas">
|
||||
<option value="score">Sort: Priority Score</option>
|
||||
@@ -378,7 +378,7 @@
|
||||
📝 Workbench & Notes
|
||||
</button>
|
||||
<button type="button" class="drawer-tab" data-drawer-tab="mockup">
|
||||
🎨 Mockup Studio <span id="drawer-mockup-count" class="tab-pill">0</span>
|
||||
🖼️ Image Mockup Studio <span id="drawer-mockup-count" class="tab-pill">0</span>
|
||||
</button>
|
||||
<button type="button" class="drawer-tab" data-drawer-tab="whack">
|
||||
💡 Brainstorm Prompts <span id="drawer-whack-count" class="tab-pill">0</span>
|
||||
@@ -526,17 +526,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB 2: NANO BANANA 2.1 VISUAL MOCKUP STUDIO -->
|
||||
<!-- TAB 2: IMAGE MOCKUP STUDIO -->
|
||||
<div id="drawer-tab-mockup" class="drawer-tab-panel hidden">
|
||||
<div class="mockup-studio-layout">
|
||||
<!-- Left: Generator & Interactive T-Shirt / Product Compositor -->
|
||||
<div class="mockup-controls-pane">
|
||||
<div class="studio-card">
|
||||
<div class="studio-card-header">
|
||||
<h3>🍌 Nano Banana 2.1 AI Mockup Generator</h3>
|
||||
<h3>🖼️ Image Mockup Studio</h3>
|
||||
<button type="button" id="btn-studio-auto-prompt" class="btn btn-ghost btn-xs">✨ Auto-Prompt from Idea</button>
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label for="nb-generator-select">AI Image Generator Engine</label>
|
||||
<select id="nb-generator-select" class="filter-select full-width">
|
||||
<option value="codex-image-gen">🤖 OpenAI Codex (gpt-5.6-sol / Built-in image_gen) — High-Res PNG</option>
|
||||
<option value="gemini-nano-banana-2.1">✨ Google Gemini (Nano Banana 2.1 / Flash-Lite Vector Fallback)</option>
|
||||
<option value="gemini-3-pro-image">🎨 Google Gemini Pro Image (Studio Detail)</option>
|
||||
<option value="gemini-3.1-flash-lite-image">⚡ Google Gemini Flash-Lite Image (Fast Sketch)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-row-2">
|
||||
<div>
|
||||
<label for="nb-template-select">Product / Surface Preset</label>
|
||||
@@ -550,7 +560,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="nb-whack-twist-select">Optional Whack Pack Twist</label>
|
||||
<label for="nb-whack-twist-select">Optional Prompt Twist</label>
|
||||
<select id="nb-whack-twist-select" class="filter-select full-width">
|
||||
<option value="">None (Direct Concept)</option>
|
||||
</select>
|
||||
@@ -579,20 +589,20 @@
|
||||
<!-- Iterative Image-to-Image Edit Banner (Active when a mockup is selected for refinement) -->
|
||||
<div id="nb-edit-mode-box" class="nb-edit-box hidden">
|
||||
<div class="nb-edit-header">
|
||||
<span>🍌 <strong>Iterative Edit Mode:</strong> Refining <em id="nb-edit-target-label">Selected Mockup</em></span>
|
||||
<span>✨ <strong>Iterative Edit Mode:</strong> Refining <em id="nb-edit-target-label">Selected Mockup</em></span>
|
||||
<button type="button" id="btn-nb-clear-edit" class="btn btn-ghost btn-xs">Cancel Edit Mode</button>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="nb-edit-instruction-input"
|
||||
class="text-input"
|
||||
placeholder="Tell Nano Banana what to change (e.g. 'Move graphic to left-chest pocket on a forest green shirt')"
|
||||
placeholder="Describe what to change (e.g. 'Move graphic to left-chest pocket on a forest green shirt')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="studio-action-row">
|
||||
<button type="button" id="btn-generate-nano-banana" class="btn btn-primary">
|
||||
🎨 Generate AI Mockup (Codex / Nano Banana)
|
||||
🖼️ Generate Image Mockup
|
||||
</button>
|
||||
<span id="nb-generation-status" class="muted-label"></span>
|
||||
</div>
|
||||
@@ -680,7 +690,7 @@
|
||||
<div class="studio-card">
|
||||
<div class="studio-card-header">
|
||||
<h3>🖼️ Idea Mockup Gallery</h3>
|
||||
<span class="muted-label">Click 'Refine with 🍌' to iterate on any mockup</span>
|
||||
<span class="muted-label">Click '✨ Refine' to iterate on any mockup</span>
|
||||
</div>
|
||||
<div id="drawer-mockup-gallery" class="drawer-mockup-gallery"></div>
|
||||
</div>
|
||||
@@ -719,37 +729,37 @@
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- MODAL: NANO BANANA 2.1 SETTINGS -->
|
||||
<!-- MODAL: AI GENERATOR & PROFILE SETTINGS -->
|
||||
<div id="settings-modal" class="modal-backdrop hidden">
|
||||
<div class="modal-card">
|
||||
<div class="modal-header">
|
||||
<h3>🍌 Nano Banana 2.1 (Gemini Image API) Settings</h3>
|
||||
<h3>⚙️ AI Mockup Generator & Profile Settings</h3>
|
||||
<button type="button" id="btn-close-settings" class="btn btn-ghost btn-sm">✕</button>
|
||||
</div>
|
||||
<form id="settings-form" class="modal-body">
|
||||
<p class="muted-label">
|
||||
SparkVault uses Google's <strong>Nano Banana 2.1 (`gemini-nano-banana-2.1`)</strong> via the official `@google/genai` Interactions API for text-to-image product mockups and multi-turn conversational image editing.
|
||||
Choose which AI generator powers the <strong>Image Mockup Studio</strong> (you can also switch generators anytime directly inside the Studio tab).
|
||||
</p>
|
||||
<div class="field-group">
|
||||
<label for="settings-api-key">Gemini API Key (`GEMINI_API_KEY`)</label>
|
||||
<label for="settings-model-select">Default Image Mockup Generator</label>
|
||||
<select id="settings-model-select" class="filter-select full-width">
|
||||
<option value="codex-image-gen">🤖 OpenAI Codex (gpt-5.6-sol + Built-in image_gen) [Recommended]</option>
|
||||
<option value="gemini-nano-banana-2.1">✨ Google Gemini (Nano Banana 2.1 / Flash-Lite Vector Fallback)</option>
|
||||
<option value="gemini-3-pro-image">🎨 Google Gemini Pro Image (High-Detail Studio)</option>
|
||||
<option value="gemini-3.1-flash-lite-image">⚡ Google Gemini Flash-Lite Image (Fast Sketch)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="settings-api-key">Optional Google Gemini API Key (`GEMINI_API_KEY`)</label>
|
||||
<input
|
||||
type="password"
|
||||
id="settings-api-key"
|
||||
class="text-input"
|
||||
placeholder="Paste your AI Studio / Gemini API key (AIza...)"
|
||||
placeholder="Only needed if using Google Gemini models (AIza...)"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<small id="settings-key-status" class="muted-label"></small>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="settings-model-select">AI Mockup Engine / Model</label>
|
||||
<select id="settings-model-select" class="filter-select full-width">
|
||||
<option value="codex-image-gen">codex-image-gen — OpenAI Codex (gpt-5.6-sol + Built-in image_gen) [Recommended]</option>
|
||||
<option value="gemini-nano-banana-2.1">gemini-nano-banana-2.1 — Nano Banana 2.1 (Google Gemini)</option>
|
||||
<option value="gemini-3-pro-image">gemini-3-pro-image — Nano Banana Pro (High-Detail Studio)</option>
|
||||
<option value="gemini-3.1-flash-lite-image">gemini-3.1-flash-lite-image — Nano Banana 2 Lite (Ultra-Fast)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label for="settings-user-background">🎲 Personal Background & Tastes (Used by '🎲 Random Idea')</label>
|
||||
<textarea
|
||||
|
||||
Reference in new issue
Block a user