nexus-web-assistant 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/nexus-assistant.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
Add this single script tag to your website:
|
|
56
56
|
|
|
57
57
|
```html
|
|
58
|
-
<script defer src="https://cdn.jsdelivr.net/
|
|
58
|
+
<script defer src="https://cdn.jsdelivr.net/npm/nexus-web-assistant/dist/nexus-assistant.min.js"></script>
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
### **Option 2: Self-Hosted**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{"use strict";const n="https://api.groq.com/openai/v1/chat/completions",e="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",t="meta-llama/llama-4-scout-17b-16e-instruct",o="Nexus AI",a="Hey! I'm Nexus AI, your intelligent assistant. Ask me anything about Website!",i="\nYou are Nexus AI, a smart assistant. Use the page context provided in every response.\n\n**Rules:**\n- Always prioritize the given page context over general knowledge.\n- Quote specific details (headings, tables, code, lists, etc.) from the context.\n- Structure answers clearly with bullet points or headings where helpful.\n- Be crisp, professional, and friendly. Use Markdown.\n- If information is not in context, politely say so and offer to help with related topics.\n- Keep responses concise (under 600 tokens).\n ",r={bg:"#0a0a0a",panelBg:"rgba(12, 12, 12, 0.85)",border:"#2a2a2a",borderHover:"#444444",textPrimary:"#f5f5f5",textSecondary:"#a0a0a0",userBubble:"#1e1e1e",botBubble:"transparent"},s=n=>document.getElementById(n);function c(n,e){if(!n)return!1;const t=void 0!==e?e:!n.classList.contains("open");return n.classList.toggle("open",t),t&&setTimeout(()=>s("aiInput")?.focus(),100),t}function l(n,e,t="bot",o=null){if(!n)return null;const a=document.createElement("div");if(a.className=`msg ${t}`,"bot"===t){const n=function(n){return"undefined"!=typeof marked&&marked.parse?marked.parse(n,{breaks:!0,gfm:!0}):n.replace(/\n/g,"<br>")}(e);a.innerHTML=n,"undefined"!=typeof hljs&&a.querySelectorAll("pre code").forEach(n=>{hljs.highlightElement(n)})}else{let n=e||"";o&&(n+=`<br><img src="${o}" class="image-preview" alt="attached">`),a.innerHTML=n}return n.appendChild(a),n.scrollTo({top:n.scrollHeight,behavior:"smooth"}),a}let d=null;function p(n,e){n&&(d&&(d.remove(),d=null),e&&(d=document.createElement("div"),d.className="typing-indicator",d.innerHTML="<span></span><span></span><span></span>",n.appendChild(d),n.scrollTo({top:n.scrollHeight,behavior:"smooth"})))}function u(n,e){if(!n)return;if(n.innerHTML="",!e)return;const t=document.createElement("div");t.className="image-preview-pill",t.innerHTML=`\n <img src="${e.dataUrl}" alt="preview">\n <span class="file-name">${e.name}</span>\n <button class="remove-file" id="aiRemoveFile"><i class="fas fa-times"></i></button>\n `,n.appendChild(t),document.getElementById("aiRemoveFile")?.addEventListener("click",()=>{})}const m={isOpen:!1,isProcessing:!1,attachedFile:null,conversationHistory:[]};function g(n){Object.assign(m,n)}const f="NexusAICache",b="pageData";let h=null;function x(){return new Promise((n,e)=>{if(h)return void n(h);const t=indexedDB.open(f,1);t.onupgradeneeded=n=>{const e=n.target.result;if(!e.objectStoreNames.contains(b)){const n=e.createObjectStore(b,{keyPath:"id"});n.createIndex("url","url",{unique:!1}),n.createIndex("timestamp","timestamp",{unique:!1}),console.log("📦 IndexedDB store created")}},t.onsuccess=e=>{h=e.target.result,n(h)},t.onerror=n=>{e(n.target.error)}})}async function y(n){try{const e=(await x()).transaction(b,"readwrite").objectStore(b),t=window.location.href,o={id:t,url:t,data:n,timestamp:Date.now()};return new Promise((n,t)=>{const a=e.put(o);a.onsuccess=()=>{console.log("💾 Data cached in IndexedDB"),n()},a.onerror=n=>{t(n.target.error)}})}catch(n){console.warn("IndexedDB write failed:",n)}}let w=null,v=null;function k(n){return new Promise((e,t)=>{const o=document.createElement("script");o.src=n,o.async=!0,o.onload=e,o.onerror=t,document.head.appendChild(o)})}function S(){console.log("🕷️ Advanced scraping started...");const n={title:document.title,url:window.location.href,timestamp:(new Date).toISOString(),pageType:E(),readability:null,structuredData:[],markdown:"",summary:"",metadata:{}};try{if(w){const e=document.cloneNode(!0),t=new w(e).parse();if(t){if(n.readability={title:t.title,byline:t.byline,excerpt:t.excerpt,content:t.content,textContent:t.textContent},v){const e=new v({headingStyle:"atx",codeBlockStyle:"fenced",emDelimiter:"*",bulletListMarker:"-"});e.addRule("strikethrough",{filter:["del","s","strike"],replacement:function(n){return"~~"+n+"~~"}}),n.markdown=e.turndown(t.content)}n.summary=t.excerpt||t.textContent.substring(0,300)}}}catch(e){console.warn("Readability failed, falling back to basic scraper:",e),n.readability=null}try{document.querySelectorAll('script[type="application/ld+json"]').forEach(e=>{try{const t=JSON.parse(e.textContent);n.structuredData.push(t)}catch(n){}})}catch(n){console.warn("JSON-LD extraction failed:",n)}if(n.metadata=function(){const n={},e=document.querySelector('meta[name="description"]');e&&(n.description=e.content);const t=document.querySelector('meta[name="keywords"]');t&&(n.keywords=t.content),["title","description","image","url","type","site_name"].forEach(e=>{const t=document.querySelector(`meta[property="og:${e}"]`);t&&(n[`og_${e}`]=t.content)}),["card","title","description","image"].forEach(e=>{const t=document.querySelector(`meta[name="twitter:${e}"]`);t&&(n[`twitter_${e}`]=t.content)});const o=document.querySelector('link[rel="canonical"]');return o&&(n.canonical=o.href),n}(),!n.readability){console.log("⚠️ Using fallback scraper");const e=function(){const n=document.body.cloneNode(!0);["nav","header","footer","aside",".sidebar",".navigation",".menu",".ads",".cookie-banner",".popup",".overlay","script","style","noscript","iframe"].forEach(e=>{n.querySelectorAll(e).forEach(n=>n.remove())});let e=n.textContent||"";e=e.replace(/\s+/g," ").trim();const t=e.substring(0,500)+"...";return{content:e,summary:t}}();n.markdown=e.content,n.summary=e.summary}return n.aiContext=$(n),console.log("✅ Advanced scraping complete"),n}function E(){const n=window.location.href,e=document.title.toLowerCase(),t=document.querySelector('meta[property="og:type"]');if(t){const n=t.content;if(n.includes("article"))return"article";if(n.includes("product"))return"product";if(n.includes("website"))return"website"}return n.includes("/blog/")||n.includes("/post/")||e.includes("blog")?"blog":n.includes("/product/")||n.includes("/shop/")?"product":n.includes("/portfolio/")||n.includes("/project/")?"portfolio":n===window.location.origin+"/"||n===window.location.origin?"homepage":"general"}function $(n){let e="";if(e+=`# 📄 ${n.title}\n\n`,e+=`**URL:** ${n.url}\n`,e+=`**Type:** ${n.pageType}\n`,e+=`**Scraped:** ${new Date(n.timestamp).toLocaleString()}\n\n`,Object.keys(n.metadata).length>0){e+="## 📋 Metadata\n";for(const[t,o]of Object.entries(n.metadata))o&&o.length>0&&(e+=`- **${t}:** ${o}\n`);e+="\n"}if(n.structuredData&&n.structuredData.length>0&&(e+="## 📊 Structured Data (Schema.org)\n",n.structuredData.forEach((n,t)=>{e+=`\n### Schema ${t+1}\n`,e+=`\`\`\`json\n${JSON.stringify(n,null,2)}\n\`\`\`\n`}),e+="\n"),n.markdown){e+="## 📝 Main Content\n\n";const t=4e3,o=n.markdown.length>t?n.markdown.substring(0,t)+"... (truncated)":n.markdown;e+=o,e+="\n\n"}return n.summary&&(e+="## 📌 Summary\n\n",e+=n.summary,e+="\n\n"),e+="---\n",e+="*🤖 AI Instructions: Use this context to answer user questions about this page. Be precise, reference specific details from the content, and maintain a friendly professional tone.*\n",e}async function I(o,a,r=null){if(m.isProcessing)return;g({isProcessing:!0});const s=document.getElementById("aiSendBtn"),c=document.getElementById("aiInput");s&&(s.disabled=!0),c&&(c.disabled=!0),p(o,!0);const d=[];if(a&&d.push({type:"text",text:a}),r&&d.push({type:"image_url",image_url:{url:r}}),m.conversationHistory.push({role:"user",content:a||"(image)"}),m.conversationHistory.length>11){const n=m.conversationHistory[0],e=m.conversationHistory.slice(-10);m.conversationHistory=[n,...e]}try{const a=await async function(){let n=await async function(){try{const n=(await x()).transaction(b,"readonly").objectStore(b),e=window.location.href;return new Promise((t,o)=>{const a=n.get(e);a.onsuccess=n=>{const o=n.target.result;if(!o)return void t(null);const a=Date.now();if(a-o.timestamp>864e5)return async function(n){try{(await x()).transaction(b,"readwrite").objectStore(b).delete(n)}catch(n){console.warn("IndexedDB delete failed:",n)}}(e),void t(null);console.log(`📦 Cache hit! Age: ${Math.floor((a-o.timestamp)/6e4)} minutes`),t(o.data)},a.onerror=n=>{o(n.target.error)}})}catch(n){return console.warn("IndexedDB read failed:",n),null}}();if(n)return console.log("📦 Using cached page data"),n;console.log("🔍 Scraping page data...");const e=$(S());return await y(e),console.log("💾 Page data cached"),e}(),r=[{role:"system",content:`${i}\n\n## Current Page Context\n${a}`}];for(let n=1;n<m.conversationHistory.length-1;n++)r.push({role:m.conversationHistory[n].role,content:m.conversationHistory[n].content});r.push({role:"user",content:d});const s=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`},body:JSON.stringify({model:t,messages:r,temperature:.7,max_tokens:600,stream:!1})});if(!s.ok){const n=await s.json().catch(()=>({}));throw new Error(`API error ${s.status}: ${n.error?.message||s.statusText}`)}const c=await s.json(),u=c.choices?.[0]?.message?.content||"Sorry, I didn't understand that.";m.conversationHistory.push({role:"assistant",content:u}),p(o,!1),l(o,u,"bot")}catch(n){console.error("Groq API Error:",n),p(o,!1),l(o,`⚠️ Oops! ${n.message||"Something went wrong."} Please try again.`,"bot")}finally{g({isProcessing:!1}),s&&(s.disabled=!1),c&&(c.disabled=!1,c.value="",c.focus())}}async function P(){await async function(){const n=document.createElement("link");n.href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css",n.rel="stylesheet",document.head.appendChild(n);const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap",e.rel="stylesheet",document.head.appendChild(e);const t=document.createElement("script");t.src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.0/marked.min.js",t.async=!0,document.head.appendChild(t);const o=document.createElement("link");o.href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css",o.rel="stylesheet",document.head.appendChild(o);const a=document.createElement("script");a.src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js",a.async=!0,document.head.appendChild(a),await async function(){await Promise.all([k("https://cdnjs.cloudflare.com/ajax/libs/readability/0.4.4/Readability.min.js"),k("https://cdnjs.cloudflare.com/ajax/libs/turndown/7.1.2/turndown.min.js")]),w=window.Readability,v=window.TurndownService,console.log("📚 Scraper libraries loaded")}()}(),function(){const n=document.createElement("style");n.id="ai-chat-widget-styles",n.textContent=`\n @keyframes slideUpFade {\n 0% { opacity: 0; transform: translateY(16px); }\n 100% { opacity: 1; transform: translateY(0); }\n }\n @keyframes pulseDot {\n 0%, 100% { transform: scale(0.8); opacity: 0.5; }\n 50% { transform: scale(1.2); opacity: 1; }\n }\n @keyframes typing {\n 0%, 80%, 100% { opacity: 0.3; }\n 40% { opacity: 1; }\n }\n\n #ai-widget-root {\n all: initial;\n font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif;\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 999999;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n pointer-events: none;\n }\n #ai-widget-root * { box-sizing: border-box; pointer-events: auto; }\n\n /* ===== FIX: Font Awesome icons ===== */\n #ai-widget-root .fas,\n #ai-widget-root .far,\n #ai-widget-root .fab {\n font-family: 'Font Awesome 6 Free', 'Font Awesome 6 Brands' !important;\n font-weight: 900 !important; /* solid icons use 900 */\n font-style: normal;\n font-variant: normal;\n text-rendering: auto;\n -webkit-font-smoothing: antialiased;\n }\n #ai-widget-root .far {\n font-weight: 400 !important; /* regular */\n }\n #ai-widget-root .fab {\n font-family: 'Font Awesome 6 Brands' !important;\n font-weight: 400 !important;\n }\n\n /* ===== FLOATING ORB (FAB) ===== */\n .ai-fab {\n width: 56px;\n height: 56px;\n border-radius: 50%;\n background: #ffffff;\n border: 1px solid rgba(255,255,255,0.1);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #000000;\n transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1),\n transform 0.3s cubic-bezier(0.16, 1, 0.3, 1),\n box-shadow 0.25s ease;\n user-select: none;\n box-shadow: 0 8px 24px rgba(0,0,0,0.4);\n position: relative;\n z-index: 10;\n will-change: transform, opacity;\n }\n .ai-fab:hover {\n transform: scale(1.05);\n box-shadow: 0 12px 32px rgba(0,0,0,0.5);\n }\n .ai-fab .bot-icon i {\n color: #000000 !important;\n font-size: 24px !important;\n }\n\n /* Hide FAB when panel is open – smooth fade + scale */\n .ai-panel.open ~ .ai-fab {\n opacity: 0;\n transform: scale(0.8);\n pointer-events: none;\n }\n\n /* ===== CHAT PANEL ===== */\n .ai-panel {\n position: absolute;\n bottom: 0;\n right: 0;\n width: 400px;\n max-width: calc(100vw - 48px);\n height: 600px;\n max-height: calc(100vh - 48px);\n background: ${r.panelBg};\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border-radius: 20px;\n box-shadow: 0 24px 80px rgba(0,0,0,0.5), 0 0 0 1px ${r.border};\n display: flex;\n flex-direction: column;\n overflow: hidden;\n opacity: 0;\n visibility: hidden;\n transform: translateY(20px);\n transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), visibility 0.3s;\n }\n .ai-panel.open {\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n }\n\n /* ===== HEADER ===== */\n .ai-header {\n padding: 20px 24px;\n border-bottom: 1px solid ${r.border};\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-shrink: 0;\n background: transparent;\n }\n .ai-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n color: ${r.textPrimary};\n font-weight: 600;\n font-size: 16px;\n }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #10B981;\n box-shadow: 0 0 8px rgba(16, 185, 129, 0.5);\n }\n .ai-close-btn {\n background: transparent;\n border: none;\n color: ${r.textSecondary};\n font-size: 20px;\n cursor: pointer;\n transition: color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .ai-close-btn:hover {\n color: ${r.textPrimary};\n }\n\n /* ===== MESSAGES ===== */\n .ai-messages {\n flex: 1;\n padding: 24px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 24px;\n scroll-behavior: smooth;\n }\n .ai-messages::-webkit-scrollbar { width: 6px; }\n .ai-messages::-webkit-scrollbar-track { background: transparent; }\n .ai-messages::-webkit-scrollbar-thumb {\n background: ${r.border};\n border-radius: 10px;\n }\n\n /* ===== BUBBLES ===== */\n .msg {\n max-width: 85%;\n font-size: 14.5px;\n line-height: 1.6;\n word-break: break-word;\n animation: slideUpFade 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n color: ${r.textPrimary};\n }\n .msg.user {\n align-self: flex-end;\n background: ${r.userBubble};\n padding: 12px 18px;\n border-radius: 16px;\n border-bottom-right-radius: 4px;\n border: 1px solid ${r.border};\n }\n .msg.bot {\n align-self: flex-start;\n background: ${r.botBubble};\n max-width: 95%;\n }\n .msg.bot .bot-label {\n font-size: 12px;\n color: ${r.textSecondary};\n margin-bottom: 6px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .msg .image-preview {\n max-width: 200px;\n border-radius: 12px;\n margin-top: 8px;\n border: 1px solid ${r.border};\n }\n .msg.user .image-preview { max-width: 100%; }\n\n /* Markdown Cleanup */\n .msg.bot p { margin: 0 0 12px 0; }\n .msg.bot p:last-child { margin-bottom: 0; }\n .msg.bot code {\n background: #1e1e1e;\n padding: 2px 6px;\n border-radius: 4px;\n font-family: ui-monospace, monospace;\n font-size: 0.9em;\n border: 1px solid ${r.border};\n }\n .msg.bot pre {\n background: #111111;\n padding: 16px;\n border-radius: 12px;\n overflow-x: auto;\n margin: 12px 0;\n border: 1px solid ${r.border};\n }\n .msg.bot pre code { background: transparent; padding: 0; border: none; }\n .msg.bot ul, .msg.bot ol { margin: 8px 0 12px 20px; padding-left: 0; }\n .msg.bot li { margin-bottom: 6px; }\n .msg.bot a { color: #fff; text-decoration: underline; text-underline-offset: 4px; }\n\n /* ===== INPUT AREA ===== */\n .ai-input-area {\n padding: 16px 24px 24px;\n background: transparent;\n flex-shrink: 0;\n }\n .ai-input-container {\n display: flex;\n align-items: center;\n background: #161616;\n border: 1px solid ${r.border};\n border-radius: 24px;\n padding: 6px 6px 6px 16px;\n transition: border-color 0.2s, background 0.2s;\n }\n .ai-input-container:focus-within {\n border-color: #555;\n background: #1a1a1a;\n }\n .ai-input-container input {\n flex: 1;\n background: transparent;\n border: none;\n color: ${r.textPrimary};\n font-size: 14px;\n outline: none;\n font-family: inherit;\n padding: 8px 0;\n }\n .ai-input-container input::placeholder {\n color: ${r.textSecondary};\n }\n .icon-btn {\n background: transparent;\n border: none;\n color: ${r.textSecondary};\n width: 36px;\n height: 36px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: color 0.2s, background 0.2s;\n }\n .icon-btn:hover {\n color: #fff;\n background: ${r.borderHover};\n }\n .send-btn {\n background: #fff;\n color: #000;\n margin-left: 4px;\n }\n .send-btn:hover {\n background: #e0e0e0;\n color: #000;\n }\n .send-btn:disabled {\n background: #333;\n color: #666;\n cursor: not-allowed;\n }\n\n /* ===== PREVIEW PILL ===== */\n #aiPreviewContainer { margin-bottom: 12px; }\n .image-preview-pill {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n background: #1e1e1e;\n border: 1px solid ${r.border};\n border-radius: 8px;\n padding: 6px 12px 6px 6px;\n animation: slideUpFade 0.3s ease forwards;\n }\n .image-preview-pill img {\n width: 24px;\n height: 24px;\n border-radius: 4px;\n object-fit: cover;\n }\n .image-preview-pill .file-name {\n font-size: 12px;\n color: ${r.textPrimary};\n max-width: 120px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .image-preview-pill .remove-file {\n background: none;\n border: none;\n color: ${r.textSecondary};\n cursor: pointer;\n font-size: 14px;\n display: flex;\n align-items: center;\n }\n .image-preview-pill .remove-file:hover { color: #fff; }\n\n /* ===== TYPING INDICATOR ===== */\n .typing-indicator {\n display: flex;\n gap: 4px;\n padding: 8px 0;\n align-self: flex-start;\n }\n .typing-indicator span {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: ${r.textSecondary};\n animation: typing 1.4s infinite;\n }\n .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }\n .typing-indicator span:nth-child(3) { animation-delay: 0.4s; }\n\n /* ===== RESPONSIVE ===== */\n @media (max-width: 480px) {\n .ai-panel {\n width: calc(100vw - 32px);\n right: 16px;\n height: 80vh;\n max-height: calc(100vh - 48px);\n }\n .ai-fab {\n width: 50px;\n height: 50px;\n }\n .ai-fab .bot-icon { font-size: 20px; }\n }\n `,document.head.appendChild(n)}(),function(){const n=document.createElement("div");n.id="ai-widget-root",n.innerHTML=`\n <div class="ai-panel" id="aiPanel">\n <div class="ai-header">\n <div class="ai-header-title">\n <span class="status-dot"></span>\n ${o}\n </div>\n <button class="ai-close-btn" id="aiCloseBtn" aria-label="Close chat">\n <i class="fas fa-xmark"></i>\n </button>\n </div>\n <div class="ai-messages" id="aiMessages">\n <div class="msg bot">\n ${a}\n </div>\n </div>\n <div class="ai-input-area" id="aiInputArea">\n <div id="aiPreviewContainer"></div>\n <div class="ai-input-container">\n <button class="icon-btn" id="aiAttachBtn" aria-label="Attach image">\n <i class="fas fa-paperclip"></i>\n </button>\n <input type="text" id="aiInput" placeholder="Message..." autocomplete="off">\n <button class="icon-btn send-btn" id="aiSendBtn">\n <i class="fas fa-arrow-up"></i>\n </button>\n </div>\n </div>\n </div>\n <button class="ai-fab" id="aiFab" aria-label="Open AI Chat">\n <span class="bot-icon">\n <i class="fas fa-robot" style="color: #000000 !important; font-size: 22px;"></i>\n </span>\n </button>\n `,document.body.appendChild(n)}();const n=s("aiPanel"),e=s("aiMessages"),t=s("aiFab"),i=s("aiCloseBtn"),d=s("aiSendBtn"),p=s("aiInput"),h=s("aiAttachBtn"),E=s("aiPreviewContainer");!function(n){let e=window.location.href;new MutationObserver(()=>{window.location.href!==e&&(e=window.location.href,console.log("🔄 Page navigation detected"),n())}).observe(document,{subtree:!0,childList:!0});const t=history.pushState;history.pushState=function(){t.apply(this,arguments),setTimeout(()=>{window.location.href!==e&&(e=window.location.href,n())},500)}}(()=>{console.log("🔄 Page changed, refreshing context..."),async function(){console.log("🔄 Forcing page context refresh...");const n=$(S());await y(n),console.log("✅ Page context refreshed")}(),l(e,"🔄 Page context updated!","bot")});const P=document.createElement("input");P.type="file",P.accept="image/*",P.style.display="none",P.id="aiFileInput",document.body.appendChild(P),h?.addEventListener("click",()=>P.click()),P.addEventListener("change",n=>{const e=n.target.files?.[0];if(!e)return;if(!e.type.startsWith("image/"))return void alert("Please select an image file.");const t=new FileReader;t.onload=n=>{const t={dataUrl:n.target.result,name:e.name,type:e.type};g({attachedFile:t}),u(E,t);const o=document.getElementById("aiRemoveFile");o&&o.addEventListener("click",()=>{g({attachedFile:null}),u(E,null),p?.focus()}),p?.focus()},t.readAsDataURL(e),n.target.value=""}),t?.addEventListener("click",()=>{g({isOpen:c(n,!0)})}),i?.addEventListener("click",()=>{g({isOpen:c(n,!1)})}),document.addEventListener("click",e=>{const t=document.getElementById("ai-widget-root");t&&m.isOpen&&!t.contains(e.target)&&g({isOpen:c(n,!1)})});const j=()=>{const n=p?.value.trim();if(!n&&!m.attachedFile)return;if(m.isProcessing)return;const t=m.attachedFile?.dataUrl||null;l(e,n,"user",t),p.value="",u(E,null);const o=m.attachedFile?.dataUrl||null;g({attachedFile:null}),I(e,n,o)};d?.addEventListener("click",j),p?.addEventListener("keydown",n=>{"Enter"!==n.key||n.shiftKey||(n.preventDefault(),j())});const B=await async function(){try{const n=(await x()).transaction(b,"readonly").objectStore(b);return new Promise(e=>{const t=n.count();t.onsuccess=()=>{e({totalEntries:t.result,database:f,version:1})},t.onerror=()=>e(null)})}catch{return null}}();console.log("📊 Cache Stats:",B||"No cache yet"),console.log("✨ Nexus AI v2.0 initialized with advanced scraper!")}m.conversationHistory=[{role:"system",content:i}],"loading"===document.readyState?document.addEventListener("DOMContentLoaded",P):P()})();
|
|
1
|
+
(()=>{"use strict";const n={API_URL:"https://api.groq.com/openai/v1/chat/completions",API_KEY:"",MODEL:"meta-llama/llama-4-scout-17b-16e-instruct",BOT_NAME:"Nexus AI",GREETING:"Hey! I'm Nexus AI, your intelligent assistant. Ask me anything about this page!",SYSTEM_PROMPT:"\nYou are Nexus AI, a smart assistant. Use the page context provided in every response.\n\n**Rules:**\n- Always prioritize the given page context over general knowledge.\n- Quote specific details (headings, tables, code, lists, etc.) from the context.\n- Structure answers clearly with bullet points or headings where helpful.\n- Be crisp, professional, and friendly. Use Markdown.\n- If information is not in context, politely say so and offer to help with related topics.\n- Keep responses concise (under 600 tokens).\n ",COLORS:{bg:"#0a0a0a",panelBg:"rgba(12, 12, 12, 0.85)",border:"#2a2a2a",borderHover:"#444444",textPrimary:"#f5f5f5",textSecondary:"#a0a0a0",userBubble:"#1e1e1e",botBubble:"transparent"}},e=n=>document.getElementById(n);function t(n,t){if(!n)return!1;const o=void 0!==t?t:!n.classList.contains("open");return n.classList.toggle("open",o),o&&setTimeout(()=>e("aiInput")?.focus(),100),o}function o(n,e,t="bot",o=null){if(!n)return null;const a=document.createElement("div");if(a.className=`msg ${t}`,"bot"===t){const n=function(n){return"undefined"!=typeof marked&&marked.parse?marked.parse(n,{breaks:!0,gfm:!0}):n.replace(/\n/g,"<br>")}(e);a.innerHTML=n,"undefined"!=typeof hljs&&a.querySelectorAll("pre code").forEach(n=>{hljs.highlightElement(n)})}else{let n=e||"";o&&(n+=`<br><img src="${o}" class="image-preview" alt="attached">`),a.innerHTML=n}return n.appendChild(a),n.scrollTo({top:n.scrollHeight,behavior:"smooth"}),a}let a=null;function i(n,e){n&&(a&&(a.remove(),a=null),e&&(a=document.createElement("div"),a.className="typing-indicator",a.innerHTML="<span></span><span></span><span></span>",n.appendChild(a),n.scrollTo({top:n.scrollHeight,behavior:"smooth"})))}function r(n,e){if(!n)return;if(n.innerHTML="",!e)return;const t=document.createElement("div");t.className="image-preview-pill",t.innerHTML=`\n <img src="${e.dataUrl}" alt="preview">\n <span class="file-name">${e.name}</span>\n <button class="remove-file" id="aiRemoveFile"><i class="fas fa-times"></i></button>\n `,n.appendChild(t),document.getElementById("aiRemoveFile")?.addEventListener("click",()=>{})}const s={isOpen:!1,isProcessing:!1,attachedFile:null,conversationHistory:[]};function c(n){Object.assign(s,n)}const l="NexusAICache",d="pageData";let p=null;function u(){return new Promise((n,e)=>{if(p)return void n(p);const t=indexedDB.open(l,1);t.onupgradeneeded=n=>{const e=n.target.result;if(!e.objectStoreNames.contains(d)){const n=e.createObjectStore(d,{keyPath:"id"});n.createIndex("url","url",{unique:!1}),n.createIndex("timestamp","timestamp",{unique:!1}),console.log("📦 IndexedDB store created")}},t.onsuccess=e=>{p=e.target.result,n(p)},t.onerror=n=>{e(n.target.error)}})}async function m(n){try{const e=(await u()).transaction(d,"readwrite").objectStore(d),t=window.location.href,o={id:t,url:t,data:n,timestamp:Date.now()};return new Promise((n,t)=>{const a=e.put(o);a.onsuccess=()=>{console.log("💾 Data cached in IndexedDB"),n()},a.onerror=n=>{t(n.target.error)}})}catch(n){console.warn("IndexedDB write failed:",n)}}let g=null,f=null;function b(n){return new Promise((e,t)=>{const o=document.createElement("script");o.src=n,o.async=!0,o.onload=e,o.onerror=t,document.head.appendChild(o)})}function h(){console.log("🕷️ Advanced scraping started...");const n={title:document.title,url:window.location.href,timestamp:(new Date).toISOString(),pageType:x(),readability:null,structuredData:[],markdown:"",summary:"",metadata:{}};try{if(g){const e=document.cloneNode(!0),t=new g(e).parse();if(t){if(n.readability={title:t.title,byline:t.byline,excerpt:t.excerpt,content:t.content,textContent:t.textContent},f){const e=new f({headingStyle:"atx",codeBlockStyle:"fenced",emDelimiter:"*",bulletListMarker:"-"});e.addRule("strikethrough",{filter:["del","s","strike"],replacement:function(n){return"~~"+n+"~~"}}),n.markdown=e.turndown(t.content)}n.summary=t.excerpt||t.textContent.substring(0,300)}}}catch(e){console.warn("Readability failed, falling back to basic scraper:",e),n.readability=null}try{document.querySelectorAll('script[type="application/ld+json"]').forEach(e=>{try{const t=JSON.parse(e.textContent);n.structuredData.push(t)}catch(n){}})}catch(n){console.warn("JSON-LD extraction failed:",n)}if(n.metadata=function(){const n={},e=document.querySelector('meta[name="description"]');e&&(n.description=e.content);const t=document.querySelector('meta[name="keywords"]');t&&(n.keywords=t.content),["title","description","image","url","type","site_name"].forEach(e=>{const t=document.querySelector(`meta[property="og:${e}"]`);t&&(n[`og_${e}`]=t.content)}),["card","title","description","image"].forEach(e=>{const t=document.querySelector(`meta[name="twitter:${e}"]`);t&&(n[`twitter_${e}`]=t.content)});const o=document.querySelector('link[rel="canonical"]');return o&&(n.canonical=o.href),n}(),!n.readability){console.log("⚠️ Using fallback scraper");const e=function(){const n=document.body.cloneNode(!0);["nav","header","footer","aside",".sidebar",".navigation",".menu",".ads",".cookie-banner",".popup",".overlay","script","style","noscript","iframe"].forEach(e=>{n.querySelectorAll(e).forEach(n=>n.remove())});let e=n.textContent||"";e=e.replace(/\s+/g," ").trim();const t=e.substring(0,500)+"...";return{content:e,summary:t}}();n.markdown=e.content,n.summary=e.summary}return n.aiContext=y(n),console.log("✅ Advanced scraping complete"),n}function x(){const n=window.location.href,e=document.title.toLowerCase(),t=document.querySelector('meta[property="og:type"]');if(t){const n=t.content;if(n.includes("article"))return"article";if(n.includes("product"))return"product";if(n.includes("website"))return"website"}return n.includes("/blog/")||n.includes("/post/")||e.includes("blog")?"blog":n.includes("/product/")||n.includes("/shop/")?"product":n.includes("/portfolio/")||n.includes("/project/")?"portfolio":n===window.location.origin+"/"||n===window.location.origin?"homepage":"general"}function y(n){let e="";if(e+=`# 📄 ${n.title}\n\n`,e+=`**URL:** ${n.url}\n`,e+=`**Type:** ${n.pageType}\n`,e+=`**Scraped:** ${new Date(n.timestamp).toLocaleString()}\n\n`,Object.keys(n.metadata).length>0){e+="## 📋 Metadata\n";for(const[t,o]of Object.entries(n.metadata))o&&o.length>0&&(e+=`- **${t}:** ${o}\n`);e+="\n"}if(n.structuredData&&n.structuredData.length>0&&(e+="## 📊 Structured Data (Schema.org)\n",n.structuredData.forEach((n,t)=>{e+=`\n### Schema ${t+1}\n`,e+=`\`\`\`json\n${JSON.stringify(n,null,2)}\n\`\`\`\n`}),e+="\n"),n.markdown){e+="## 📝 Main Content\n\n";const t=4e3,o=n.markdown.length>t?n.markdown.substring(0,t)+"... (truncated)":n.markdown;e+=o,e+="\n\n"}return n.summary&&(e+="## 📌 Summary\n\n",e+=n.summary,e+="\n\n"),e+="---\n",e+="*🤖 AI Instructions: Use this context to answer user questions about this page. Be precise, reference specific details from the content, and maintain a friendly professional tone.*\n",e}async function w(e,t,a=null){if(s.isProcessing)return;c({isProcessing:!0});const r=document.getElementById("aiSendBtn"),l=document.getElementById("aiInput");r&&(r.disabled=!0),l&&(l.disabled=!0),i(e,!0);const p=[];if(t&&p.push({type:"text",text:t}),a&&p.push({type:"image_url",image_url:{url:a}}),s.conversationHistory.push({role:"user",content:t||"(image)"}),s.conversationHistory.length>11){const n=s.conversationHistory[0],e=s.conversationHistory.slice(-10);s.conversationHistory=[n,...e]}try{const t=await async function(){let n=await async function(){try{const n=(await u()).transaction(d,"readonly").objectStore(d),e=window.location.href;return new Promise((t,o)=>{const a=n.get(e);a.onsuccess=n=>{const o=n.target.result;if(!o)return void t(null);const a=Date.now();if(a-o.timestamp>864e5)return async function(n){try{(await u()).transaction(d,"readwrite").objectStore(d).delete(n)}catch(n){console.warn("IndexedDB delete failed:",n)}}(e),void t(null);console.log(`📦 Cache hit! Age: ${Math.floor((a-o.timestamp)/6e4)} minutes`),t(o.data)},a.onerror=n=>{o(n.target.error)}})}catch(n){return console.warn("IndexedDB read failed:",n),null}}();if(n)return console.log("📦 Using cached page data"),n;console.log("🔍 Scraping page data...");const e=y(h());return await m(e),console.log("💾 Page data cached"),e}(),a=window.__NEXUS_CONFIG||n,r=[{role:"system",content:`${a.SYSTEM_PROMPT}\n\n## Current Page Context\n${t}`}];for(let n=1;n<s.conversationHistory.length-1;n++)r.push({role:s.conversationHistory[n].role,content:s.conversationHistory[n].content});r.push({role:"user",content:p});const c=await fetch(a.API_URL,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.API_KEY}`},body:JSON.stringify({model:a.MODEL,messages:r,temperature:.7,max_tokens:600,stream:!1})});if(!c.ok){const n=await c.json().catch(()=>({}));throw new Error(`API error ${c.status}: ${n.error?.message||c.statusText}`)}const l=await c.json(),g=l.choices?.[0]?.message?.content||"Sorry, I didn't understand that.";s.conversationHistory.push({role:"assistant",content:g}),i(e,!1),o(e,g,"bot")}catch(n){console.error("Groq API Error:",n),i(e,!1),o(e,`⚠️ Oops! ${n.message||"Something went wrong."} Please try again.`,"bot")}finally{c({isProcessing:!1}),r&&(r.disabled=!1),l&&(l.disabled=!1,l.value="",l.focus())}}async function v(){const a=function(){const e=window.NexusConfig||{};return{API_URL:n.API_URL,API_KEY:e.apiKey||n.API_KEY,MODEL:e.model||n.MODEL,BOT_NAME:e.botName||n.BOT_NAME,GREETING:e.greeting||n.GREETING,SYSTEM_PROMPT:e.systemPrompt||n.SYSTEM_PROMPT,COLORS:n.COLORS}}();window.__NEXUS_CONFIG=a,await async function(){const n=document.createElement("link");n.href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css",n.rel="stylesheet",document.head.appendChild(n);const e=document.createElement("link");e.href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap",e.rel="stylesheet",document.head.appendChild(e);const t=document.createElement("script");t.src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.0/marked.min.js",t.async=!0,document.head.appendChild(t);const o=document.createElement("link");o.href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css",o.rel="stylesheet",document.head.appendChild(o);const a=document.createElement("script");a.src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js",a.async=!0,document.head.appendChild(a),await async function(){await Promise.all([b("https://cdnjs.cloudflare.com/ajax/libs/readability/0.4.4/Readability.min.js"),b("https://cdnjs.cloudflare.com/ajax/libs/turndown/7.1.2/turndown.min.js")]),g=window.Readability,f=window.TurndownService,console.log("📚 Scraper libraries loaded")}()}(),function(){const e=document.createElement("style");e.id="ai-chat-widget-styles",e.textContent=`\n @keyframes slideUpFade {\n 0% { opacity: 0; transform: translateY(16px); }\n 100% { opacity: 1; transform: translateY(0); }\n }\n @keyframes pulseDot {\n 0%, 100% { transform: scale(0.8); opacity: 0.5; }\n 50% { transform: scale(1.2); opacity: 1; }\n }\n @keyframes typing {\n 0%, 80%, 100% { opacity: 0.3; }\n 40% { opacity: 1; }\n }\n\n #ai-widget-root {\n all: initial;\n font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif;\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 999999;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n pointer-events: none;\n }\n #ai-widget-root * { box-sizing: border-box; pointer-events: auto; }\n\n /* ===== FIX: Font Awesome icons ===== */\n #ai-widget-root .fas,\n #ai-widget-root .far,\n #ai-widget-root .fab {\n font-family: 'Font Awesome 6 Free', 'Font Awesome 6 Brands' !important;\n font-weight: 900 !important; /* solid icons use 900 */\n font-style: normal;\n font-variant: normal;\n text-rendering: auto;\n -webkit-font-smoothing: antialiased;\n }\n #ai-widget-root .far {\n font-weight: 400 !important; /* regular */\n }\n #ai-widget-root .fab {\n font-family: 'Font Awesome 6 Brands' !important;\n font-weight: 400 !important;\n }\n\n /* ===== FLOATING ORB (FAB) ===== */\n .ai-fab {\n width: 56px;\n height: 56px;\n border-radius: 50%;\n background: #ffffff;\n border: 1px solid rgba(255,255,255,0.1);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #000000;\n transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1),\n transform 0.3s cubic-bezier(0.16, 1, 0.3, 1),\n box-shadow 0.25s ease;\n user-select: none;\n box-shadow: 0 8px 24px rgba(0,0,0,0.4);\n position: relative;\n z-index: 10;\n will-change: transform, opacity;\n }\n .ai-fab:hover {\n transform: scale(1.05);\n box-shadow: 0 12px 32px rgba(0,0,0,0.5);\n }\n .ai-fab .bot-icon i {\n color: #000000 !important;\n font-size: 24px !important;\n }\n\n /* Hide FAB when panel is open – smooth fade + scale */\n .ai-panel.open ~ .ai-fab {\n opacity: 0;\n transform: scale(0.8);\n pointer-events: none;\n }\n\n /* ===== CHAT PANEL ===== */\n .ai-panel {\n position: absolute;\n bottom: 0;\n right: 0;\n width: 400px;\n max-width: calc(100vw - 48px);\n height: 600px;\n max-height: calc(100vh - 48px);\n background: ${n.COLORS.panelBg};\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border-radius: 20px;\n box-shadow: 0 24px 80px rgba(0,0,0,0.5), 0 0 0 1px ${n.COLORS.border};\n display: flex;\n flex-direction: column;\n overflow: hidden;\n opacity: 0;\n visibility: hidden;\n transform: translateY(20px);\n transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), visibility 0.3s;\n }\n .ai-panel.open {\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n }\n\n /* ===== HEADER ===== */\n .ai-header {\n padding: 20px 24px;\n border-bottom: 1px solid ${n.COLORS.border};\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-shrink: 0;\n background: transparent;\n }\n .ai-header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n color: ${n.COLORS.textPrimary};\n font-weight: 600;\n font-size: 16px;\n }\n .status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #10B981;\n box-shadow: 0 0 8px rgba(16, 185, 129, 0.5);\n }\n .ai-close-btn {\n background: transparent;\n border: none;\n color: ${n.COLORS.textSecondary};\n font-size: 20px;\n cursor: pointer;\n transition: color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .ai-close-btn:hover {\n color: ${n.COLORS.textPrimary};\n }\n\n /* ===== MESSAGES ===== */\n .ai-messages {\n flex: 1;\n padding: 24px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 24px;\n scroll-behavior: smooth;\n }\n .ai-messages::-webkit-scrollbar { width: 6px; }\n .ai-messages::-webkit-scrollbar-track { background: transparent; }\n .ai-messages::-webkit-scrollbar-thumb {\n background: ${n.COLORS.border};\n border-radius: 10px;\n }\n\n /* ===== BUBBLES ===== */\n .msg {\n max-width: 85%;\n font-size: 14.5px;\n line-height: 1.6;\n word-break: break-word;\n animation: slideUpFade 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n color: ${n.COLORS.textPrimary};\n }\n .msg.user {\n align-self: flex-end;\n background: ${n.COLORS.userBubble};\n padding: 12px 18px;\n border-radius: 16px;\n border-bottom-right-radius: 4px;\n border: 1px solid ${n.COLORS.border};\n }\n .msg.bot {\n align-self: flex-start;\n background: ${n.COLORS.botBubble};\n max-width: 95%;\n }\n .msg.bot .bot-label {\n font-size: 12px;\n color: ${n.COLORS.textSecondary};\n margin-bottom: 6px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .msg .image-preview {\n max-width: 200px;\n border-radius: 12px;\n margin-top: 8px;\n border: 1px solid ${n.COLORS.border};\n }\n .msg.user .image-preview { max-width: 100%; }\n\n /* Markdown Cleanup */\n .msg.bot p { margin: 0 0 12px 0; }\n .msg.bot p:last-child { margin-bottom: 0; }\n .msg.bot code {\n background: #1e1e1e;\n padding: 2px 6px;\n border-radius: 4px;\n font-family: ui-monospace, monospace;\n font-size: 0.9em;\n border: 1px solid ${n.COLORS.border};\n }\n .msg.bot pre {\n background: #111111;\n padding: 16px;\n border-radius: 12px;\n overflow-x: auto;\n margin: 12px 0;\n border: 1px solid ${n.COLORS.border};\n }\n .msg.bot pre code { background: transparent; padding: 0; border: none; }\n .msg.bot ul, .msg.bot ol { margin: 8px 0 12px 20px; padding-left: 0; }\n .msg.bot li { margin-bottom: 6px; }\n .msg.bot a { color: #fff; text-decoration: underline; text-underline-offset: 4px; }\n\n /* ===== INPUT AREA ===== */\n .ai-input-area {\n padding: 16px 24px 24px;\n background: transparent;\n flex-shrink: 0;\n }\n .ai-input-container {\n display: flex;\n align-items: center;\n background: #161616;\n border: 1px solid ${n.COLORS.border};\n border-radius: 24px;\n padding: 6px 6px 6px 16px;\n transition: border-color 0.2s, background 0.2s;\n }\n .ai-input-container:focus-within {\n border-color: #555;\n background: #1a1a1a;\n }\n .ai-input-container input {\n flex: 1;\n background: transparent;\n border: none;\n color: ${n.COLORS.textPrimary};\n font-size: 14px;\n outline: none;\n font-family: inherit;\n padding: 8px 0;\n }\n .ai-input-container input::placeholder {\n color: ${n.COLORS.textSecondary};\n }\n .icon-btn {\n background: transparent;\n border: none;\n color: ${n.COLORS.textSecondary};\n width: 36px;\n height: 36px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: color 0.2s, background 0.2s;\n }\n .icon-btn:hover {\n color: #fff;\n background: ${n.COLORS.borderHover};\n }\n .send-btn {\n background: #fff;\n color: #000;\n margin-left: 4px;\n }\n .send-btn:hover {\n background: #e0e0e0;\n color: #000;\n }\n .send-btn:disabled {\n background: #333;\n color: #666;\n cursor: not-allowed;\n }\n\n /* ===== PREVIEW PILL ===== */\n #aiPreviewContainer { margin-bottom: 12px; }\n .image-preview-pill {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n background: #1e1e1e;\n border: 1px solid ${n.COLORS.border};\n border-radius: 8px;\n padding: 6px 12px 6px 6px;\n animation: slideUpFade 0.3s ease forwards;\n }\n .image-preview-pill img {\n width: 24px;\n height: 24px;\n border-radius: 4px;\n object-fit: cover;\n }\n .image-preview-pill .file-name {\n font-size: 12px;\n color: ${n.COLORS.textPrimary};\n max-width: 120px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .image-preview-pill .remove-file {\n background: none;\n border: none;\n color: ${n.COLORS.textSecondary};\n cursor: pointer;\n font-size: 14px;\n display: flex;\n align-items: center;\n }\n .image-preview-pill .remove-file:hover { color: #fff; }\n\n /* ===== TYPING INDICATOR ===== */\n .typing-indicator {\n display: flex;\n gap: 4px;\n padding: 8px 0;\n align-self: flex-start;\n }\n .typing-indicator span {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: ${n.COLORS.textSecondary};\n animation: typing 1.4s infinite;\n }\n .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }\n .typing-indicator span:nth-child(3) { animation-delay: 0.4s; }\n\n /* ===== RESPONSIVE ===== */\n @media (max-width: 480px) {\n .ai-panel {\n width: calc(100vw - 32px);\n right: 16px;\n height: 80vh;\n max-height: calc(100vh - 48px);\n }\n .ai-fab {\n width: 50px;\n height: 50px;\n }\n .ai-fab .bot-icon { font-size: 20px; }\n }\n `,document.head.appendChild(e)}(),function(){const e=window.__NEXUS_CONFIG?.BOT_NAME||n.BOT_NAME,t=window.__NEXUS_CONFIG?.GREETING||n.GREETING,o=document.createElement("div");o.id="ai-widget-root",o.innerHTML=`\n <div class="ai-panel" id="aiPanel">\n <div class="ai-header">\n <div class="ai-header-title">\n <span class="status-dot"></span>\n ${e}\n </div>\n <button class="ai-close-btn" id="aiCloseBtn" aria-label="Close chat">\n <i class="fas fa-xmark"></i>\n </button>\n </div>\n <div class="ai-messages" id="aiMessages">\n <div class="msg bot">\n ${t}\n </div>\n </div>\n <div class="ai-input-area" id="aiInputArea">\n <div id="aiPreviewContainer"></div>\n <div class="ai-input-container">\n <button class="icon-btn" id="aiAttachBtn" aria-label="Attach image">\n <i class="fas fa-paperclip"></i>\n </button>\n <input type="text" id="aiInput" placeholder="Message..." autocomplete="off">\n <button class="icon-btn send-btn" id="aiSendBtn">\n <i class="fas fa-arrow-up"></i>\n </button>\n </div>\n </div>\n </div>\n <button class="ai-fab" id="aiFab" aria-label="Open AI Chat">\n <span class="bot-icon">\n <i class="fas fa-robot" style="color: #000000 !important; font-size: 22px;"></i>\n </span>\n </button>\n `,document.body.appendChild(o)}();const i=e("aiPanel"),p=e("aiMessages"),x=e("aiFab"),v=e("aiCloseBtn"),S=e("aiSendBtn"),O=e("aiInput"),k=e("aiAttachBtn"),E=e("aiPreviewContainer");!function(n){let e=window.location.href;new MutationObserver(()=>{window.location.href!==e&&(e=window.location.href,console.log("🔄 Page navigation detected"),n())}).observe(document,{subtree:!0,childList:!0});const t=history.pushState;history.pushState=function(){t.apply(this,arguments),setTimeout(()=>{window.location.href!==e&&(e=window.location.href,n())},500)}}(()=>{console.log("🔄 Page changed, refreshing context..."),async function(){console.log("🔄 Forcing page context refresh...");const n=y(h());await m(n),console.log("✅ Page context refreshed")}(),o(p,"🔄 Page context updated!","bot")});const C=document.createElement("input");C.type="file",C.accept="image/*",C.style.display="none",C.id="aiFileInput",document.body.appendChild(C),k?.addEventListener("click",()=>C.click()),C.addEventListener("change",n=>{const e=n.target.files?.[0];if(!e)return;if(!e.type.startsWith("image/"))return void alert("Please select an image file.");const t=new FileReader;t.onload=n=>{const t={dataUrl:n.target.result,name:e.name,type:e.type};c({attachedFile:t}),r(E,t);const o=document.getElementById("aiRemoveFile");o&&o.addEventListener("click",()=>{c({attachedFile:null}),r(E,null),O?.focus()}),O?.focus()},t.readAsDataURL(e),n.target.value=""}),x?.addEventListener("click",()=>{c({isOpen:t(i,!0)})}),v?.addEventListener("click",()=>{c({isOpen:t(i,!1)})}),document.addEventListener("click",n=>{const e=document.getElementById("ai-widget-root");e&&s.isOpen&&!e.contains(n.target)&&c({isOpen:t(i,!1)})});const L=()=>{const n=O?.value.trim();if(!n&&!s.attachedFile)return;if(s.isProcessing)return;const e=s.attachedFile?.dataUrl||null;o(p,n,"user",e),O.value="",r(E,null);const t=s.attachedFile?.dataUrl||null;c({attachedFile:null}),w(p,n,t)};S?.addEventListener("click",L),O?.addEventListener("keydown",n=>{"Enter"!==n.key||n.shiftKey||(n.preventDefault(),L())});const P=await async function(){try{const n=(await u()).transaction(d,"readonly").objectStore(d);return new Promise(e=>{const t=n.count();t.onsuccess=()=>{e({totalEntries:t.result,database:l,version:1})},t.onerror=()=>e(null)})}catch{return null}}();console.log("📊 Cache Stats:",P||"No cache yet"),console.log("✨ Nexus AI v2.0 initialized with advanced scraper!")}s.conversationHistory=[{role:"system",content:n.SYSTEM_PROMPT}],"loading"===document.readyState?document.addEventListener("DOMContentLoaded",v):v()})();
|