1(()=>{(function(){let d={spark:'<svg width="18" height="18" viewBox="0 0 24 24" fill="#fff"><path d="M12 2l1.9 6.1L20 10l-6.1 1.9L12 18l-1.9-6.1L4 10l6.1-1.9z"/></svg>',sparkSm:'<svg width="15" height="15" viewBox="0 0 24 24" fill="#fff"><path d="M12 2l1.9 6.1L20 10l-6.1 1.9L12 18l-1.9-6.1L4 10l6.1-1.9z"/></svg>',send:'<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 2 11 13M22 2l-7 20-4-9-9-4z"/></svg>',close:'<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>',q:'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>'};function v(c){return String(c).replace(/[&<>"]/g,t=>({"&":"&","<":"<",">":">",'"':"""})[t])}function j(c){return v(c).replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>").replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+|\/[^)\s]*)\)/g,(t,m,i)=>`<a href="${v(i)}" target="_blank" rel="noopener">${m}</a>`)}function A(c){return String(c).trim().split(/\n{2,}/).map(m=>{let i=m.split(` 2`);return i.length&&i.every(y=>/^\s*[-â¢]\s+/.test(y))?'<ul class="b-list">'+i.map(y=>"<li>"+j(y.replace(/^\s*[-â¢]\s+/,""))+"</li>").join("")+"</ul>":"<p>"+i.map(j).join("<br>")+"</p>"}).join("")}function a(c){let t=document.createElement("template");return t.innerHTML=c.trim(),t.content.firstElementChild}function z(c){c=c||{};let t=c.strings||{},m=(c.api||"").replace(/\/$/,""),i=c.lang||"en";if(!m||document.querySelector(".fsc-root"))return;let y=a('<div class="fsc-root"></div>'),k=a(`<button class="fsc-launcher" aria-label="${v(t.open||"Find a course")}"> 3 <span class="ava">${d.spark}</span><span class="lbl"></span></button>`);k.querySelector(".lbl").textContent=t.launcher||"Find a course";let o=a(`<div class="fsc-panel" role="dialog" aria-label="${v(t.name||"Kai")}"> 4 <div class="fsc-head"> 5 <span class="ava">${d.sparkSm}</span> 6 <div class="meta"><div class="name"></div><div class="status"><span class="dot"></span><span class="st-txt"></span></div></div> 7 <button class="x" aria-label="${v(t.close||"Close chat")}">${d.close}</button> 8 </div> 9 <div class="fsc-body" tabindex="-1"></div> 10 <div class="fsc-composer"> 11 <div class="fsc-inputrow"> 12 <textarea class="fsc-input" rows="1" aria-label="${v(t.placeholder||"")}"></textarea> 13 <button class="fsc-send" aria-label="${v(t.send||"Send")}" disabled>${d.send}</button> 14 </div> 15 <div class="fsc-foot"></div> 16 </div> 17 </div>`);o.querySelector(".name").textContent=t.name||"Kai",o.querySelector(".st-txt").textContent=t.status||"",o.querySelector(".fsc-input").placeholder=t.placeholder||"",o.querySelector(".fsc-foot").textContent=t.footer||"",y.append(k,o),document.body.appendChild(y);let l=o.querySelector(".fsc-body"),r=o.querySelector(".fsc-input"),q=o.querySelector(".fsc-send"),g=[],S=!1,_=!1,f=0,$="fsc_kai_v1:"+i,J=360*60*1e3,P=30;function G(){try{if(!g.length)return;localStorage.setItem($,JSON.stringify({v:1,lang:i,ts:Date.now(),messages:g.slice(-P)}))}catch{}}function W(){try{let e=JSON.parse(localStorage.getItem($)||"null");return!e||e.v!==1||!Array.isArray(e.messages)||!e.messages.length?null:Date.now()-(e.ts||0)>J?(localStorage.removeItem($),null):e.messages.filter(s=>
17s&&(s.role==="user"||s.role==="assistant")&&typeof s.content=="string")}catch{return null}}function F(e){e.forEach(s=>{if(s.role==="user"){let n=a('<div class="fsc-msg user"><div class="fsc-bubble"></div></div>');n.querySelector(".fsc-bubble").textContent=s.content,l.appendChild(n)}else{let n=a(`<div class="fsc-msg bot"><span class="m-ava">${d.sparkSm}</span><div class="fsc-botcol"><div class="fsc-bubble"></div></div></div>`);n.querySelector(".fsc-bubble").innerHTML=A(s.content),l.appendChild(n)}})}function u(e,s){try{let n=Object.assign({chat_lang:i},s||{});typeof window.gtag=="function"?window.gtag("event",e,n):window.dataLayer&&window.dataLayer.push&&window.dataLayer.push(Object.assign({event:e},n))}catch{}}function B(){let e=_;o.classList.add("open"),k.classList.add("hide"),_?w():(U(),_=!0),u("kai_open",{reopened:e}),setTimeout(()=>r.focus(),220)}function H(){o.classList.remove("open"),k.classList.remove("hide"),u("kai_close",{turns:f})}l.addEventListener("click",e=>{let s=e.target.closest&&e.target.closest(".fsc-bubble a");s&&u("kai_course_click",{course_url:s.getAttribute("href")||"",course_label:(s.textContent||"").slice(0,120)})}),k.addEventListener("click",B),o.querySelector(".x").addEventListener("click",H),document.addEventListener("keydown",e=>{e.key==="Escape"&&o.classList.contains("open")&&H()});function U(){let e=a(`<div class="fsc-welcome fsc-in"> 18 <span class="big-ava">${d.spark}</span> 19 <h3></h3><p></p> 20 <div class="fsc-suggest-label"></div> 21 </div>`);e.querySelector("h3").textContent=t.welcome_title||"Hi, I'm Kai",e.querySelector("p").textContent=t.welcome_sub||"",e.querySelector(".fsc-suggest-label").textContent=t.suggest_label||"";let s=a('<div class="fsc-chips"></div>'),n=[t.chip_1,t.chip_2,t.chip_3,t.chip_4].filter(Boolean);n.forEach((b,p)=>{let h=a(`<button class="fsc-chip">${d.q}<span></span></button>`);h.querySelector("span").textContent=b;let E=p===n.length-1;h.addEventListener("click",()=>{u("kai_chip_click",{chip_label:b,chip_index:p,is_other:E}),C(E&&t.chip_other_msg||b)}),s.appendChild(h)}),l.append(e,s)}function X(){l.querySelectorAll(".fsc-welcome, .fsc-chips").forEach(e=>e.remove())}function w(){l.scrollTop=l.scrollHeight}function Y(e){let s=a('<div class="fsc-msg user fsc-in"><div class="fsc-bubble"></div></div>');s.querySelector(".fsc-bubble").textContent=e,l.appendChild(s),w()}function Q(){let e=a(`<div class="fsc-msg bot fsc-in"><span class="m-ava">${d.sparkSm}</span><div class="fsc-typing"><i></i><i></i><i></i></div></div>`);return l.appendChild(e),w(),e}function M(){let e=a(`<div class="fsc-msg bot fsc-in"><span class="m-ava">${d.sparkSm}</span><div class="fsc-bubble"><div class="fsc-error"></div></div></div>`);e.querySelector(".fsc-error").textContent=t.error||"Something went wrong. Please try again.",l.appendChild(e),w()}async function C(e){if(e=(e||"").trim(),S||!e)return;S=!0,q.disabled=!0,X(),Y(e),g.push({role:"user",content:e}),f++,u("kai_message_sent",{turn:f,message_length:e.length}),r.value="",I();let s=Q(),n=null,b=null,p="";try{let h=await fetch(m+"/api/chat?lang="+encodeURIComponent(i),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:g})});if(!h.ok)throw new Error("HTTP "+h.status);let E=h.body.getReader(),V=new TextDecoder,T="";for(;;){let{done:O,value:Z}=await E.read();if(O)break;T+=V.decode(Z,{stream:!0});let N=T.split(` 22`);T=N.pop();for(let D of N){if(!D.startsWith("data:"))continue;let R=D.slice(5).trim();if(R==="[DONE]")continue;let x;try{x=JSON.parse(R)}catch{continue}x.c&&(n||(s.remove(),n=a(`<div class="fsc-msg bot fsc-in"><span class="m-ava">${d.sparkSm}</span><div class="fsc-botcol"><div class="fsc-bubble"></div></div></div>`),b=n.querySelector(".fsc-bubble"),l.appendChild(n)),p+=x.c,b.innerHTML=A(p),w()),x.error&&!n&&(s.remove(),M())}}if(s.remove(),p){g.push({role:"assistant",content:p}),G();let O=(p.match(/\]\((https?:\/\/[^)\s]+|\/[^)\s]*)\)/g)||[]).length;u("kai_reply",{turn:f,reply_length:p.length,course_count:O})}else n||(M(),u("kai_reply_error",{turn:f,reason:"empty"}))}catch{s.remove(),n||M(),u("kai_reply_error",{turn:f,reason:"fetch"})}finally{S=!1,K(),r.focus()}}function I(){r.style.height="auto",r.style.height=Math.min(r.scrollHeight,96)+"px"}function K(){q.disabled=S||!r.value.trim()}r.addEventListener("input",()=>{I(),K()}),r.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),C(r.value))}),q.addEventListener("click",()=>C(r.value));let L=W();
22L&&L.length&&(L.forEach(e=>g.push(e)),F(L),_=!0,f=g.filter(e=>e.role==="user").length,u("kai_restore",{turns:f})),window.KaiChat._open=B}window.KaiChat={init:z,_open:null}})();})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.