feat(naming): add Suggest Names button for ideas and tee compositor
This commit is contained in:
1 parent
25d24a53a6
commit
6c6867b49c
6 files changed
+276
-7
No files matched your search
+101
-4
@@ -168,8 +168,8 @@ async function generateIdeaViaCodex(prompt) {
|
||||
}
|
||||
|
||||
const parsed = extractJsonObject(lastAgentText);
|
||||
if (!parsed || !parsed.title) {
|
||||
throw new Error(`Codex exited (${exitCode}) without returning valid idea JSON.`);
|
||||
if (!parsed || (!parsed.title && !Array.isArray(parsed.suggestions))) {
|
||||
throw new Error(`Codex exited (${exitCode}) without returning valid JSON.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
@@ -181,8 +181,8 @@ async function generateIdeaViaGemini(apiKey, prompt) {
|
||||
input: prompt
|
||||
});
|
||||
const parsed = extractJsonObject(interaction.output_text);
|
||||
if (!parsed || !parsed.title) {
|
||||
throw new Error("Gemini completed without returning valid idea JSON.");
|
||||
if (!parsed || (!parsed.title && !Array.isArray(parsed.suggestions))) {
|
||||
throw new Error("Gemini completed without returning valid JSON.");
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
@@ -310,3 +310,100 @@ export async function generatePersonalizedRandomIdea({
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates 6 creative name / title & slogan suggestions for a given idea
|
||||
* across distinct naming angles (Punchy Brand, Vintage Club/Badge, Clever/Witty,
|
||||
* Minimalist Editorial, Graphic Slogan, Technical/Maker).
|
||||
*/
|
||||
export async function generateNameSuggestions({
|
||||
idea,
|
||||
userBackground = DEFAULT_USER_BACKGROUND,
|
||||
geminiApiKey = ""
|
||||
}) {
|
||||
const prompt = [
|
||||
"You are a sharp creative director, brand namer, and apparel/product copywriter.",
|
||||
`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).",
|
||||
"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)"
|
||||
}
|
||||
]
|
||||
})
|
||||
].join("\n\n");
|
||||
|
||||
let resultObj = null;
|
||||
let generatorUsed = "Smart Name Generator";
|
||||
|
||||
if (isCodexConfigured()) {
|
||||
try {
|
||||
resultObj = await generateIdeaViaCodex(prompt);
|
||||
generatorUsed = "OpenAI Codex (gpt-5.6-sol)";
|
||||
} catch (err) {
|
||||
console.warn("Codex name suggestion fallback:", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!resultObj && geminiApiKey) {
|
||||
try {
|
||||
resultObj = await generateIdeaViaGemini(geminiApiKey, prompt);
|
||||
generatorUsed = "Gemini 3.1 Flash-Lite";
|
||||
} catch (err) {
|
||||
console.warn("Gemini name suggestion fallback:", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!resultObj || !Array.isArray(resultObj.suggestions) || resultObj.suggestions.length === 0) {
|
||||
const coreWords = String(idea.title || "Spark")
|
||||
.replace(/—.*$/, "")
|
||||
.replace(/[^\w\s-]/g, "")
|
||||
.trim();
|
||||
const shortCore = coreWords.split(/\s+/).slice(0, 3).join(" ") || "Alpine Spark";
|
||||
resultObj = {
|
||||
suggestions: [
|
||||
{
|
||||
name: `${shortCore} Society`,
|
||||
tagline: `EST. 1984 • HIGH ALTITUDE DIVISION`,
|
||||
style: "Vintage Club"
|
||||
},
|
||||
{
|
||||
name: `The ${shortCore} Dept.`,
|
||||
tagline: `FIELD TESTED & SELF-HOSTED`,
|
||||
style: "Heritage Badge"
|
||||
},
|
||||
{
|
||||
name: `${shortCore} — Heavyweight Edition`,
|
||||
tagline: `BUILT FOR LATE NIGHTS & EARLY SUMMITS`,
|
||||
style: "Apparel / Merch"
|
||||
},
|
||||
{
|
||||
name: `Project ${shortCore.split(" ")[0] || "Pulse"}`,
|
||||
tagline: `ZERO FRICTION • LOCAL FIRST`,
|
||||
style: "Maker Codename"
|
||||
},
|
||||
{
|
||||
name: `${shortCore} Co. Outpost`,
|
||||
tagline: `CRAFTED IN COLORADO • NO SHORTCUTS`,
|
||||
style: "Outfitter Crest"
|
||||
},
|
||||
{
|
||||
name: ` Midnight ${shortCore} Club`,
|
||||
tagline: `IF IT AIN'T BROKE, UPGRADE IT`,
|
||||
style: "Witty / Homelab"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
generatorUsed,
|
||||
suggestions: resultObj.suggestions.slice(0, 6)
|
||||
};
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user