Built for bigger screens

The Web-Builder Wizard uses a live preview panel that needs a bit more room to work its magic. Open it on a laptop or desktop for the full experience.

Web-Builder Wizard

Polish:
Zoom
/* ── Build a plain-text context string from wizard state ── */ function buildBlazeContext() { if (typeof state === 'undefined' || !state) return ''; var a = state.answers || {}; var out = []; out.push('CURRENT WEB WIZARD CONFIGURATION'); out.push('Polish level: ' + (state.polish || 'basic')); /* Site type */ if (a.project) { var p = a.project; out.push('Site type: ' + (p.label || p.value || JSON.stringify(p))); } /* Style / mood */ if (a.style) { var s = a.style; out.push('Style / mood: ' + (s.label || s.value || JSON.stringify(s))); } /* Colour palette */ if (a.color) { var cl = a.color; out.push('Colour palette: ' + (cl.name || cl.label || cl.value || JSON.stringify(cl))); } /* Layout */ if (a.layout) { var l = a.layout; out.push('Layout: ' + (l.label || l.value || JSON.stringify(l))); } /* Typography */ if (a.typography) { var ty = a.typography; out.push('Typography: ' + (ty.label || ty.value || JSON.stringify(ty))); } /* Components */ if (a.components && Array.isArray(a.components) && a.components.length) { out.push('Components selected: ' + a.components.map(function(c) { return c.label || c.value || c; }).join(', ')); } /* Pages */ if (a.pages && Array.isArray(a.pages) && a.pages.length) { out.push('Pages: ' + a.pages.map(function(pg) { return pg.label || pg.value || pg; }).join(', ')); } /* Content */ if (a.content) { var ct = a.content; if (ct.siteName) out.push('Site name: ' + ct.siteName); if (ct.tagline) out.push('Tagline: ' + ct.tagline); if (ct.description) out.push('Description: ' + ct.description); } out.push('Step the user is on: ' + (state.currentStepId || 'start')); return out.join('\n'); } /* ── Update the context bar label to reflect current state ── */ function updateBlazeContextBar() { var label = document.getElementById('blazeContextLabel'); if (!label) return; if (typeof state === 'undefined' || !state || !state.answers) { label.textContent = 'Reads your current wizard setup'; return; } var a = state.answers; var parts = []; if (a.project) parts.push(a.project.label || a.project.value || 'site'); if (a.style) parts.push(a.style.label || a.style.value || 'style'); if (a.color) parts.push(a.color.name || a.color.label || 'palette'); label.textContent = parts.length ? 'Context loaded: ' + parts.join(' · ') : 'Reads your current wizard setup'; } /* ── Build the initial message Blaze will receive (hidden) ── */ function buildBlazeInitialMessage() { var ctx = buildBlazeContext(); if (!ctx) { return 'The user has just opened the Blaze web assistant from inside ' + 'the LumanPOP Web Builder Wizard. Please greet them warmly, ' + 'briefly explain what you can help with (design decisions, ' + 'component choices, copy, UX patterns), and ask what they\u2019d ' + 'like to work on.'; } return 'The user has opened Blaze from inside the LumanPOP Web Builder ' + 'Wizard. Here is their current configuration:\n\n' + ctx + '\n\nPlease greet them briefly, acknowledge what they\u2019ve ' + 'configured so far, and offer 2-3 specific and actionable ' + 'observations or suggestions based on their selections. ' + 'Keep it concise \u2014 they can ask follow-ups.'; } /* ── Package wizard state and hand off to AI Ultra Web Builder ── Called when the user clicks the handoff button after confirming they want to try AI Ultra. POSTs the project to the worker, gets a 30-minute single-use token, redirects to ai-web-builder. Falls back to ai-web-builder without a session if anything fails — the user can still reach the page, they just start fresh. */ function doHandoff() { /* Build the same project shape saveProject() uses */ var project = { _v: (typeof STATE_VERSION !== 'undefined') ? STATE_VERSION : 1, _saved: new Date().toISOString(), _app: 'LumanPOP Web-Builder Wizard', polish: (state && state.polish) ? state.polish : 'standard', answers: (state && state.answers) ? state.answers : {} }; /* Disable the button immediately so the user can't double-fire */ if (blazeChat && blazeChat.showHandoff) { blazeChat.showHandoff(null); } fetch('https://dash.lumanpop.com', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'handoff-save', project: project }) }) .then(function(res) { return res.json(); }) .then(function(data) { if (data.ok && data.token) { window.location.href = '/ai-web-builder.html?session=' + encodeURIComponent(data.token); } else { /* Worker responded but no token — go without session */ window.location.href = '/ai-web-builder.html'; } }) .catch(function() { /* Network failure — still send the user to AI Ultra, just without context */ window.location.href = '/ai-web-builder.html'; }); } /* ── Watch for [UPGRADE_TO_ULTRA] in Blaze's streamed responses ── Uses a MutationObserver on the chat container so it fires as content arrives during streaming rather than after. When the marker is found: 1. Strips it from the visible text node (user never sees it) 2. Reveals the handoff button wired to doHandoff() 3. Disconnects the observer (fires once per session) */ function watchForUpgradeMarker() { var container = document.getElementById('blazeChatContainer'); if (!container) return; var observer = new MutationObserver(function() { if (upgradeMarkerDetected) return; if (container.textContent.indexOf('[UPGRADE_TO_ULTRA]') === -1) return; /* Marker found — fire once */ upgradeMarkerDetected = true; observer.disconnect(); /* Strip the marker from whichever text node contains it */ var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null, false); var node; while ((node = walker.nextNode())) { if (node.textContent.indexOf('[UPGRADE_TO_ULTRA]') !== -1) { node.textContent = node.textContent.replace(/\[UPGRADE_TO_ULTRA\]/g, '').trim(); } } /* Reveal the handoff button */ if (blazeChat && blazeChat.showHandoff) { blazeChat.showHandoff(doHandoff); } }); observer.observe(container, { childList: true, subtree: true, characterData: true }); } /* ── Toggle the panel open/closed ── */ function toggleBlazePanel() { blazePanelOpen = !blazePanelOpen; var panel = document.getElementById('blazePanel'); var btn = document.getElementById('blazeBtn'); if (blazePanelOpen) { updateBlazeContextBar(); panel.classList.add('open'); if (btn) btn.innerHTML = '✕ Close Blaze'; /* Initialise chat on first open */ if (!blazeChat) { var ctx = buildBlazeContext(); blazeChat = LumanChat.init({ coach: 'blaze', containerId: 'blazeChatContainer', context: ctx, displayName: 'Blaze', initialMessage: buildBlazeInitialMessage() }); /* Start watching for the upgrade marker immediately after init */ watchForUpgradeMarker(); } } else { panel.classList.remove('open'); if (btn) btn.innerHTML = '🔥 Ask Blaze'; } }