Gush AI
`; const blob = new Blob(['\ufeff' + fullHtml], { type: 'application/msword' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = docTitle + '.doc'; a.click(); showToast('Word Document (.doc) exported successfully!', 'success'); } else if (format === 'pdf') { generatePdfDownload(cleanHtmlBody, docTitle); } } function generatePdfDownload(bodyHtml, title) { const wrapper = document.createElement('div'); wrapper.innerHTML = `

${escapeHtml(title)}

Generated ${escapeHtml(new Date().toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' }))}

${bodyHtml}
`; showToast('Generating PDF...', 'info'); html2pdf().set({ margin: [14, 12, 14, 12], filename: title.replace(/[^a-z0-9\-_]+/gi, '_') + '.pdf', html2canvas: { scale: 2, useCORS: true }, jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }, pagebreak: { mode: ['css', 'legacy'] } }).from(wrapper).save() .then(() => showToast('PDF downloaded successfully!', 'success')) .catch(err => showToast('PDF export failed: ' + err.message, 'error')); } function openPluginPanel(slug, label) { const frame = document.getElementById('plugin-iframe'); const title = document.getElementById('plugin-panel-title'); if (frame) { if (slug.startsWith('/') || slug.startsWith('http') || slug.startsWith('..')) { frame.src = slug; } else { frame.src = '/' + slug + '/'; } } if (title) title.textContent = label || slug; setMode('plugin'); } function openGushConnectModal() { if (!isLoggedIn) { const gate = document.getElementById('auth-gate'); if (gate) gate.style.display = 'flex'; return; } const modal = document.getElementById('gush-connect-modal'); if (modal) modal.style.display = 'flex'; if (typeof loadUserIntegrations === 'function') loadUserIntegrations(); if (typeof loadBuiltinServicesList === 'function') loadBuiltinServicesList(); } async function loadBuiltinServicesList() { const wrap = document.getElementById('builtin-services-list'); if (!wrap) return; wrap.innerHTML = '
Loading available services…
'; try { const services = (typeof loadAvailableServices === 'function') ? await loadAvailableServices(true) : await fetch('?action=get_available_services').then(r => r.json()).then(r => (r && r.ok) ? r.services : []); if (!services || !services.length) { wrap.innerHTML = '
No services installed. Drop a folder into /services/ — see /services/README.md.
'; return; } wrap.innerHTML = services.map(function (s) { const priceLabel = (s.pricing && (s.pricing.free_limit > 0 || s.pricing.price_ngn > 0)) ? `${s.pricing.free_limit} free / ${s.pricing.free_window_hours}h, then ₦${Number(s.pricing.price_ngn).toFixed(2)}` : 'Free'; const visBadge = s.visibility === 'private' ? 'Private' : ''; return `
${esc(s.name || s.key)} ${esc(s.command || '')}${visBadge}
${esc(s.description || '')}
${esc(priceLabel)}
`; }).join(''); } catch (e) { wrap.innerHTML = '
Could not load services.
'; } } function closeGushConnectModal() { const modal = document.getElementById('gush-connect-modal'); if (modal) modal.style.display = 'none'; } function openAddServiceModal() { if (!isLoggedIn) { const gate = document.getElementById('auth-gate'); if (gate) gate.style.display = 'flex'; return; } const modal = document.getElementById('add-service-modal'); if (modal) modal.style.display = 'flex'; const status = document.getElementById('svc-install-status'); if (status) status.innerHTML = ''; document.getElementById('svc-shipping-rows').innerHTML = ''; toggleServiceStoreFields(); } function closeAddServiceModal() { const modal = document.getElementById('add-service-modal'); if (modal) modal.style.display = 'none'; } function toggleServiceStoreFields() { const type = document.getElementById('svc-type').value; const wrap = document.getElementById('svc-store-fields'); if (!wrap) return; const isStore = (type === 'product' || type === 'ecommerce'); wrap.style.display = isStore ? 'flex' : 'none'; } function addShippingRow() { const rows = document.getElementById('svc-shipping-rows'); const row = document.createElement('div'); row.style.cssText = 'display:flex;gap:6px;align-items:center;'; row.innerHTML = ` `; rows.appendChild(row); } function collectShippingOptions() { const rows = document.querySelectorAll('#svc-shipping-rows > div'); const out = []; rows.forEach(r => { const label = r.querySelector('.svc-ship-label').value.trim(); if (!label) return; out.push({ label, cost_ngn: parseFloat(r.querySelector('.svc-ship-cost').value || '0') || 0, eta: r.querySelector('.svc-ship-eta').value.trim(), }); }); return out; } async function submitServiceInstall(evt) { evt.preventDefault(); const status = document.getElementById('svc-install-status'); const btn = document.getElementById('svc-submit-btn'); const form = document.getElementById('add-service-form'); const fd = new FormData(form); const type = document.getElementById('svc-type').value; if (type === 'product' || type === 'ecommerce') { fd.set('shipping_options', JSON.stringify(collectShippingOptions())); } btn.disabled = true; btn.innerHTML = ' Scanning package for corruption & malware…'; status.style.color = 'var(--muted)'; status.textContent = 'This can take a few seconds — every file is scanned before anything is saved.'; try { const res = await fetch('?action=install_service', { method: 'POST', body: fd }); const data = await res.json(); if (data && data.ok) { status.style.color = 'var(--emerald,#34d399)'; status.textContent = data.message || 'Installed.'; if (typeof loadBuiltinServicesList === 'function') loadBuiltinServicesList(); if (typeof loadAvailableServices === 'function') loadAvailableServices(true); setTimeout(closeAddServiceModal, 1800); } else { status.style.color = 'var(--rose,#f87171)'; status.textContent = (data && data.error) || "Service couldn't be installed."; } } catch (e) { status.style.color = 'var(--rose,#f87171)'; status.textContent = "Service couldn't be installed: network or server error."; } finally { btn.disabled = false; btn.innerHTML = ' Scan & Install'; } return false; } function switchGushConnectSubTab(tabName) { const tabs = ['integrations', 'sdk', 'marketplace', 'community']; tabs.forEach(t => { const btn = document.getElementById('gc-tab-' + t); const pane = document.getElementById('gc-pane-' + t); if (btn) { if (t === tabName) { btn.classList.add('active', 'text-cyan-400'); btn.classList.remove('text-slate-400'); } else { btn.classList.remove('active', 'text-cyan-400'); btn.classList.add('text-slate-400'); } } if (pane) { pane.style.display = (t === tabName) ? 'block' : 'none'; } }); if (tabName === 'marketplace' && typeof loadMarketplaceList === 'function') loadMarketplaceList(); } document.addEventListener('DOMContentLoaded', function () { const sel = document.getElementById('search-pref'); const savedPref = localStorage.getItem('gush_search_pref'); if (sel && savedPref) { sel.value = savedPref; if (typeof liveSearchPref !== 'undefined') { liveSearchPref = savedPref; } window.liveSearchPref = savedPref; } if (typeof toggleDeepBadgeVisibility === 'function') toggleDeepBadgeVisibility(); if (typeof applyGroundingUI === 'function') applyGroundingUI(); });