console.log("Script execution started.");
// --- Global Variables ---
let generatedCode = [];
let currentCleanedCode = [];
let evolutionTimeline = [];
let activeTimelineIndex = -1;
let selectedVariationGridIndex = -1;
let currentFullscreenHistoryIndex = -1;
let originalUserPromptForCurrentGeneration = '';
let lastPreviewUpdateTime = [];
let previewUpdateInterval = 500;
let numVariationsToGenerate = 4;
let activeApiControllers = [];
let lastGenerationConfig = {
prompt: '',
isRefinement: false,
numVariations: 4,
refinedTimelineIndex: -1,
thinkingBudget: 0
};
// --- DOM Element References ---
let apiKeyEl, codeOutputEl, errorMessageEl;
let modelSelEl;
let selectButtons = [];
let fullscreenButtons = [];
let previewItems = [];
let refinementLoadingIndicator;
let mainContentEl, configButtonEl;
let intervalSliderEl, intervalValueDisplayEl;
let fullscreenOverlayEl, fullscreenIframeEl, exitFullscreenBtnEl;
let perspectiveViewportEl, previewGridWrapperEl;
let historyPanelEl, historyPanelPlaceholderEl;
let selectedCodeTitleH3El;
let mainContentTitleH1El, mainContentSubtitleH2El;
let fullscreenHistoryNavEl, historyNavPrevBtnEl, historyNavNextBtnEl;
let promptModalOverlayEl, promptModalContentEl, modalUserPromptEl, modalGenerateBtnEl, modalCancelBtnEl, modalLoadingIndicatorEl;
let modalRefinementCheckboxEl, numVariationsSliderEl, numVariationsValueDisplayEl;
let configModalOverlayEl, configModalContentEl, configModalCloseBtnEl, copyCodeButtonEl;
let historyToggleButtonEl, historyArrowDownEl, historyArrowUpEl;
let exportCodeButtonEl;
let newButtonEl;
let confirmModalOverlayEl, confirmModalMessageEl, confirmModalConfirmBtnEl, confirmModalCancelBtnEl;
let currentConfirmCallback = null;
let historyNavLeftBtnEl, historyNavRightBtnEl;
let modalThinkingBudgetSliderEl, modalThinkingBudgetValueDisplayEl;
let promptDisplayModalOverlayEl, promptDisplayModalContentEl, fullPromptTextEl, promptDisplayModalCloseBtnEl;
let showPromptModalButtonEl; // Added for the new button
let variationNavLeftBtnEl, variationNavRightBtnEl; // Buttons for variation scrolling
// --- Elements for Initial Setup CTA ---
let initialSetupCtaEl;
let initialApiKeyInputEl;
let examplePromptsContainerEl;
// --- Constants ---
const API_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta/models/';
// --- Helper Function to Process Stream for One Variation ---
async function processStreamForVariation(apiKey, prompt, variationIndex, modelName, signal) {
console.log(`[Variation ${variationIndex + 1}] Starting generation with model ${modelName}...`);
const previewFrame = document.getElementById(`preview-frame-${variationIndex + 1}`);
const loader = document.getElementById(`preview-loader-${variationIndex + 1}`);
const selectBtn = selectButtons[variationIndex];
const fullscreenBtn = fullscreenButtons[variationIndex];
const currentThinkingBudget = parseInt(lastGenerationConfig.thinkingBudget, 10);
if (!previewFrame) { console.error(`[Variation ${variationIndex + 1}] Preview frame missing.`); return false; }
if (!loader) { console.warn(`[Variation ${variationIndex + 1}] Loader missing.`); }
if (loader) loader.classList.remove('hidden');
if (selectBtn) selectBtn.disabled = true;
if (fullscreenBtn) fullscreenBtn.disabled = true;
if (previewFrame) updateLivePreviewInGrid(variationIndex, '
', false);
if(lastPreviewUpdateTime[variationIndex] !== undefined) lastPreviewUpdateTime[variationIndex] = 0;
const API_ENDPOINT = `${API_BASE_URL}${modelName}:streamGenerateContent?key=${apiKey}&alt=sse`;
let rawGeneratedCode = '';
let htmlBlockStarted = false;
let accumulatedStreamData = '';
let success = false;
try {
const requestBody = {
contents: [{ parts: [{ text: prompt }] }]
};
if (currentThinkingBudget > 0) {
requestBody.generationConfig = {
thinkingConfig: {
thinkingBudget: currentThinkingBudget
}
};
console.log(`[Variation ${variationIndex + 1}] Thinking Mode ENABLED. Budget: ${currentThinkingBudget}`);
}
const response = await fetch(API_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody),
signal: signal
});
if (!response.ok) {
let errorDetails = `HTTP Error: ${response.status} ${response.statusText}`;
try { const errorData = await response.json(); errorDetails += ` - ${errorData?.error?.message || 'No msg.'}`; } catch (e) { /* Ignore */ }
throw new Error(errorDetails);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let sseBuffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
sseBuffer += decoder.decode(value, { stream: true });
let eolIndex;
while ((eolIndex = sseBuffer.indexOf('\n')) >= 0) {
const line = sseBuffer.substring(0, eolIndex).trim();
sseBuffer = sseBuffer.substring(eolIndex + 1);
if (line.startsWith('data: ')) {
try {
const jsonData = JSON.parse(line.substring(5).trim());
if (jsonData.candidates?.[0]?.content?.parts?.[0]?.text) {
const textPart = jsonData.candidates[0].content.parts[0].text;
if (!htmlBlockStarted) {
accumulatedStreamData += textPart;
const marker = "```html";
const markerIndex = accumulatedStreamData.indexOf(marker);
if (markerIndex !== -1) {
htmlBlockStarted = true;
let codeStartIndex = markerIndex + marker.length;
if (accumulatedStreamData[codeStartIndex] === '\n') {
codeStartIndex++;
}
rawGeneratedCode = accumulatedStreamData.substring(codeStartIndex);
}
} else {
rawGeneratedCode += textPart;
}
if (htmlBlockStarted) {
const now = Date.now();
if (lastPreviewUpdateTime[variationIndex] !== undefined && (now - lastPreviewUpdateTime[variationIndex] >= previewUpdateInterval)) {
updateLivePreviewInGrid(variationIndex, rawGeneratedCode, true);
lastPreviewUpdateTime[variationIndex] = now;
}
}
}
} catch (e) { console.warn(`[Var ${variationIndex + 1}] JSON parse error:`, e, "Problematic Line:", line); }
}
}
}
console.log(`[Variation ${variationIndex + 1}] Streaming finished. HTML Block Started: ${htmlBlockStarted}. Raw content after marker: ${(rawGeneratedCode || "").substring(0, 200)}`);
let finalExtractedHtml = null;
let processingErrorMessage = null;
if (htmlBlockStarted) {
let tempHtml = rawGeneratedCode.trim();
if (tempHtml.endsWith("```")) {
tempHtml = tempHtml.substring(0, tempHtml.length - 3).trim();
} else if (tempHtml.endsWith("```html")) {
tempHtml = tempHtml.substring(0, tempHtml.length - 7).trim();
}
if (tempHtml.length > 0) {
finalExtractedHtml = tempHtml;
const minLength = 20;
const hasStructuralTag = /]*>|]*>|]*>/i.test(finalExtractedHtml);
const hasAnyTag = /<[a-zA-Z][^>]*>/.test(finalExtractedHtml);
if (finalExtractedHtml.length >= minLength && (hasStructuralTag || hasAnyTag)) {
success = true;
console.log(`[Variation ${variationIndex + 1}] HTML validation SUCCEEDED.`);
} else {
success = false;
console.warn("[Variation " + (variationIndex + 1) + "] HTML validation FAILED (length: " + finalExtractedHtml.length + ", structural: " + hasStructuralTag + ", anyTag: " + hasAnyTag + "). Review content manually.");
}
} else {
processingErrorMessage = "// Error: No meaningful content found after '```html' marker.";
console.warn(`[Variation ${variationIndex + 1}] ${processingErrorMessage}`);
success = false;
}
} else {
processingErrorMessage = `// Error: '\`\`\`html' code block marker not found. Received:\n${(accumulatedStreamData || rawGeneratedCode || "").substring(0, 200)}`;
console.warn(`[Variation ${variationIndex + 1}] ${processingErrorMessage}`);
success = false;
}
if (success && finalExtractedHtml) {
let processedHtml = finalExtractedHtml;
const requiredHeadContent = `
blocks (that are not JSON-LD or other non-executable script types). If no functional