From f5aebe51ef5f49c962a0110d8e2f0ecb1f66dbe8 Mon Sep 17 00:00:00 2001 From: Lissan <150966211+DataForSolution@users.noreply.github.com> Date: Tue, 2 Sep 2025 12:29:44 -0400 Subject: [PATCH] feat: add floating AI panel (bottom-left) using gpt5.js --- assets/gpt5-panel.js | 182 +++++++++++++++++++++++++------------------ 1 file changed, 106 insertions(+), 76 deletions(-) diff --git a/assets/gpt5-panel.js b/assets/gpt5-panel.js index a54be94..3da6654 100644 --- a/assets/gpt5-panel.js +++ b/assets/gpt5-panel.js @@ -1,92 +1,122 @@ -// Floating AI Summary panel for OraDigit (ES5-safe). -// Requires window.gpt5 from /assets/gpt5.js +// OraDigit Floating AI Panel (bottom-left) — uses window.gpt5 +// Safe to include site-wide. Avoids clash with bottom-right chatbot. (function () { - if (typeof window.gpt5 !== "object") { - console.warn("gpt5.js not loaded; panel will not initialize."); + 'use strict'; + + if (!window.gpt5) { + console.warn('[gpt5-panel] window.gpt5 not found. Make sure /assets/gpt5.js is included.'); return; } - - function pageText() { - var el = document.querySelector("main") || document.querySelector("article") || - document.querySelector(".content") || document.querySelector(".post-content") || - document.getElementById("content") || document.body; - var t = (el && (el.innerText || el.textContent)) || ""; - t = t.replace(/\s+/g, " ").trim(); - if (t.length > 6000) t = t.slice(0, 6000); - return t; + if (document.getElementById('od-ai-toggle') || document.getElementById('od-ai-panel')) { + return; // already installed } - function init() { - if (document.getElementById("aiSumPanel")) return; // already added + // ------- Styles (scoped) ------- + var style = document.createElement('style'); + style.textContent = + '#od-ai-toggle{position:fixed;left:20px;bottom:20px;z-index:9999;background:#111827;color:#fff;border:0;border-radius:9999px;padding:10px 14px;font:600 14px/1.1 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto;border:1px solid #1f2937;box-shadow:0 4px 14px rgba(0,0,0,.2);cursor:pointer}'+ + '#od-ai-toggle:hover{background:#0b1220}'+ + '#od-ai-panel{position:fixed;left:20px;bottom:70px;z-index:9999;width:360px;max-width:95vw;background:#ffffff;border:1px solid #e5e7eb;border-radius:14px;box-shadow:0 12px 32px rgba(0,0,0,.18);overflow:hidden}'+ + '@media (prefers-color-scheme: dark){#od-ai-panel{background:#0b1220;border-color:#1f2937}}'+ + '#od-ai-head{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid #e5e7eb}'+ + '@media (prefers-color-scheme: dark){#od-ai-head{border-color:#1f2937}}'+ + '#od-ai-title{font:600 14px/1.2 ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto;color:#111827}'+ + '@media (prefers-color-scheme: dark){#od-ai-title{color:#e5e7eb}}'+ + '#od-ai-close{background:transparent;border:0;color:#6b7280;font:700 16px/1 ui-sans-serif;cursor:pointer}'+ + '#od-ai-body{padding:10px 12px;max-height:55vh;overflow:auto}'+ + '#od-ai-out{white-space:pre-wrap;font:500 14px/1.4 ui-sans-serif,system-ui;min-height:60px;color:#111827}'+ + '@media (prefers-color-scheme: dark){#od-ai-out{color:#e5e7eb}}'+ + '#od-ai-row{display:flex;gap:8px;margin:10px 0 4px}'+ + '#od-ai-msg{flex:1;min-height:44px;max-height:120px;padding:10px;border-radius:10px;border:1px solid #e5e7eb;background:#fff;color:#111827;resize:vertical}'+ + '@media (prefers-color-scheme: dark){#od-ai-msg{background:#0b1220;border-color:#1f2937;color:#e5e7eb}}'+ + '.od-btn{border:0;border-radius:10px;padding:10px 12px;font:600 13px/1 ui-sans-serif;cursor:pointer}'+ + '.od-btn.primary{background:#111827;color:#fff} .od-btn.primary:hover{background:#0b1220}'+ + '.od-btn.neutral{background:#f3f4f6;color:#111827} .od-btn.neutral:hover{background:#e5e7eb}'+ + '@media (prefers-color-scheme: dark){.od-btn.neutral{background:#111827;color:#e5e7eb} .od-btn.neutral:hover{background:#0b1220}}'; + document.head.appendChild(style); + + // ------- Markup ------- + var toggle = document.createElement('button'); + toggle.id = 'od-ai-toggle'; + toggle.type = 'button'; + toggle.setAttribute('aria-expanded', 'false'); + toggle.textContent = 'Ask AI'; - // panel - var box = document.createElement("div"); - box.id = "aiSumPanel"; - box.style.cssText = "position:fixed;right:16px;bottom:16px;z-index:2147483647;background:#fff;border:1px solid #ddd;border-radius:8px;padding:10px;box-shadow:0 6px 24px rgba(0,0,0,.12);max-width:520px;width:min(92vw,520px)"; - box.innerHTML = - '
' + - 'AI Summary' + - '' + + var wrap = document.createElement('div'); + wrap.id = 'od-ai-panel'; + wrap.setAttribute('role', 'dialog'); + wrap.setAttribute('aria-modal', 'true'); + wrap.style.display = 'none'; + wrap.innerHTML = + '
' + + '
OraDigit Assistant
' + + '' + + '
' + + '
' + + '
' + + '
' + + '' + + '' + + '' + '
' + - '
' + - '
' + - '' + - '
'; - document.body.appendChild(box); + '
'; - var btn = document.getElementById("aiSumBtn"); - var stop = document.getElementById("aiStopBtn"); - var out = document.getElementById("aiOut"); + document.body.appendChild(toggle); + document.body.appendChild(wrap); - var activeSession = 0; + // ------- Behavior ------- + var open = false; + var out = wrap.querySelector('#od-ai-out'); + var msg = wrap.querySelector('#od-ai-msg'); + var send = wrap.querySelector('#od-ai-send'); + var stop = wrap.querySelector('#od-ai-stop'); + var close= wrap.querySelector('#od-ai-close'); + var current = null; - btn.onclick = function () { - var t = pageText(); - if (!t) { out.textContent = "Sorry—no readable content found on this page."; return; } + function setOpen(v){ + open = !!v; + wrap.style.display = open ? 'block' : 'none'; + toggle.setAttribute('aria-expanded', String(open)); + if (open) setTimeout(function(){ msg.focus(); }, 0); + } - activeSession = Date.now(); - var mySession = activeSession; + toggle.addEventListener('click', function(){ setOpen(!open); }); + close.addEventListener('click', function(){ setOpen(false); }); - btn.disabled = true; - stop.disabled = false; - out.textContent = "Thinking…"; - var full = ""; + function startQuery(text){ + out.textContent = ''; + send.disabled = true; + stop.disabled = false; + current = window.gpt5.ask(text, { + onToken: function(tok){ out.textContent += tok; } + }).then(function(){ /* done */ }).catch(function(err){ + out.textContent = 'Error: ' + (err && err.message || err); + }).finally(function(){ + send.disabled = false; stop.disabled = true; + }); + } - window.gpt5.stream({ - system: "You are LLbot for OraDigit.com. Summarize for executives and prospects in 5–7 concise bullets.", - messages: [{ role: "user", content: "Summarize this page:\n\n" + t }], - onToken: function (ch) { - if (mySession !== activeSession) return; // ignore late chunks - // smart spacing to avoid words sticking together - if (full && /[A-Za-z0-9]$/.test(full) && /^[A-Za-z0-9]/.test(ch)) { ch = " " + ch; } - full += ch; - out.textContent = full; - } - }) - .then(function () { - if (mySession !== activeSession) return; - btn.disabled = false; - stop.disabled = true; - }) - .catch(function (e) { - if (mySession !== activeSession) return; - btn.disabled = false; - stop.disabled = true; - out.textContent = "Error: " + (e && e.message ? e.message : e); - }); - }; + send.addEventListener('click', function(){ + var text = String(msg.value || '').trim(); + if (!text) return; + startQuery(text); + }); - stop.onclick = function () { - activeSession = 0; // cancel updates - try { window.gpt5.abort(); } catch (e) {} - btn.disabled = false; - stop.disabled = true; - }; - } + stop.addEventListener('click', function(){ + window.gpt5.abort(); + }); + + msg.addEventListener('keydown', function (e) { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + send.click(); + } + }); + + // expose a tiny hook if you ever want to control it elsewhere + window.oraPanel = { + open: function(){ setOpen(true); }, + close: function(){ setOpen(false); } + }; - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", init); - } else { - init(); - } })();