Add Submit to Antigravity or Codex for Development modal with model picker and deep links
This commit is contained in:
1 parent
b5af03b628
commit
92c5039bcb
6 files changed
+752
-1
No files matched your search
+239
@@ -1847,6 +1847,104 @@ function stageClipModalImage(dataUrl) {
|
||||
}
|
||||
}
|
||||
|
||||
const DEV_TARGETS = {
|
||||
antigravity: {
|
||||
id: "antigravity",
|
||||
label: "🛸 Google Antigravity",
|
||||
defaultModel: "gemini-3.1-pro",
|
||||
models: [
|
||||
{ id: "gemini-3.1-pro", label: "Gemini 3.1 Pro (Recommended — Full-Stack & Architecture)" },
|
||||
{ id: "gemini-3.0-pro", label: "Gemini 3.0 Pro (Deep Reasoning & Complex Refactors)" },
|
||||
{ id: "gemini-3.1-flash", label: "Gemini 3.1 Flash (Fast Iteration & UI Prototyping)" },
|
||||
{ id: "gemini-3.1-flash-lite", label: "Gemini 3.1 Flash-Lite (Lightweight & Rapid Tasks)" },
|
||||
{ id: "claude-sonnet-4.5", label: "Claude 4.5 Sonnet (via Antigravity Model Selector)" },
|
||||
{ id: "claude-opus-4.5", label: "Claude 4.5 Opus (via Antigravity Model Selector)" }
|
||||
]
|
||||
},
|
||||
codex: {
|
||||
id: "codex",
|
||||
label: "🤖 OpenAI Codex",
|
||||
defaultModel: "gpt-5.6-sol",
|
||||
models: [
|
||||
{ id: "gpt-5.6-sol", label: "GPT-5.6-Sol (Recommended — Active Codex Default)" },
|
||||
{ id: "gpt-5.3-codex", label: "GPT-5.3-Codex (Specialized Agentic Coding)" },
|
||||
{ id: "gpt-5.4", label: "GPT-5.4 (Flagship Reasoning & Implementation)" },
|
||||
{ id: "o3", label: "OpenAI o3 (Deep Architecture & Debugging)" },
|
||||
{ id: "codex-mini-latest", label: "Codex Mini (Fast Lightweight Execution)" }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let activeDevTarget = "antigravity";
|
||||
|
||||
function populateDevModelOptions(targetApp) {
|
||||
const meta = DEV_TARGETS[targetApp] || DEV_TARGETS.antigravity;
|
||||
const sel = document.getElementById("dev-model-select");
|
||||
if (!sel) return;
|
||||
sel.innerHTML = meta.models
|
||||
.map((m) => `<option value="${escapeHtml(m.id)}">${escapeHtml(m.label)}</option>`)
|
||||
.join("");
|
||||
sel.value = meta.defaultModel;
|
||||
}
|
||||
|
||||
async function refreshDevSpecPreview() {
|
||||
if (!state.activeIdeaId) return;
|
||||
const model = document.getElementById("dev-model-select")?.value || "";
|
||||
const includeHomelab = document.getElementById("dev-include-homelab")?.checked !== false;
|
||||
const textarea = document.getElementById("dev-spec-preview-textarea");
|
||||
if (!textarea) return;
|
||||
|
||||
const params = new URLSearchParams({
|
||||
target: activeDevTarget,
|
||||
model,
|
||||
includeHomelab: String(includeHomelab)
|
||||
});
|
||||
try {
|
||||
const res = await fetch(`/api/ideas/${state.activeIdeaId}/spec.md?${params.toString()}`);
|
||||
if (res.ok) {
|
||||
textarea.value = await res.text();
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("Failed to load spec preview:", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function openDevHandoffModal(defaultTarget = "antigravity") {
|
||||
if (!state.activeIdeaId || !state.activeIdea) return;
|
||||
activeDevTarget = defaultTarget;
|
||||
|
||||
const modal = document.getElementById("dev-handoff-modal");
|
||||
if (!modal) return;
|
||||
|
||||
document.querySelectorAll("#dev-target-pills [data-dev-target]").forEach((btn) => {
|
||||
btn.classList.toggle("active", btn.dataset.devTarget === activeDevTarget);
|
||||
});
|
||||
|
||||
populateDevModelOptions(activeDevTarget);
|
||||
|
||||
const serverCodexWrap = document.getElementById("dev-server-codex-wrap");
|
||||
if (serverCodexWrap) {
|
||||
serverCodexWrap.classList.toggle("hidden", activeDevTarget !== "codex");
|
||||
}
|
||||
|
||||
const submitBtn = document.getElementById("btn-submit-dev-handoff");
|
||||
if (submitBtn) {
|
||||
submitBtn.textContent =
|
||||
activeDevTarget === "codex"
|
||||
? "🚀 Submit & Open in OpenAI Codex"
|
||||
: "🚀 Submit & Launch in Antigravity";
|
||||
}
|
||||
|
||||
const homelabCb = document.getElementById("dev-include-homelab");
|
||||
if (homelabCb) {
|
||||
homelabCb.checked = state.activeIdea.category === "project" || !isCurrentProductTShirt();
|
||||
}
|
||||
|
||||
document.getElementById("dev-handoff-result-box")?.classList.add("hidden");
|
||||
modal.classList.remove("hidden");
|
||||
await refreshDevSpecPreview();
|
||||
}
|
||||
|
||||
function openExternalClipModal({ ideaId = null, prefillTitle = "", prefillText = "", prefillUrl = "" } = {}) {
|
||||
const modal = document.getElementById("external-clip-modal");
|
||||
const targetSel = document.getElementById("clip-target-idea");
|
||||
@@ -1921,6 +2019,7 @@ function bindGlobalEvents() {
|
||||
} else if (e.key === "Escape") {
|
||||
document.getElementById("lightbox-modal")?.classList.add("hidden");
|
||||
document.getElementById("settings-modal")?.classList.add("hidden");
|
||||
document.getElementById("dev-handoff-modal")?.classList.add("hidden");
|
||||
closeIdeaDrawer();
|
||||
}
|
||||
});
|
||||
@@ -2681,6 +2780,146 @@ function bindGlobalEvents() {
|
||||
}
|
||||
});
|
||||
|
||||
// --- SUBMIT TO ANTIGRAVITY OR CODEX FOR DEVELOPMENT EVENTS ---
|
||||
document.getElementById("btn-open-dev-handoff")?.addEventListener("click", () => {
|
||||
openDevHandoffModal(activeDevTarget || "antigravity");
|
||||
});
|
||||
document.getElementById("btn-close-dev-modal")?.addEventListener("click", () => {
|
||||
document.getElementById("dev-handoff-modal")?.classList.add("hidden");
|
||||
});
|
||||
document.getElementById("btn-cancel-dev-modal")?.addEventListener("click", () => {
|
||||
document.getElementById("dev-handoff-modal")?.classList.add("hidden");
|
||||
});
|
||||
|
||||
document.querySelectorAll("#dev-target-pills [data-dev-target]").forEach((btn) => {
|
||||
btn.addEventListener("click", async () => {
|
||||
activeDevTarget = btn.dataset.devTarget;
|
||||
document.querySelectorAll("#dev-target-pills [data-dev-target]").forEach((b) =>
|
||||
b.classList.toggle("active", b === btn)
|
||||
);
|
||||
populateDevModelOptions(activeDevTarget);
|
||||
document.getElementById("dev-server-codex-wrap")?.classList.toggle("hidden", activeDevTarget !== "codex");
|
||||
const submitBtn = document.getElementById("btn-submit-dev-handoff");
|
||||
if (submitBtn) {
|
||||
submitBtn.textContent =
|
||||
activeDevTarget === "codex"
|
||||
? "🚀 Submit & Open in OpenAI Codex"
|
||||
: "🚀 Submit & Launch in Antigravity";
|
||||
}
|
||||
await refreshDevSpecPreview();
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("dev-model-select")?.addEventListener("change", refreshDevSpecPreview);
|
||||
document.getElementById("dev-include-homelab")?.addEventListener("change", refreshDevSpecPreview);
|
||||
|
||||
document.getElementById("btn-copy-dev-spec")?.addEventListener("click", async () => {
|
||||
const text = document.getElementById("dev-spec-preview-textarea")?.value || "";
|
||||
if (!text) return;
|
||||
await navigator.clipboard.writeText(text);
|
||||
showToast("📋 Copied full development specification to clipboard!");
|
||||
});
|
||||
|
||||
document.getElementById("btn-download-dev-spec")?.addEventListener("click", () => {
|
||||
if (!state.activeIdeaId) return;
|
||||
const model = document.getElementById("dev-model-select")?.value || "";
|
||||
const includeHomelab = document.getElementById("dev-include-homelab")?.checked !== false;
|
||||
window.open(
|
||||
`/api/ideas/${state.activeIdeaId}/spec.md?target=${encodeURIComponent(activeDevTarget)}&model=${encodeURIComponent(model)}&includeHomelab=${includeHomelab}`,
|
||||
"_blank"
|
||||
);
|
||||
});
|
||||
|
||||
document.getElementById("dev-handoff-form")?.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
if (!state.activeIdeaId) return;
|
||||
|
||||
const submitBtn = document.getElementById("btn-submit-dev-handoff");
|
||||
const resultBox = document.getElementById("dev-handoff-result-box");
|
||||
const model = document.getElementById("dev-model-select")?.value || "";
|
||||
const customPrompt = document.getElementById("dev-spec-preview-textarea")?.value || "";
|
||||
const includeHomelab = document.getElementById("dev-include-homelab")?.checked !== false;
|
||||
const moveStatusToBuilding = document.getElementById("dev-move-building")?.checked !== false;
|
||||
const executeServerCodex =
|
||||
activeDevTarget === "codex" && Boolean(document.getElementById("dev-run-server-codex")?.checked);
|
||||
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = executeServerCodex
|
||||
? "🤖 Running Codex Blueprint & Submitting..."
|
||||
: "🚀 Packaging & Launching...";
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. Copy full specification to clipboard so it's immediately ready to paste (Ctrl+V) in Antigravity or Codex
|
||||
try {
|
||||
await navigator.clipboard.writeText(customPrompt);
|
||||
} catch {
|
||||
// Ignore clipboard permission errors if non-HTTPS context
|
||||
}
|
||||
|
||||
// 2. Persist handoff package & workspace on server
|
||||
const res = await apiFetch(`/api/ideas/${state.activeIdeaId}/dev-handoff`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
targetApp: activeDevTarget,
|
||||
model,
|
||||
customPrompt,
|
||||
includeHomelab,
|
||||
moveStatusToBuilding,
|
||||
executeServerCodex
|
||||
})
|
||||
});
|
||||
|
||||
state.activeIdea = res.idea;
|
||||
const handoff = res.handoff;
|
||||
|
||||
if (resultBox) {
|
||||
resultBox.classList.remove("hidden");
|
||||
resultBox.innerHTML = `
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:0.5rem;">
|
||||
<strong>✅ Ready in ${escapeHtml(handoff.targetLabel)} (${escapeHtml(handoff.model)})</strong>
|
||||
<div style="display:flex;gap:0.4rem;">
|
||||
<a href="${escapeHtml(handoff.deepLinkUrl)}" class="btn btn-primary btn-xs">
|
||||
🚀 Open ${activeDevTarget === "codex" ? "Codex App" : "Antigravity App"}
|
||||
</a>
|
||||
<a href="${escapeHtml(handoff.specUrl)}" target="_blank" rel="noopener noreferrer" class="btn btn-ghost btn-xs">
|
||||
📄 View SPEC.md
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="muted-label" style="margin-top:0.25rem;">
|
||||
Full specification has been <strong>copied to your clipboard (<kbd>Ctrl+V</kbd>)</strong> and saved to server workspace <code>${escapeHtml(handoff.slug)}</code>.
|
||||
</p>
|
||||
${
|
||||
handoff.codexExecution?.output
|
||||
? `<pre style="max-height:160px;overflow:auto;background:var(--bg-base);padding:0.5rem;border-radius:6px;font-size:0.75rem;white-space:pre-wrap;margin-top:0.35rem;">${escapeHtml(handoff.codexExecution.output)}</pre>`
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
showToast(`🚀 Copied full spec & launching ${handoff.targetLabel} (${handoff.model})!`);
|
||||
await loadIdeas();
|
||||
renderDrawerContent();
|
||||
|
||||
// 3. Trigger the OS deep link to open Antigravity or Codex Desktop App
|
||||
if (handoff.deepLinkUrl) {
|
||||
window.location.href = handoff.deepLinkUrl;
|
||||
}
|
||||
} catch (err) {
|
||||
showToast(`⚠️ ${err.message}`);
|
||||
} finally {
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent =
|
||||
activeDevTarget === "codex"
|
||||
? "🚀 Submit & Open in OpenAI Codex"
|
||||
: "🚀 Submit & Launch in Antigravity";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Global Clipboard Paste (Ctrl+V) for screenshots & copied images
|
||||
window.addEventListener("paste", async (e) => {
|
||||
const items = e.clipboardData?.items;
|
||||
|
||||
Reference in new issue
Block a user