Fix Antigravity model list and enhance clipboard-guided handoff UX

This commit is contained in:
airent106 committed 2026-10-29 22:43:05 -06:00
1 parent 0467eebf0e
commit 29ef28b7cf
4 files changed
+124 -23

No files matched your search

+92 -16
View File
@@ -1851,14 +1851,13 @@ const DEV_TARGETS = {
antigravity: {
id: "antigravity",
label: "🛸 Google Antigravity",
defaultModel: "gemini-3.1-pro",
defaultModel: "gemini-3.8-flash",
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)" }
{ id: "gemini-4-argon", label: "Gemini 4 Argon (Medium)" },
{ id: "gemini-3.8-flash", label: "Gemini 3.8 Flash (Medium)" },
{ id: "claude-opus-5.5", label: "Claude Opus 5.5 (Medium)" },
{ id: "claude-sonnet-5.5", label: "Claude Sonnet 5.5 (Medium)" },
{ id: "gpt-oss-120b", label: "GPT-OSS 120B (Medium)" }
]
},
codex: {
@@ -1875,6 +1874,33 @@ const DEV_TARGETS = {
}
};
async function copyTextToClipboard(text) {
if (!text) return false;
if (navigator.clipboard && window.isSecureContext) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch {
// Fall through to fallback
}
}
try {
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
const successful = document.execCommand("copy");
textArea.remove();
return successful;
} catch {
return false;
}
}
let activeDevTarget = "antigravity";
function populateDevModelOptions(targetApp) {
@@ -1940,6 +1966,14 @@ async function openDevHandoffModal(defaultTarget = "antigravity") {
homelabCb.checked = state.activeIdea.category === "project" || !isCurrentProductTShirt();
}
const instructionNote = document.getElementById("dev-handoff-instruction-note");
if (instructionNote) {
instructionNote.innerHTML =
activeDevTarget === "codex"
? "🤖 Submitting opens OpenAI Codex with the prompt preloaded and saves the full spec to your workspace."
: "🛸 Submitting switches focus to Antigravity and copies the complete project specification to your clipboard. Simply press <kbd>Ctrl+V</kbd> in Antigravity's chat to start coding.";
}
document.getElementById("dev-handoff-result-box")?.classList.add("hidden");
modal.classList.remove("hidden");
await refreshDevSpecPreview();
@@ -2806,6 +2840,13 @@ function bindGlobalEvents() {
? "🚀 Submit & Open in OpenAI Codex"
: "🚀 Submit & Launch in Antigravity";
}
const instructionNote = document.getElementById("dev-handoff-instruction-note");
if (instructionNote) {
instructionNote.innerHTML =
activeDevTarget === "codex"
? "🤖 Submitting opens OpenAI Codex with the prompt preloaded and saves the full spec to your workspace."
: "🛸 Submitting switches focus to Antigravity and copies the complete project specification to your clipboard. Simply press <kbd>Ctrl+V</kbd> in Antigravity's chat to start coding.";
}
await refreshDevSpecPreview();
});
});
@@ -2816,8 +2857,8 @@ function bindGlobalEvents() {
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!");
const ok = await copyTextToClipboard(text);
showToast(ok ? "📋 Copied full development specification to clipboard!" : "⚠️ Unable to auto-copy; please copy text manually.");
});
document.getElementById("btn-download-dev-spec")?.addEventListener("click", () => {
@@ -2852,11 +2893,7 @@ function bindGlobalEvents() {
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
}
await copyTextToClipboard(customPrompt);
// 2. Persist handoff package & workspace on server
const res = await apiFetch(`/api/ideas/${state.activeIdeaId}/dev-handoff`, {
@@ -2876,12 +2913,50 @@ function bindGlobalEvents() {
if (resultBox) {
resultBox.classList.remove("hidden");
const modelName = handoff.modelLabel || handoff.model;
if (activeDevTarget === "antigravity") {
resultBox.innerHTML = `
<div style="background:linear-gradient(135deg, rgba(76,140,245,0.12), rgba(52,199,89,0.12)); border:1px solid rgba(76,140,245,0.35); border-radius:8px; padding:0.85rem; margin-top:0.4rem;">
<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:0.5rem;">
<div style="font-weight:700;font-size:0.95rem;display:flex;align-items:center;gap:0.4rem;">
<span>🛸</span> Switched to Google Antigravity!
</div>
<div style="display:flex;gap:0.4rem;flex-wrap:wrap;">
<button type="button" id="btn-copy-dev-spec-again" class="btn btn-secondary btn-xs">
📋 Copy Spec Again
</button>
<a href="${escapeHtml(handoff.deepLinkUrl)}" class="btn btn-primary btn-xs">
🛸 Re-Focus Antigravity
</a>
<a href="${escapeHtml(handoff.specUrl)}" target="_blank" rel="noopener noreferrer" class="btn btn-ghost btn-xs">
📄 View SPEC.md
</a>
</div>
</div>
<div style="margin-top:0.6rem;padding:0.65rem 0.8rem;background:var(--bg-card);border-radius:6px;border:1px dashed var(--border-color);font-size:0.85rem;line-height:1.5;">
<div style="color:var(--text-main);font-weight:600;">
👉 Ready to develop with <strong>${escapeHtml(modelName)}</strong>!
</div>
<div style="margin-top:0.25rem;color:var(--text-secondary);">
The full specification for <strong>${escapeHtml(state.activeIdea.title)}</strong> has been loaded onto your clipboard! Since Antigravity requires starting or selecting your conversation, simply click into Antigravity's chat bar and press <kbd style="background:var(--bg-base);padding:2px 6px;border-radius:4px;border:1px solid var(--border-color);font-weight:700;">Ctrl+V</kbd> (or <kbd style="background:var(--bg-base);padding:2px 6px;border-radius:4px;border:1px solid var(--border-color);font-weight:700;">Cmd+V</kbd>) to paste the prompt and begin.
</div>
</div>
</div>
`;
document.getElementById("btn-copy-dev-spec-again")?.addEventListener("click", async () => {
const reCopied = await copyTextToClipboard(customPrompt);
showToast(reCopied ? "📋 Specification copied to clipboard! Press Ctrl+V in Antigravity." : "⚠️ Please copy manually from the preview below.");
});
} else {
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"}
🚀 Open Codex App
</a>
<a href="${escapeHtml(handoff.specUrl)}" target="_blank" rel="noopener noreferrer" class="btn btn-ghost btn-xs">
📄 View SPEC.md
@@ -2898,8 +2973,9 @@ function bindGlobalEvents() {
}
`;
}
}
showToast(`🚀 Copied full spec & launching ${handoff.targetLabel} (${handoff.model})!`);
showToast(`🛸 Copied spec & launching Antigravity! Paste with Ctrl+V.`);
await loadIdeas();
renderDrawerContent();
+4
View File
@@ -936,6 +936,10 @@
<div id="dev-handoff-result-box" class="nb-edit-box hidden"></div>
<div id="dev-handoff-instruction-note" class="muted-label" style="font-size:0.8rem; padding:0.4rem 0.6rem; background:rgba(76,140,245,0.08); border-radius:6px; border-left:3px solid var(--accent-primary); margin-top:0.4rem;">
🛸 Submitting switches focus to Antigravity and copies the complete project specification to your clipboard. Simply press <kbd>Ctrl+V</kbd> in Antigravity's chat to start coding.
</div>
<div class="modal-footer">
<button type="button" id="btn-cancel-dev-modal" class="btn btn-ghost">Cancel</button>
<button type="submit" id="btn-submit-dev-handoff" class="btn btn-primary">
+10 -7
View File
@@ -424,14 +424,13 @@ export const DEV_HANDOFF_TARGETS = {
id: "antigravity",
label: "🛸 Google Antigravity",
protocol: "antigravity://",
defaultModel: "gemini-3.1-pro",
defaultModel: "gemini-3.8-flash",
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)" }
{ id: "gemini-4-argon", label: "Gemini 4 Argon (Medium)" },
{ id: "gemini-3.8-flash", label: "Gemini 3.8 Flash (Medium)" },
{ id: "claude-opus-5.5", label: "Claude Opus 5.5 (Medium)" },
{ id: "claude-sonnet-5.5", label: "Claude Sonnet 5.5 (Medium)" },
{ id: "gpt-oss-120b", label: "GPT-OSS 120B (Medium)" }
]
},
codex: {
@@ -688,10 +687,14 @@ export async function executeDevelopmentHandoff({
}
}
const modelObj = targetMeta.models.find((m) => m.id === chosenModel);
const modelLabel = modelObj ? modelObj.label : chosenModel;
return {
targetApp,
targetLabel: targetMeta.label,
model: chosenModel,
modelLabel,
slug,
specMarkdown: compiledSpec,
specUrl,
+18
View File
@@ -269,6 +269,24 @@ test("Development Handoff endpoint compiles full Spark spec and submits to Antig
assert.equal(handoffData.handoff.model, "gpt-5.6-sol");
assert.match(handoffData.handoff.deepLinkUrl, /^codex:\/\/threads\/new/);
assert.equal(handoffData.idea.status, "building");
// Verify Antigravity handoff with updated models (Gemini 3.8 Flash default)
const agyRes = await fetch(`${baseUrl}/api/ideas/1/dev-handoff`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
targetApp: "antigravity",
model: "gemini-3.8-flash",
includeHomelab: true,
moveStatusToBuilding: true
})
});
assert.equal(agyRes.status, 200);
const agyData = await agyRes.json();
assert.equal(agyData.handoff.targetApp, "antigravity");
assert.equal(agyData.handoff.model, "gemini-3.8-flash");
assert.equal(agyData.handoff.modelLabel, "Gemini 3.8 Flash (Medium)");
assert.match(agyData.handoff.deepLinkUrl, /^antigravity:\/\/new\?model=gemini-3\.8-flash/);
});