1
2
3
4
βœ“
Step 1 of 4

Enter your website URL

We'll scan your site's primary pages and index the content so your AI assistant can answer visitor questions from day one.

Free Free setup indexes up to 20 primary first-level pages. Upgrade later for more.
Your homepage and sitemap will be used to discover primary pages automatically.
Step 2 of 4

Choose what to include

We've pulled your top-level pages. Uncheck any you don't want β€” we'll swap in another option from your site.

Loading pages…
Limit reached β€” uncheck a page to free a slot.
Step 3 of 4

Set navigation shortcuts

Step 4 of 4

Pick your brand color

We pulled the colors that stood out on your header. Pick the one that should skin the chat button and header, or set a custom hex.

Your AI
Online Β· Ask me anything
This is roughly how the chat header will look on your site.

Building your AI…

Scanning and indexing your pages. This usually takes a few minutes.

Starting… 0%
βœ…
Setup complete

Your AI chat is ready

Save this site to your account

Get analytics, conversation history, and live human takeover.

Sign up free β†’
β€”
Pages indexed
β€”
In your plan
β€”
Pages discovered
Nav shortcuts

Install Yap Chat on your site

The block below is the prompt. Paste it into Claude Code, Cursor, Copilot, Codex, Lovable, v0, Replit, or any AI coding agent. It will detect your framework β€” Next.js, React, Vue, Svelte, Astro, Remix, Nuxt, SvelteKit, Gatsby, plain HTML, WordPress, Shopify, Webflow, Squarespace, or Wix β€” and install the script in the correct global layout.

Hand this to your AI coding agent:

Open your site's main HTML template (or shared header/footer include) and paste the <script> block just before the closing </body> tag. If you have a shared include, paste it there once instead of on every page.

Paste this before </body>:

WordPress admin β†’ Appearance β†’ Theme File Editor β†’ open footer.php and paste the script just before </body> (or before wp_footer()). Alternative: install the Insert Headers and Footers plugin or WPCode and paste the script into the Footer slot.

Paste this into the Footer slot:

Shopify admin β†’ Online Store β†’ Themes β†’ β‹― β†’ Edit code β†’ open layout/theme.liquid and paste the script just before </body>. Save the file β€” the change goes live immediately on the published theme.

Paste this into layout/theme.liquid:

Webflow project β†’ Project Settings β†’ Custom Code β†’ Footer Code β†’ paste the script tag and click Save Changes. Then publish the site for the change to take effect on the live domain.

Paste this into Footer Code:

Squarespace site β†’ Settings β†’ Advanced β†’ Code Injection β†’ paste the script into the Footer field and save. The widget loads on every page on the next reload.

Paste this into the Footer field:

Want more pages indexed?

`; } // ── STEP 5: PROGRESS ───────────────────────────────────────────────────────── function goToProgressStep() { if (crawlDone) { goToSuccessStep(); return; } setStep('progress'); } async function doCrawl() { let attempts = 0; while (attempts < 400) { attempts++; try { const res = await fetch(API + `/api/v1/sites/${currentSiteId}/crawl-next`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ batchSize: 3 }), }); if (!res.ok) { await sleep(2000); continue; } const data = await res.json(); crawlFinalIndexed = data.indexed; crawlFinalTotal = data.total; const processed = data.total - data.remaining; const pct = data.total > 0 ? Math.round((processed / data.total) * 100) : 0; updateProgress(pct, processed, data.total, data.indexed); document.getElementById('miniBar').style.width = pct + '%'; if (data.done) break; await sleep(300); } catch { await sleep(2000); } } crawlDone = true; if (getCurrentStep() === 'progress') goToSuccessStep(); } function updateProgress(pct, processed, total, indexed) { document.getElementById('progressBar').style.width = pct + '%'; document.getElementById('progressPct').textContent = pct + '%'; document.getElementById('progressLabel').textContent = total > 0 ? `${indexed} pages indexed Β· ${processed} of ${total} scanned` : `${indexed} pages indexed`; } // ── STEP 6: SUCCESS ────────────────────────────────────────────────────────── function renderSuccessIcon() { const host = document.getElementById('successIcon'); if (!host) return; host.innerHTML = ''; if (currentIconUrl) { const img = document.createElement('img'); img.src = currentIconUrl; img.alt = currentBrandName ? `${currentBrandName} icon` : 'Site icon'; img.referrerPolicy = 'no-referrer'; img.onerror = () => { host.textContent = 'βœ…'; }; host.appendChild(img); const badge = document.createElement('span'); badge.className = 'success-badge'; badge.textContent = 'βœ“'; host.appendChild(badge); } else { host.textContent = 'βœ…'; } } function goToSuccessStep() { setStep('success'); renderSuccessIcon(); document.getElementById('successTitle').textContent = currentBrandName ? `Your ${currentBrandName} AI chat is ready` : 'Your AI chat is ready'; document.getElementById('statIndexed').textContent = crawlFinalIndexed || 'β€”'; document.getElementById('statInPlan').textContent = crawlFinalTotal || currentPageLimit || 'β€”'; document.getElementById('statIdentified').textContent = crawlFinalDiscovered || crawlFinalTotal || 'β€”'; document.getElementById('statNavLinks').textContent = navCheckedUrls.size || 'β€”'; if (selectedBrandColor) currentScriptTag = buildScriptTag(currentSiteId, selectedBrandColor); currentAIPrompt = buildAIPrompt(currentSiteId, currentScriptTag, currentOriginUrl); activePlatform = 'ai'; renderAllCodeBlocks(); wireRegisterPrompt(); document.getElementById('successSub').textContent = `${crawlFinalIndexed || 'Your'} pages are indexed. Hand the prompt below to your AI agent β€” or pick a platform tab to paste the script tag manually.`; renderTiers(); } // ── PRICING / TIER UPGRADE ────────────────────────────────────────────────── function setBilling(mode) { billing = mode; document.getElementById('billMonthly').classList.toggle('active', mode === 'monthly'); document.getElementById('billAnnual').classList.toggle('active', mode === 'annual'); renderTiers(); } function renderTiers() { const grid = document.getElementById('tierGrid'); if (!grid) return; grid.innerHTML = ''; for (const t of TIERS) { if (t.id === 'free') continue; const isCurrent = t.id === currentTier; const card = document.createElement('div'); card.className = 'tier-card' + (isCurrent ? ' current' : '') + (t.featured && !isCurrent ? ' featured' : ''); const name = document.createElement('div'); name.className = 'tier-name'; name.textContent = t.name; const price = document.createElement('div'); price.className = 'tier-price'; const showAnnual = billing === 'annual' && t.monthly > 0; if (t.monthly === 0) { price.innerHTML = `$0/mo`; } else if (showAnnual) { price.innerHTML = `$${t.monthly}$${t.annualMonthly}/mo`; } else { price.innerHTML = `$${t.monthly}/mo`; } const note = document.createElement('div'); note.className = 'tier-annual-note'; if (showAnnual) { const yearlySavings = t.monthly * 12 - t.annualTotal; note.innerHTML = `Save $${yearlySavings}/yr Β· $${t.annualTotal} billed annually`; } else if (t.monthly > 0) { note.textContent = `Save 20% with annual`; } const pages = document.createElement('div'); pages.className = 'tier-pages'; pages.textContent = t.pages; const cta = document.createElement('button'); cta.type = 'button'; cta.className = 'tier-cta' + (isCurrent ? ' current' : (t.featured ? ' primary' : '')); cta.textContent = isCurrent ? 'Current plan' : (t.id === 'free' ? 'Stay on Free' : `Choose ${t.name}`); if (isCurrent) { cta.disabled = true; } else { cta.onclick = () => selectTier(t.id); } card.append(name, price, note, pages, cta); grid.appendChild(card); } } function selectTier(tierId) { if (tierId === 'free' || tierId === currentTier) return; const subject = encodeURIComponent(`Upgrade ${currentSiteId} to ${tierId} (${billing})`); const body = encodeURIComponent( `Site: ${currentSiteId}\nPlan: ${tierId}\nBilling: ${billing}\n\nI'd like to activate this plan for my site.` ); window.location.href = `mailto:hello@yap-now.com?subject=${subject}&body=${body}`; } // ── HELPERS ────────────────────────────────────────────────────────────────── // Single source of truth for step order and visual-dot mapping. // Add / remove / reorder rows here and the rest of the flow follows. // `dot` is the 1-based indicator-dot index; steps sharing the same dot collapse on the indicator. const STEPS = [ { name: 'url', dot: 1 }, { name: 'pages', dot: 2 }, { name: 'nav', dot: 3 }, { name: 'color', dot: 4 }, { name: 'progress', dot: 4 }, { name: 'success', dot: 5 }, ]; const TOTAL_DOTS = Math.max(...STEPS.map(s => s.dot)); function setStep(name) { const step = STEPS.find(s => s.name === name); if (!step) return; STEPS.forEach(s => { const el = document.getElementById('step-' + s.name); if (el) el.classList.toggle('active', s.name === name); }); for (let i = 1; i <= TOTAL_DOTS; i++) { const dot = document.getElementById('sd' + i); if (dot) { dot.classList.toggle('active', i === step.dot); dot.classList.toggle('done', i < step.dot); } if (i < TOTAL_DOTS) { const line = document.getElementById('sl' + i); if (line) line.classList.toggle('done', i < step.dot); } } } function getCurrentStep() { for (const s of STEPS) { if (document.getElementById('step-' + s.name)?.classList.contains('active')) return s.name; } return STEPS[0].name; } function stopSitemapPolling() { if (sitemapPollId) { clearInterval(sitemapPollId); sitemapPollId = null; } cancelSitemapAutoSkip(); } function startSitemapAutoSkip() { cancelSitemapAutoSkip(); const banner = document.getElementById('sitemapMissingBanner'); const countdownEl = document.getElementById('sitemapMissingCountdown'); if (!banner || !countdownEl) { skipToProgress(); return; } let remaining = 5; countdownEl.textContent = String(remaining); banner.style.display = 'flex'; sitemapAutoSkipTimer = setInterval(() => { remaining -= 1; if (remaining <= 0) { cancelSitemapAutoSkip(); skipToProgress(); return; } countdownEl.textContent = String(remaining); }, 1000); } function cancelSitemapAutoSkip() { if (sitemapAutoSkipTimer) { clearInterval(sitemapAutoSkipTimer); sitemapAutoSkipTimer = null; } const banner = document.getElementById('sitemapMissingBanner'); if (banner) banner.style.display = 'none'; } function showFieldError(input, errEl, msg) { input.classList.add('error'); errEl.textContent = msg; errEl.classList.add('visible'); } function hideFieldError(input, errEl) { input.classList.remove('error'); errEl.classList.remove('visible'); } const SCRIPT_PLATFORMS = ['html', 'wordpress', 'shopify', 'webflow', 'squarespace']; function showPlatform(id, tab) { document.querySelectorAll('.platform-panel').forEach(el => el.classList.remove('active')); document.querySelectorAll('.ptab').forEach(el => el.classList.remove('active')); const panel = document.getElementById('panel-' + id); if (panel) panel.classList.add('active'); tab.classList.add('active'); activePlatform = id; } function renderAllCodeBlocks() { const aiEl = document.getElementById('code-ai'); if (aiEl) aiEl.textContent = currentAIPrompt; SCRIPT_PLATFORMS.forEach(p => { const el = document.getElementById('code-' + p); if (el) el.textContent = currentScriptTag; }); } async function copyCode(kind, btn) { const payload = kind === 'ai' ? currentAIPrompt : currentScriptTag; const original = btn.textContent; try { await navigator.clipboard.writeText(payload); btn.textContent = 'Copied!'; btn.classList.add('copied'); setTimeout(() => { btn.textContent = original; btn.classList.remove('copied'); }, 2000); } catch { btn.textContent = 'Select & copy'; } } function buildAIPrompt(siteId, scriptTag, originUrl) { const site = originUrl || '(your website)'; return `You are installing Yap Chat on a website. ABOUT YAP CHAT Yap Chat is an AI chat and voice assistant for websites, made by YapAI. One line of code installs it on any website. It answers visitor questions using the site's own content, supports text and voice input, works on desktop and mobile, and is fully hosted β€” there is no server to run and no package to install. THIS SITE Origin: ${site} Site ID: ${siteId} SCRIPT TAG (paste verbatim, do not modify): ${scriptTag} INSTALL Detect the framework or CMS, then add the script tag once, globally, just before (or the framework's equivalent global slot): - Next.js App Router: app/layout.tsx, inside , via