Once the brief is visible in the white space, click “Generate Prompt” and it will be merged with the levels of hallucination and idea type you selected.
NOTE: be specific about additional details to ensure the AI understands your needs.
Creative Brief
Ideas & Feedback Loop
Input AI Response
Paste your AI response here and click Process to review ideas and provide feedback.
AI Response
Approved Ideas
No approved ideas yet
IX AI Instructions
Stage 1: Super Prompt Generation
Step 1: Select Hallucination Level
Choose the level of creativity for the AI's ideas (None, Microdose, Buzzed, Tripping).
Step 2: Choose AI Engine
Select your preferred AI engine (e.g., Grok, ChatGPT, Perplexity) for generating ideas.
Step 3: Idea Type
Specify the type of idea you want, then click Submit to proceed.
Step 4: Generate Prompt
Type, use the Brief Builder, or upload a PDF brief into the creative brief area, then click "Generate Prompt" to copy a prompt into your clipboard. A pop-up will allow you to open your selected AI engine in a new tab. Paste the prompt there and copy the full response.
Stage 2: Ideas & Feedback Loop
Step 5: Input AI Response
Paste the entire AI-generated response into the "AI Response" text area. Click "Process" to display the ideas for review.
Step 6: Provide Feedback
Review the ideas, add feedback in the text areas, check "Approve" for ideas you want to keep, and click "Generate Feedback Prompt" to create a new prompt. Paste it back into the same AI tool thread to refine ideas.
Step 7: Export Approved Ideas
Approved ideas appear on the right; click "Generate PDF" to save them, including any notes added in the idea details.
IX AI Tips
This app is optimized for the Google Chrome browser.
The ideas generated will hopefully inspire you to come up with even better ideas.
If an idea isn’t right, explain why in the feedback box, and the AI will learn your preferences.
For best experience, expand your browser as much as possible.
IX AI Suggestion Box
An email to willb@ideasiclex.com with the subject "IX AI Suggestions" has been opened. Please send your suggestions!
Brief Builder
What are we promoting?
Who is the target audience?
What is the key message?
What else should the AI know?
Open AI Engine
Click below to be magically transported to . Paste your prompt, copy the entire response, and return here.
Feedback Prompt Instructions
The feedback prompt has been copied to your clipboard. Please switch to the existing tab, paste the prompt into the same thread, copy the response, and return here.
Add any notes here:
//
let sessionIdeas = [];
let lastIdeas = [];
let lastHallucinationLevel = 'none';
let lastAiEngine = 'Grok';
let lastBrief = '';
let lastIdeaType = '';
let lastGeneralFeedback = '';
let stage2Active = false;
let suppressWarning = false;
let aiEngineWindow = null;
// Global DOM elements
let stage1, stage2, notification, generatePromptBtn, clearInputBtn, processBtn, generateFeedbackBtn,
miniCardsContainer, generatePdfBtn, noIdeas, modal, modalContent, modalClose, modalApproveCheckbox,
modalIdeaNotes, approvedCount, backToInput, navInstructions, navTips, navSuggestionBox,
instructionsModal, tipsModal, suggestionBoxModal, aiEngineModal, openAiEngineBtn, aiEngineName,
feedbackPromptModal, closeFeedbackPromptModal, feedbackAiEngineName, briefBuilderBtn, uploadBriefBtn,
uploadBriefInput, briefBuilderModal, saveBriefBuilder, cancelBriefBuilder, briefBuilderLink,
uploadBriefLink, ideaTypeInput, submitIdeaTypeBtn, briefInput;
function initializeDOMElements() {
console.log('Initializing DOM elements at:', new Date().toLocaleString());
const elements = {
stage1: 'stage1',
stage2: 'stage2',
notification: 'notification',
generatePromptBtn: 'generatePromptBtn',
clearInputBtn: 'clearInputBtn',
processBtn: 'processBtn',
generateFeedbackBtn: 'generateFeedbackBtn',
miniCardsContainer: 'miniCardsContainer',
generatePdfBtn: 'generatePdfBtn',
noIdeas: 'noIdeas',
modal: 'ideaModal',
modalContent: 'modalIdeaContent',
modalClose: 'modalClose',
modalApproveCheckbox: 'modalApproveCheckbox',
modalIdeaNotes: 'modalIdeaNotes',
approvedCount: 'approvedCount',
backToInput: 'backToInput',
navInstructions: 'navInstructions',
navTips: 'navTips',
navSuggestionBox: 'navSuggestionBox',
instructionsModal: 'instructionsModal',
tipsModal: 'tipsModal',
suggestionBoxModal: 'suggestionBoxModal',
aiEngineModal: 'aiEngineModal',
openAiEngineBtn: 'openAiEngineBtn',
aiEngineName: 'aiEngineName',
feedbackPromptModal: 'feedbackPromptModal',
closeFeedbackPromptModal: 'closeFeedbackPromptModal',
feedbackAiEngineName: 'feedbackAiEngineName',
briefBuilderBtn: 'briefBuilderBtn',
uploadBriefBtn: 'uploadBriefBtn',
uploadBriefInput: 'uploadBriefInput',
briefBuilderModal: 'briefBuilderModal',
saveBriefBuilder: 'saveBriefBuilder',
cancelBriefBuilder: 'cancelBriefBuilder',
briefBuilderLink: 'briefBuilderLink',
uploadBriefLink: 'uploadBriefLink',
ideaTypeInput: 'ideaTypeInput',
submitIdeaTypeBtn: 'submitIdeaTypeBtn',
briefInput: 'briefInput'
};
for (const [key, id] of Object.entries(elements)) {
window[key] = document.getElementById(id);
if (!window[key]) {
console.warn(`${key} (ID: ${id}) not found in DOM`);
} else {
console.log(`${key} found in DOM`);
}
}
return Object.values(elements).every(id => document.getElementById(id));
}
//
function showNotification(message, type, duration = 3000) {
console.log(`Showing notification: ${message}, Type: ${type}, Duration: ${duration}`);
if (notification) {
notification.textContent = message;
notification.className = `notification ${type}`;
notification.style.display = 'block';
setTimeout(() => {
if (notification) notification.style.display = 'none';
console.log(`Hiding notification: ${message}`);
}, duration);
} else {
console.error('Notification element not found, using alert');
alert(message);
}
}
//
function clearInfoBoxes(containerId) {
const container = document.getElementById(containerId);
if (container) {
const infoBoxes = container.getElementsByClassName('info-box');
while (infoBoxes.length > 0) {
infoBoxes[0].remove();
console.log(`Removed info-box in ${containerId}`);
}
}
}
//
function resetApp(clearBrief = false) {
console.log('Resetting app, clearBrief:', clearBrief);
sessionIdeas = [];
lastIdeas = [];
lastGeneralFeedback = '';
stage2Active = false;
aiEngineWindow = null;
if (clearBrief) {
if (briefInput) briefInput.value = '';
if (ideaTypeInput) {
ideaTypeInput.value = '';
ideaTypeInput.disabled = false;
}
lastBrief = '';
lastIdeaType = '';
lastHallucinationLevel = 'none';
lastAiEngine = 'Grok';
const noneRadio = document.querySelector('input[name="hallucinationLevel"][value="none"]');
const grokRadio = document.querySelector('input[name="aiEngine"][value="Grok"]');
if (noneRadio) noneRadio.checked = true;
if (grokRadio) grokRadio.checked = true;
if (briefInput) briefInput.disabled = true;
if (briefBuilderBtn) briefBuilderBtn.disabled = true;
if (uploadBriefBtn) uploadBriefBtn.disabled = true;
if (generatePromptBtn) generatePromptBtn.disabled = true;
if (clearInputBtn) clearInputBtn.disabled = true;
if (submitIdeaTypeBtn && ideaTypeInput) {
submitIdeaTypeBtn.disabled = !ideaTypeInput.value.trim();
submitIdeaTypeBtn.title = ideaTypeInput.value.trim() ? 'Submit idea type' : 'Enter an idea type to submit';
const inputEvent = new Event('input', { bubbles: true });
ideaTypeInput.dispatchEvent(inputEvent);
}
}
const llmResponse = document.getElementById('llmResponse');
if (llmResponse) llmResponse.value = '';
const ideasContainer = document.getElementById('ideasContainer');
if (ideasContainer) ideasContainer.innerHTML = '';
const stage2Actions = document.getElementById('stage2Actions');
if (stage2Actions) stage2Actions.style.display = 'none';
const approvedIdeas = document.getElementById('approvedIdeas');
if (approvedIdeas) approvedIdeas.style.display = 'none';
if (stage2) stage2.style.display = 'none';
if (generatePromptBtn) {
generatePromptBtn.disabled = true;
generatePromptBtn.title = 'Submit an idea type and brief to generate a prompt';
}
['stage1', 'stage2'].forEach(clearInfoBoxes);
updateMiniCards();
updateApprovedCount();
if (backToInput) backToInput.style.display = 'none';
}
//
function updateApprovedCount() {
if (approvedCount) {
approvedCount.textContent = sessionIdeas.length;
console.log('Approved count updated:', sessionIdeas.length);
}
}
//
function generatePDF() {
try {
console.log('Generating PDF, sessionIdeas count:', sessionIdeas.length);
if (!sessionIdeas.length) {
showNotification('No ideas to generate PDF', 'error', 5000);
return;
}
const { jsPDF } = window.jspdf || {};
if (!jsPDF) {
console.error('jsPDF library not loaded');
showNotification('PDF generation failed: jsPDF not loaded', 'error', 5000);
return;
}
const doc = new jsPDF();
doc.setFont('helvetica', 'bold');
doc.setFontSize(16);
doc.text('IX AI Approved Ideas', 20, 20);
doc.setFont('helvetica', 'normal');
doc.setFontSize(12);
let y = 30;
sessionIdeas.forEach((idea, index) => {
console.log('Adding idea to PDF:', idea.title, 'y:', y);
if (y > 270) {
doc.addPage();
y = 20;
}
doc.setFont('helvetica', 'normal');
doc.text(`Idea #${idea.ideaNumber}${idea.reaction ? ' (Revised)' : ''}`, 20, y);
y += 8;
doc.setFont('helvetica', 'bold');
doc.setFontSize(14);
doc.text(idea.title || 'Untitled', 20, y, { maxWidth: 170 });
y += 8;
doc.setFont('helvetica', 'normal');
doc.setFontSize(12);
const descLines = doc.splitTextToSize(idea.description || 'No description', 170);
doc.text(descLines, 20, y);
y += descLines.length * 6 + 0.5;
if (idea.rationale && idea.rationale.trim()) {
doc.setFont('helvetica', 'bold');
doc.text('Rationale:', 20, y);
doc.setFont('helvetica', 'normal');
const rationaleLines = doc.splitTextToSize(idea.rationale.replace(/[^ -~]/g, ''), 170);
doc.text(rationaleLines, 20, y + 4, { maxWidth: 170 });
y += rationaleLines.length * 6 + 6;
}
if (idea.notes && idea.notes.trim()) {
doc.setFont('helvetica', 'bold');
doc.text('Notes:', 20, y);
doc.setFont('helvetica', 'normal');
const notesLines = doc.splitTextToSize(idea.notes.replace(/[^ -~]/g, ''), 170);
doc.text(notesLines, 20, y + 4, { maxWidth: 170 });
y += notesLines.length * 6 + 6;
}
});
doc.save('IX_AI_Approved_Ideas.pdf');
showNotification('PDF Generated', 'success', 3000);
} catch (e) {
showNotification('Error generating PDF: ' + e.message, 'error', 5000);
console.error('PDF generation error:', e);
}
}
//
function saveBriefBuilderInput() {
const promoting = document.getElementById('briefPromoting')?.value.trim() || '';
const audience = document.getElementById('briefAudience')?.value.trim() || '';
const message = document.getElementById('briefMessage')?.value.trim() || '';
const extra = document.getElementById('briefExtra')?.value.trim() || '';
console.log('Saving Brief Builder input:', { promoting, audience, message, extra });
if (briefInput) {
const ideaType = lastIdeaType || 'unspecified';
briefInput.value = [
`Idea Type: ${ideaType}`,
`What are we promoting?: ${promoting}`,
`Who is the target audience?: ${audience}`,
`What is the key message?: ${message}`,
`What else should the AI know?: ${extra}`
].filter(line => !line.endsWith(':')).join('\n');
briefInput.disabled = false;
console.log('Brief Builder saved to textarea:', briefInput.value);
if (briefBuilderModal) {
briefBuilderModal.style.display = 'none';
document.body.classList.remove('modal-open');
}
const inputEvent = new Event('input', { bubbles: true });
briefInput.dispatchEvent(inputEvent);
resetPromptState();
briefInput.scrollIntoView({ behavior: 'smooth', block: 'center' });
return true;
} else {
console.error('briefInput element not found');
showNotification('Error saving brief', 'error', 5000);
return false;
}
}
//
async function handlePdfUpload(file) {
console.log('Handling PDF upload:', file?.name);
if (!file || file.type !== 'application/pdf') {
showNotification('Please upload a valid PDF file.', 'error', 5000);
return;
}
try {
if (!window.pdfjsLib) {
console.error('pdf.js library not loaded');
showNotification('PDF processing failed: pdf.js not loaded', 'error', 5000);
return;
}
const arrayBuffer = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
let text = '';
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const content = await page.getTextContent();
const pageText = content.items.map(item => item.str.trim()).filter(str => str).join('\n');
text += (text ? '\n\n' : '') + pageText;
}
console.log('Extracted PDF text length:', text.length);
if (briefInput) {
const cleanedText = text.replace(/\n\s*\n+/g, '\n').replace(/\s+/g, ' ').trim();
briefInput.value = cleanedText ? `Idea Type: ${lastIdeaType || 'unspecified'}\nUploaded Brief:\n${cleanedText}` : 'No readable text found in PDF.\n';
briefInput.disabled = false;
console.log('PDF text pasted to textarea:', briefInput.value.substring(0, 100) + '...');
showNotification('PDF brief uploaded successfully. Please review and edit the text below.', 'success', 5000);
const inputEvent = new Event('input', { bubbles: true });
briefInput.dispatchEvent(inputEvent);
resetPromptState();
briefInput.scrollIntoView({ behavior: 'smooth', block: 'center' });
} else {
console.error('briefInput element not found');
showNotification('Error pasting PDF text', 'error', 5000);
}
} catch (e) {
console.error('PDF processing error:', e);
showNotification('Failed to read PDF. It may be scanned or corrupted. Try a text-based PDF or manual input.', 'error', 5000);
}
}
//
function generatePrompt(brief, ideaType) {
console.log('Generating prompt with ideaType:', ideaType);
if (!brief) throw new Error('No brief provided');
if (!ideaType) throw new Error('No idea type provided');
const hallucinationLevel = document.querySelector('input[name="hallucinationLevel"]:checked')?.value;
const aiEngine = document.querySelector('input[name="aiEngine"]:checked')?.value;
if (!hallucinationLevel) throw new Error('No hallucination level selected');
if (!aiEngine) throw new Error('No AI engine selected');
console.log('Selected hallucination level:', hallucinationLevel, 'AI engine:', aiEngine);
const ideaTypeKeywords = [
'tv campaign', 'tv spot', 'television ad', 'television spot',
'radio campaign', 'radio spot', 'radio ad',
'print ad', 'print campaign', 'magazine ad',
'billboard', 'outdoor ad', 'outdoor campaign',
'social media', 'social media campaign', 'social post',
'video', 'video ad', 'video campaign',
'pr stunt', 'publicity stunt', 'public relations',
'product name', 'company name', 'feature name', 'naming',
'blog post', 'blog', 'article', 'content',
'coasters', 'bar coasters', 'coaster designs',
'advertising campaign', 'ad campaign'
];
const ideaTypeLower = ideaType.toLowerCase();
let detectedIdeaType = ideaTypeKeywords.find(keyword => ideaTypeLower.includes(keyword)) || ideaTypeLower;
console.log('Detected idea type:', detectedIdeaType);
let prompt = '';
let hallucinationInstruction = '';
let outputInstructions = '';
const baseOutputInstructions = `
Output instructions:
You MUST generate EXACTLY FIVE ideas. Each idea MUST be formatted as a single block with the fields "Title:", "Description:", and "Rationale:", separated by single newlines. The description MUST be a MAXIMUM of 3 sentences, where a sentence ends with a period, exclamation point, or question mark (can be fewer but not more). The rationale MUST be EXACTLY 1 sentence, with no citations or references (e.g., [1], [2]). Use the exact labels "Title:", "Description:", and "Rationale:" with colons, ensuring each label is bolded in the output (e.g., **Title:**). Do not use headers, numbers, bullet points, or any other separators between ideas. Separate each idea block with exactly two blank lines (double newlines). Do not include any introductions, conclusions, extra text, or formatting deviations. Each of the five ideas MUST represent a radically different approach to the task, with no overlap in themes or execution, and must avoid repeating concepts or themes from any previously provided ideas in this session. Any deviation from this format, including incorrect sentence counts, non-bolded labels, or fewer/more than five ideas, will break the system, so follow it precisely.
`;
if (detectedIdeaType.includes('name') || detectedIdeaType.includes('naming')) {
outputInstructions = `
${baseOutputInstructions}
Each idea MUST be a naming concept (e.g., product name, company name, feature name) as specified in the brief. The description MUST start with "This name..." and explain the name’s concept and appeal in 1-3 sentences.
Format for each idea:
**Title:** [Name]
**Description:** [Maximum 3 sentences starting with "This name..."]
**Rationale:** [Exactly 1 sentence explaining why the name is effective]
`;
} else {
outputInstructions = `
${baseOutputInstructions}
Each idea MUST be a ${detectedIdeaType} concept, with the first sentence of the description explicitly stating it is a ${detectedIdeaType} and summarizing its main focus. The description should outline the ${detectedIdeaType}’s key elements or execution, tailored to the audience specified in the brief. All ideas MUST strictly adhere to the ${detectedIdeaType} format and not use any other medium or format.
Format for each idea:
**Title:** [${detectedIdeaType} Title]
**Description:** [Maximum 3 sentences starting with "This ${detectedIdeaType}..."]
**Rationale:** [Exactly 1 sentence explaining why the ${detectedIdeaType} is effective]
`;
}
switch (hallucinationLevel) {
case 'none':
hallucinationInstruction = `Do not apply any hallucination; generate ${detectedIdeaType} ideas strictly based on the brief and conventional ${detectedIdeaType.includes('name') ? 'naming' : detectedIdeaType.includes('blog') ? 'content marketing' : detectedIdeaType.includes('coaster') ? 'promotional' : 'advertising'} principles, mimicking a temperature of 0.1 for maximum predictability.`;
prompt = `
Creative brief:
${brief}
Instructions:
${hallucinationInstruction}
Generate EXACTLY FIVE ${detectedIdeaType} ideas based on the provided creative brief, adhering strictly to the format specified below. Act as an expert on the target audience described in the creative brief, ensuring all ideas are highly relevant to their preferences, behaviors, and values.
${outputInstructions}
`;
console.log(`Generated prompt for "none" hallucination level (${detectedIdeaType})`);
break;
case 'microdose':
hallucinationInstruction = `Introduce a single, subtle hallucination per ${detectedIdeaType} idea (e.g., an unexpected but plausible angle or perspective), mimicking a temperature of 0.3 for slight randomness, while keeping ideas highly feasible.`;
prompt = `
You are a strategic ${detectedIdeaType.includes('name') ? 'naming' : detectedIdeaType.includes('blog') ? 'content marketing' : detectedIdeaType.includes('coaster') ? 'promotional' : 'advertising'} expert tasked with creating practical, audience-focused ${detectedIdeaType} ideas based on the provided creative brief. Your goal is to develop ideas that enhance conventional ${detectedIdeaType} formats with subtle, innovative twists, ensuring high feasibility and alignment with the audience’s values and behaviors.
Creative brief:
${brief}
Instructions for Creative Output:
- ${hallucinationInstruction}
- Act as an expert on the target audience described in the creative brief, ensuring all ideas are highly relevant to their preferences, behaviors, and values.
- Maximize diversity across the five ideas by exploring distinct themes (e.g., humor, inspiration, mystery), emotional tones (e.g., joy, curiosity, awe), and execution styles, ensuring no conceptual overlap.
- For each idea, adopt a unique creative lens inspired by a distinct era, culture, genre, or imagined world to ensure varied perspectives.
- Focus on established ${detectedIdeaType} formats with one subtle, innovative twist per idea, grounded in current cultural trends and proven strategies.
- Avoid predictable ${detectedIdeaType.includes('name') ? 'naming conventions' : detectedIdeaType.includes('blog') ? 'blog content tropes' : detectedIdeaType.includes('coaster') ? 'promotional tropes' : 'advertising tropes'}, ensuring ideas are fresh yet executable.
- All ideas MUST be ${detectedIdeaType} concepts, regardless of any other types mentioned in the brief.
${outputInstructions}
`;
console.log(`Generated prompt for "microdose" hallucination level (${detectedIdeaType})`);
break;
case 'buzzed':
hallucinationInstruction = `Fuse two seemingly unrelated concepts to create bold, surprising ${detectedIdeaType} ideas, mimicking a temperature of 0.7 for moderate randomness and creativity.`;
prompt = `
You are a bold creative director tasked with pushing ${detectedIdeaType.includes('name') ? 'naming' : detectedIdeaType.includes('blog') ? 'content marketing' : detectedIdeaType.includes('coaster') ? 'promotional' : 'advertising'} boundaries while keeping ideas feasible and impactful, based on the provided creative brief. Your goal is to generate innovative ${detectedIdeaType} ideas that combine unexpected concepts into surprising, executable concepts, drawing inspiration from any domain of human knowledge or imagination to captivate the audience.
Creative brief:
${brief}
Instructions for Creative Output:
- ${hallucinationInstruction}
- Act as an expert on the target audience described in the creative brief, ensuring all ideas are highly relevant to their preferences, behaviors, and values.
- Maximize diversity across the five ideas by exploring distinct themes (e.g., humor, inspiration, mystery), emotional tones (e.g., joy, curiosity, awe), and execution styles, ensuring no conceptual overlap.
- For each idea, adopt a unique creative lens inspired by a distinct era, culture, genre, or imagined world to ensure varied perspectives.
- Ensure ideas push creative boundaries but remain anchored to the brand’s essence and audience expectations, with clear execution paths.
- Avoid predictable ${detectedIdeaType.includes('name') ? 'naming conventions' : detectedIdeaType.includes('blog') ? 'blog content tropes' : detectedIdeaType.includes('coaster') ? 'promotional tropes' : 'advertising tropes'}, focusing on surprising yet implementable concepts.
- All ideas MUST be ${detectedIdeaType} concepts, regardless of any other types mentioned in the brief.
${outputInstructions}
`;
console.log(`Generated prompt for "buzzed" hallucination level (${detectedIdeaType})`);
break;
case 'tripping':
hallucinationInstruction = `Generate surreal, boundary-defying ${detectedIdeaType} ideas with maximal creative freedom, mimicking a temperature of 1.2 for high randomness and hallucination.`;
prompt = `
You are a visionary artist unbound by reality, tasked with redefining ${detectedIdeaType.includes('name') ? 'naming' : detectedIdeaType.includes('blog') ? 'content marketing' : detectedIdeaType.includes('coaster') ? 'promotional' : 'advertising'} itself based on the provided creative brief. Your goal is to create surreal, transcendent ${detectedIdeaType} ideas that invent new formats, emotions, or experiences, drawing inspiration from any domain of human knowledge or imagination.
Creative brief:
${brief}
Instructions for Creative Output:
- ${hallucinationInstruction}
- Act as an expert on the target audience described in the creative brief, ensuring all ideas are highly relevant to their preferences, behaviors, and values, even if expressed in unconventional ways.
- Maximize diversity across the five ideas by exploring distinct themes (e.g., humor, inspiration, mystery), emotional tones (e.g., joy, curiosity, awe), and execution styles, ensuring no conceptual overlap.
- For each idea, adopt a unique creative lens inspired by a distinct era, culture, genre, or imagined world to ensure varied perspectives.
- Invent entirely new ${detectedIdeaType} formats or experiences that challenge conventional ${detectedIdeaType.includes('blog') ? 'content' : detectedIdeaType.includes('coaster') ? 'promotional items' : 'media'}, prioritizing imagination and unexpected emotional impact.
- Avoid predictable ${detectedIdeaType.includes('name') ? 'naming conventions' : detectedIdeaType.includes('blog') ? 'blog content tropes' : detectedIdeaType.includes('coaster') ? 'promotional tropes' : 'advertising tropes'}, focusing on visionary concepts that may be speculative or futuristic.
- All ideas MUST be ${detectedIdeaType} concepts, regardless of any other types mentioned in the brief.
${outputInstructions}
`;
console.log(`Generated prompt for "tripping" hallucination level (${detectedIdeaType})`);
break;
default:
throw new Error('Invalid hallucination level selected');
}
return prompt;
}
//
function showAiEngineModal(aiEngine, fullPrompt, isFeedback = false) {
console.log('Showing AI engine modal for:', aiEngine, 'isFeedback:', isFeedback);
if (!aiEngineName || !openAiEngineBtn || !aiEngineModal) {
console.error('AI engine modal elements missing:', { aiEngineName, openAiEngineBtn, aiEngineModal });
showNotification('Error: AI engine modal elements missing', 'error', 5000);
return;
}
const aiEngineUrls = {
'Grok': 'https://x.com/i/grok',
'ChatGPT': 'https://chat.openai.com',
'Perplexity': 'https://www.perplexity.ai'
};
const url = aiEngineUrls[aiEngine] || 'https://x.com/i/grok';
aiEngineName.textContent = aiEngine;
openAiEngineBtn.textContent = isFeedback ? 'Go to Existing AI Tab' : 'Open AI Engine';
openAiEngineBtn.setAttribute('aria-label', isFeedback ? 'Go to existing AI engine tab' : 'Open selected AI engine');
openAiEngineBtn.onclick = () => {
console.log(isFeedback ? 'Focusing existing AI engine tab:' : 'Opening AI engine tab:', url);
if (aiEngineWindow && !aiEngineWindow.closed) {
aiEngineWindow.focus();
} else {
aiEngineWindow = window.open(url, 'aiEngineTab');
}
aiEngineModal.style.display = 'none';
document.body.classList.remove('modal-open');
navigator.clipboard.writeText(fullPrompt).then(() => {
showNotification(`Prompt copied. Paste it into the same ${aiEngine} thread and return with the response.`, 'success', 5000);
if (stage2) stage2.style.display = 'block';
const llmResponse = document.getElementById('llmResponse');
if (llmResponse) {
llmResponse.scrollIntoView({ behavior: 'smooth', block: 'center' });
} else if (stage2) {
console.error('llmResponse textarea not found for scrolling');
window.scrollTo({ top: stage2.offsetTop, behavior: 'smooth' });
}
}).catch(err => {
showNotification('Clipboard access failed.', 'error', 5000);
console.error('Clipboard write error:', err);
});
};
aiEngineModal.style.display = 'block';
document.body.classList.add('modal-open');
}
//
function parseLLMResponse(text) {
console.log('Parsing LLM response, raw text length:', text.length);
const ideas = { newIdeas: [], revisedIdeas: [], generalFeedbackReaction: '' };
const normalizedText = text.replace(/\r\n|\r/g, '\n').trim();
console.log('Normalized text length:', normalizedText.length);
const blocks = normalizedText.split(/\n{1,2}/).map(block => block.trim()).filter(block => block);
console.log('Parsed blocks count:', blocks.length);
let newIdeaNumber = 1;
let parsingErrors = [];
for (let block of blocks) {
if (block.match(/^General\s*Feedback\s*Reaction\s*:/i)) {
ideas.generalFeedbackReaction = block.replace(/^General\s*Feedback\s*Reaction\s*:\s*/i, '').trim();
console.log('Parsed general feedback reaction:', ideas.generalFeedbackReaction);
continue;
}
const lines = block.split('\n').map(line => line.trim()).filter(line => line);
let idea = { ideaNumber: '', title: '', description: '', rationale: '', reaction: '' };
let currentField = null;
let tempFields = { title: [], description: [], rationale: [], reaction: [] };
for (let i = 0; i < lines.length; i++) {
let cleanLine = lines[i].replace(/^\*\*|\*\*$/g, '').replace(/\*/g, '').trim();
if (cleanLine.match(/^Title\s*:/i)) {
currentField = 'title';
tempFields.title.push(cleanLine.replace(/^Title\s*:/i, '').trim());
} else if (cleanLine.match(/^Description\s*:/i)) {
currentField = 'description';
tempFields.description.push(cleanLine.replace(/^Description\s*:/i, '').trim());
for (let j = i + 1; j < lines.length; j++) {
let nextLine = lines[j].replace(/^\*\*|\*\*$/g, '').replace(/\*/g, '').trim();
if (nextLine.match(/^Rationale\s*:/i) || nextLine.match(/^Reaction\s*to\s*feedback\s*:/i) || nextLine.match(/^Title\s*:/i)) {
break;
}
tempFields.description.push(nextLine);
i = j;
}
} else if (cleanLine.match(/^Rationale\s*:/i)) {
currentField = 'rationale';
tempFields.rationale.push(cleanLine.replace(/^Rationale\s*:/i, '').trim());
} else if (cleanLine.match(/^Reaction\s*to\s*feedback\s*:/i)) {
currentField = 'reaction';
tempFields.reaction.push(cleanLine.replace(/^Reaction\s*to\s*feedback\s*:/i, '').trim());
} else if (currentField) {
tempFields[currentField].push(cleanLine);
} else {
tempFields.description.push(cleanLine);
}
}
idea.title = tempFields.title.join(' ').trim();
idea.description = tempFields.description.filter(line => line).join(' ').trim();
idea.rationale = tempFields.rationale.join(' ').trim().replace(/\[\d+\](?:\[\d+\])*/g, '');
idea.reaction = tempFields.reaction.join(' ').trim();
if (idea.title || idea.description) {
if (!idea.title) idea.title = 'Untitled';
if (!idea.description) idea.description = 'No description provided.';
if (!idea.rationale && !idea.reaction) {
idea.rationale = 'No rationale provided.';
console.warn('No rationale found for idea:', idea.title);
}
if (idea.description) {
let sentences = idea.description.match(/(? s.trim()).map(s => s.replace(/[.!?]$/, '').trim())
: [idea.description.trim()];
sentences = sentences.filter(s => s && !/[.!?]/.test(s));
if (sentences.length > 3) {
idea.description = sentences.slice(0, 3).join('. ') + (sentences[2].endsWith('.') ? '' : '.');
console.log('Limited description to 3 sentences for idea:', idea.title);
} else {
idea.description = idea.description.trim();
if (!/[.!?]$/.test(idea.description) && sentences.length > 0) {
idea.description += '.';
}
}
}
idea.ideaNumber = newIdeaNumber.toString();
if (idea.reaction) {
ideas.revisedIdeas.push(idea);
} else {
ideas.newIdeas.push(idea);
}
newIdeaNumber++;
console.log('Valid idea added:', idea);
} else {
parsingErrors.push(`Skipped invalid block (missing title and description): ${block.substring(0, 100)}...`);
console.warn('Skipped invalid block:', block);
}
}
if (parsingErrors.length) {
console.error('Parsing errors encountered:', parsingErrors);
}
if (ideas.newIdeas.length > 5) {
console.warn(`Received ${ideas.newIdeas.length} new ideas, limiting to 5`);
ideas.newIdeas = ideas.newIdeas.slice(0, 5);
showNotification('Received more than 5 new ideas. Displaying only the first 5.', 'error', 5000);
}
if (!ideas.newIdeas.length && !ideas.revisedIdeas.length && !ideas.generalFeedbackReaction) {
console.error('No valid ideas or feedback reaction parsed. Raw response:', text.substring(0, 500));
showNotification('No valid ideas or feedback found. Ensure the AI response includes ideas with a Title, Description, or General Feedback Reaction.', 'error', 5000);
} else if (ideas.newIdeas.length < 5 && !ideas.revisedIdeas.length && ideas.newIdeas.length > 0) {
console.warn(`Only ${ideas.newIdeas.length} new ideas parsed. Expected 5.`);
showNotification(`Only ${ideas.newIdeas.length} ideas received. Check AI response formatting for Title, Description, and Rationale fields.', 'error', 5000);
}
console.log('Final parsed ideas:', ideas);
return ideas;
}
//
function renderIdeas(ideas) {
console.log('Rendering ideas to screen, count:', ideas.newIdeas.length + ideas.revisedIdeas.length);
const ideasContainer = document.getElementById('ideasContainer');
if (!ideasContainer) {
console.error('Ideas container not found');
showNotification('Error: Ideas container missing', 'error', 5000);
return;
}
ideasContainer.innerHTML = '';
if (!ideas.newIdeas.length && !ideas.revisedIdeas.length && !ideas.generalFeedbackReaction && !lastGeneralFeedback) {
ideasContainer.innerHTML = '
No ideas or feedback found. Please check the AI response format.
';
showNotification('No ideas or feedback found in response', 'error', 5000);
console.warn('No ideas, general feedback reaction, or user feedback to render');
return;
}
if (lastGeneralFeedback) {
const userFeedbackDiv = document.createElement('div');
userFeedbackDiv.className = 'general-feedback-card';
userFeedbackDiv.innerHTML = `
`;
ideasContainer.appendChild(generalFeedbackCard);
}
//
function processIdeas(text) {
try {
console.log('Processing ideas, input text length:', text.length);
const parsedIdeas = parseLLMResponse(text);
if (!Array.isArray(parsedIdeas.newIdeas)) parsedIdeas.newIdeas = [];
if (!Array.isArray(parsedIdeas.revisedIdeas)) parsedIdeas.revisedIdeas = [];
if (!parsedIdeas.newIdeas.length && !parsedIdeas.revisedIdeas.length) {
showNotification('No valid ideas found. Ensure the AI response includes ideas with a Title or Description.', 'error', 5000);
return;
}
lastIdeas = [...parsedIdeas.revisedIdeas, ...parsedIdeas.newIdeas];
lastIdeas.generalFeedbackReaction = parsedIdeas.generalFeedbackReaction;
console.log('Processed ideas:', lastIdeas);
if (lastIdeas.length < 5 && !parsedIdeas.revisedIdeas.length && lastIdeas.length > 0) {
showNotification(`Only ${lastIdeas.length} ideas received. Check AI response formatting for Title, Description, and Rationale fields.`, 'error', 5000);
}
stage2Active = true;
renderIdeas(parsedIdeas);
if (stage2) stage2.style.display = 'block';
if (document.getElementById('stage2Actions')) document.getElementById('stage2Actions').style.display = 'block';
if (document.getElementById('approvedIdeas')) document.getElementById('approvedIdeas').style.display = 'block';
if (generatePromptBtn) {
generatePromptBtn.disabled = true;
generatePromptBtn.title = 'Submit a new idea type or brief to enable';
}
if (backToInput) backToInput.style.display = window.innerWidth <= 768 ? 'block' : 'none';
const ideasContainer = document.getElementById('ideasContainer');
if (ideasContainer) {
window.scrollTo({ top: ideasContainer.offsetTop, behavior: 'smooth' });
} else {
console.error('Ideas container not found for scrolling');
}
} catch (e) {
showNotification('Error processing response: ' + e.message, 'error', 5000);
console.error('Process ideas error:', e);
}
}
//
function generateFeedbackPrompt(feedbackData, cards, generalFeedback) {
console.log('Generating feedback prompt, feedbackData:', feedbackData, 'generalFeedback:', generalFeedback);
lastGeneralFeedback = generalFeedback;
let feedbackText = '';
const outputInstructions = `
Output instructions:
You MUST generate EXACTLY FIVE new ideas. Each new idea MUST be formatted as a single block with the fields "Title:", "Description:", and "Rationale:", separated by single newlines. The description MUST be a MAXIMUM of 3 sentences, where a sentence ends with a period, exclamation point, or question mark (can be fewer but not more). The rationale MUST be EXACTLY 1 sentence, with no citations or references (e.g., [1], [2]). Use the exact labels "Title:", "Description:", and "Rationale:" with colons, ensuring each label is bolded in the output (e.g., **Title:**). Do not use headers, numbers, bullet points, or any other separators between ideas. Separate each idea block with exactly two blank lines (double newlines). Do not include any introductions, conclusions, extra text, or formatting deviations. Each of the five new ideas MUST represent a radically different approach to the task, with no overlap in themes or execution, and must avoid repeating concepts or themes from any previously provided ideas in this session. Any deviation from this format, including incorrect sentence counts, non-bolded labels, or fewer/more than five ideas, will break the system, so follow it precisely.
`;
if (feedbackData.some(f => f.feedback) || generalFeedback) {
if (generalFeedback) {
feedbackText = `General Feedback: ${generalFeedback}\n`;
}
const specificFeedback = feedbackData.filter(f => f.feedback).map(f => {
const card = cards.find(card => card.dataset.ideaNumber === f.ideaNumber.toString());
if (!card) {
console.warn(`No card found for idea number ${f.ideaNumber}`);
return '';
}
const paragraphs = Array.from(card.querySelectorAll('p'));
const titleP = paragraphs.find(p => p.textContent.startsWith('Title:'));
return `Idea #${f.ideaNumber} (${titleP ? titleP.textContent.replace(/^Title:/, '').trim() : 'Untitled'}): ${f.feedback}`;
}).filter(text => text).join('\n');
feedbackText += specificFeedback;
return `
You've provided feedback on the following ideas, with General Feedback (if provided) being the most critical input to address, as it reflects your overall sentiment or concerns about the ideas. ${generalFeedback ? 'If General Feedback is present, you MUST include a "General Feedback Reaction:" section (2-3 sentences) before any idea blocks, critically evaluating the feedback’s implications and proposing how it will shape the revised or new ideas.' : ''} For each idea with specific feedback, critically evaluate its relevance, feasibility, and alignment with your goals for promoting Ideasicle X as a platform for virtual, four-person creative teams. Provide a "Reaction to feedback" section (2-3 sentences) for each idea with feedback, explaining whether the feedback is valid and how it will be addressed, or proposing a feasible alternative if the feedback is impractical. Then, revise each idea by rewriting the description (MAXIMUM 3 sentences, where a sentence ends with a period, exclamation point, or question mark) to address the feedback, maintaining the idea’s original title. Additionally, generate EXACTLY FIVE new ideas based on insights from the feedback, adhering to the output instructions below.
${outputInstructions}
Format for revised ideas:
**Title:** [Original Title for Revised Ideas]
**Reaction to feedback:** [2-3 sentences evaluating the feedback]
**Description:** [Maximum 3 sentences]
Format for new ideas:
**Title:** [Unique, Descriptive Idea Title]
**Description:** [Maximum 3 sentences]
**Rationale:** [Exactly 1 sentence]
Your Feedback:
${feedbackText}
`;
} else {
const rejectedIdeas = lastIdeas.map((idea, i) => `Idea #${i + 1} (${idea.title || 'Untitled'}): Rejected by you (no feedback provided).`).join('\n');
return `
You've rejected all previous ideas without providing specific feedback. Review the rejected ideas below, learn from their weaknesses, and generate EXACTLY FIVE new ideas for promoting Ideasicle X as a platform for virtual, four-person creative teams, adhering to the output instructions below.
${outputInstructions}
Format for new ideas:
**Title:** [Unique, Descriptive Idea Title]
**Description:** [Maximum 3 sentences]
**Rationale:** [Exactly 1 sentence]
Your Rejected Ideas:
${rejectedIdeas}
`;
}
}
//
function saveAndDisplayMiniCards(ideas) {
console.log('Saving and displaying mini-cards, ideas count:', ideas.length);
const uniqueIdeas = ideas.filter(idea => !sessionIdeas.some(stored =>
stored.title === idea.title &&
stored.description === idea.description &&
stored.reaction === idea.reaction
));
sessionIdeas = [...uniqueIdeas, ...sessionIdeas];
console.log('Updated sessionIdeas:', sessionIdeas);
updateMiniCards();
updateApprovedCount();
return uniqueIdeas.length;
}
//
function updateMiniCards() {
console.log('Updating mini-cards, sessionIdeas count:', sessionIdeas.length);
if (!miniCardsContainer || !noIdeas || !generatePdfBtn) {
console.error('Mini cards container, no ideas, or generate PDF button not found');
return;
}
miniCardsContainer.innerHTML = '';
noIdeas.style.display = sessionIdeas.length ? 'none' : 'block';
generatePdfBtn.style.display = sessionIdeas.length ? 'block' : 'none';
sessionIdeas.forEach((idea, index) => {
const card = document.createElement('div');
card.className = `mini-card ${idea.reaction ? 'revised' : ''}`;
card.dataset.index = index;
card.innerHTML = `
${idea.title || 'Untitled'}
`;
card.addEventListener('click', () => {
showModal(idea, index);
document.body.classList.add('modal-open');
});
miniCardsContainer.appendChild(card);
});
}
//
function showModal(idea, index) {
console.log('Showing modal for idea:', idea.title);
if (!modalContent || !modalIdeaNotes || !modalApproveCheckbox || !modal) {
console.error('Modal content, notes, or approve checkbox not found');
showNotification('Error: Modal elements missing', 'error', 5000);
return;
}
modalContent.innerHTML = `
Idea #${idea.ideaNumber}${idea.reaction ? ' (Revised)' : ''}