cevvo-widget 1.0.9 → 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/dist/widget.js +46 -22
- package/package.json +1 -1
package/dist/widget.js
CHANGED
|
@@ -308,18 +308,41 @@
|
|
|
308
308
|
border-bottom-right-radius: 4px;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
.cevvo-bubble code {
|
|
311
|
+
.cevvo-bubble .cevvo-inline-code {
|
|
312
312
|
background: rgba(0,0,0,0.08);
|
|
313
313
|
padding: 2px 6px;
|
|
314
314
|
border-radius: 4px;
|
|
315
|
-
font-family: 'SF Mono', Monaco, monospace;
|
|
316
|
-
font-size:
|
|
315
|
+
font-family: 'SF Mono', 'Fira Code', Monaco, Consolas, monospace;
|
|
316
|
+
font-size: 12px;
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
.cevvo-bubble.user code {
|
|
319
|
+
.cevvo-bubble.user .cevvo-inline-code {
|
|
320
320
|
background: rgba(255,255,255,0.15);
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
.cevvo-bubble .cevvo-code-block {
|
|
324
|
+
background: #1a1b26;
|
|
325
|
+
color: #c0caf5;
|
|
326
|
+
border-radius: 8px;
|
|
327
|
+
padding: 14px 16px;
|
|
328
|
+
margin: 8px 0;
|
|
329
|
+
overflow-x: auto;
|
|
330
|
+
font-family: 'SF Mono', 'Fira Code', Monaco, Consolas, monospace;
|
|
331
|
+
font-size: 12px;
|
|
332
|
+
line-height: 1.6;
|
|
333
|
+
white-space: pre;
|
|
334
|
+
word-break: normal;
|
|
335
|
+
-webkit-overflow-scrolling: touch;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.cevvo-bubble .cevvo-code-block code {
|
|
339
|
+
background: none;
|
|
340
|
+
padding: 0;
|
|
341
|
+
border-radius: 0;
|
|
342
|
+
font-size: inherit;
|
|
343
|
+
color: inherit;
|
|
344
|
+
}
|
|
345
|
+
|
|
323
346
|
/* Typing indicator */
|
|
324
347
|
.cevvo-typing {
|
|
325
348
|
display: flex;
|
|
@@ -836,7 +859,7 @@
|
|
|
836
859
|
border-color: #38383a;
|
|
837
860
|
color: #e5e5ea;
|
|
838
861
|
}
|
|
839
|
-
`;class h{constructor(
|
|
862
|
+
`;class h{constructor(o={}){this.config={...b,...o},this.isOpen=!1,this.messages=[],this.threadId=null,this.isLoading=!1;const e=()=>{this.injectStyles(),this.createElements(),this.bindEvents()};document.body?e():document.addEventListener("DOMContentLoaded",e)}injectStyles(){if(document.getElementById("cevvo-widget-styles"))return;const o=document.createElement("style");o.id="cevvo-widget-styles",o.textContent=f,document.head.appendChild(o)}resolveTheme(){return this.config.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":this.config.theme||"light"}createElements(){this.container=document.createElement("div");const o=this.resolveTheme()==="dark"?"cevvo-dark":"";this.container.className=`cevvo-widget ${o}`.trim(),this.container.style.setProperty("--cevvo-accent",this.config.projectColor),this.button=document.createElement("button"),this.button.className=`cevvo-widget-btn ${this.config.buttonPosition}`,this.button.style.background=this.config.buttonBgColor,this.button.style.color=this.config.buttonTextColor,this.button.innerHTML=`${n.cevvoLogo}<span>${this.config.buttonText}</span>`,this.overlay=document.createElement("div"),this.overlay.className=`cevvo-modal-overlay ${this.config.mode==="modal"?"mode-modal":""}`,this.overlay.innerHTML=this.config.mode==="modal"?this.renderModalMode():this.renderModal(),this.container.appendChild(this.button),this.container.appendChild(this.overlay),document.body.appendChild(this.container),this.modal=this.overlay.querySelector(".cevvo-modal"),this.messagesContainer=this.overlay.querySelector(".cevvo-modal-body"),this.input=this.overlay.querySelector(".cevvo-input"),this.form=this.overlay.querySelector(".cevvo-input-form"),this.clearBtn=this.overlay.querySelector(".cevvo-clear-btn"),this.closeBtn=this.overlay.querySelector(".cevvo-close-btn")}renderModal(){const o=this.config.modalExampleQuestions.map(e=>`<button class="cevvo-example-btn">${this.escapeHtml(e)}</button>`).join("");return`
|
|
840
863
|
<div class="cevvo-modal">
|
|
841
864
|
<div class="cevvo-modal-header">
|
|
842
865
|
<div class="cevvo-header-info">
|
|
@@ -855,7 +878,7 @@
|
|
|
855
878
|
<div class="cevvo-empty-state">
|
|
856
879
|
<div class="cevvo-empty-icon">${n.sparkle}</div>
|
|
857
880
|
<h3>${this.escapeHtml(this.config.welcomeMessage)}</h3>
|
|
858
|
-
${
|
|
881
|
+
${o?`<div class="cevvo-example-questions">${o}</div>`:""}
|
|
859
882
|
</div>
|
|
860
883
|
</div>
|
|
861
884
|
<div class="cevvo-modal-footer">
|
|
@@ -867,7 +890,7 @@
|
|
|
867
890
|
<p class="cevvo-powered">Powered by <a href="https://cevvo.ai" target="_blank" rel="noopener">Cevvo</a></p>
|
|
868
891
|
</div>
|
|
869
892
|
</div>
|
|
870
|
-
`}renderModalMode(){const
|
|
893
|
+
`}renderModalMode(){const o=this.config.modalDescription||this.config.modalSubtitle||"Ask me anything about the documentation.";return`
|
|
871
894
|
<div class="cevvo-modal">
|
|
872
895
|
<div class="cevvo-modal-header">
|
|
873
896
|
<div class="cevvo-header-info">
|
|
@@ -882,7 +905,7 @@
|
|
|
882
905
|
</div>
|
|
883
906
|
</div>
|
|
884
907
|
<div class="cevvo-modal-description" style="padding: 0 24px;">
|
|
885
|
-
${
|
|
908
|
+
${o}
|
|
886
909
|
</div>
|
|
887
910
|
<div class="cevvo-modal-body">
|
|
888
911
|
</div>
|
|
@@ -914,25 +937,25 @@
|
|
|
914
937
|
</div>
|
|
915
938
|
</div>
|
|
916
939
|
</div>
|
|
917
|
-
`}bindEvents(){this.button.addEventListener("click",()=>this.toggle()),this.overlay.addEventListener("click",
|
|
918
|
-
`);for(const m of w)if(m.startsWith("data: ")){const u=m.slice(6);if(u==="[DONE]")continue;try{const v=JSON.parse(u);v.thread_id&&(this.threadId=v.thread_id),v.token&&(d+=v.token,this.updateMessage(
|
|
940
|
+
`}bindEvents(){this.button.addEventListener("click",()=>this.toggle()),this.overlay.addEventListener("click",e=>{e.target===this.overlay&&this.close()}),this.closeBtn.addEventListener("click",()=>this.close()),this.clearBtn.addEventListener("click",()=>this.clearMessages()),this.form.addEventListener("submit",e=>{e.preventDefault(),this.sendMessage()}),this.overlay.querySelector(".cevvo-send-btn").addEventListener("click",e=>{e.preventDefault(),this.sendMessage()}),this.input.addEventListener("input",()=>{const e=this.overlay.querySelector(".cevvo-send-btn");e.disabled=!this.input.value.trim()||this.isLoading}),this.input.addEventListener("keydown",e=>{e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),this.sendMessage())}),this.overlay.addEventListener("click",e=>{e.target.classList.contains("cevvo-example-btn")&&(this.input.value=e.target.textContent,this.sendMessage())}),this.messagesContainer.addEventListener("click",e=>{const t=e.target.closest("button");if(!t)return;const s=t.closest(".cevvo-actions");if(!s)return;const i=s.dataset.messageId,r=t.closest(".cevvo-message"),d=r==null?void 0:r.dataset.msgId,c=this.messages.find(l=>l.id===d);if(t.classList.contains("cevvo-copy-btn")&&(c!=null&&c.content)&&(navigator.clipboard.writeText(c.content),t.innerHTML=n.check,setTimeout(()=>{t.innerHTML=n.copy},2e3)),t.classList.contains("cevvo-thumbs-up")&&i){const l=(c==null?void 0:c.feedback)==="positive"?null:"positive";c&&(c.feedback=l),this.submitFeedback(parseInt(i),5),this.renderMessages()}if(t.classList.contains("cevvo-thumbs-down")&&i){const l=(c==null?void 0:c.feedback)==="negative"?null:"negative";c&&(c.feedback=l),this.submitFeedback(parseInt(i),1),this.renderMessages()}}),document.addEventListener("keydown",e=>{(e.metaKey||e.ctrlKey)&&e.key==="k"&&(e.preventDefault(),this.toggle()),e.key==="Escape"&&this.isOpen&&this.close()})}toggle(){this.isOpen?this.close():this.open()}open(){this.isOpen=!0,this.overlay.classList.add("is-open"),this.button.style.display="none",setTimeout(()=>this.input.focus(),100)}close(){this.isOpen=!1,this.overlay.classList.remove("is-open"),this.button.style.display="flex"}updateModalState(){this.config.mode==="modal"&&(this.messages.length>0?this.overlay.classList.add("has-messages"):this.overlay.classList.remove("has-messages"))}async sendMessage(){const o=this.input.value.trim();if(!o||this.isLoading)return;if(!this.config.projectId||!this.config.apiKey){this.addMessage({role:"assistant",content:'Widget not configured. Please set Project ID and API Key, then click "Apply Changes".'});return}this.input.value="",this.overlay.querySelector(".cevvo-send-btn").disabled=!0,this.addMessage({role:"user",content:o});const e=this.addMessage({role:"assistant",content:"",isLoading:!0});this.isLoading=!0;try{const t={"Content-Type":"application/json","X-API-Key":this.config.apiKey},s=await fetch(`${this.config.apiUrl}/widget/chat/stream`,{method:"POST",headers:t,body:JSON.stringify({message:o,thread_id:this.threadId,project_id:this.config.projectId})});if(!s.ok)throw new Error("Failed to send message");const i=s.body.getReader(),r=new TextDecoder;let d="",c=[],l=null;for(;;){const{done:x,value:y}=await i.read();if(x)break;const w=r.decode(y,{stream:!0}).split(`
|
|
941
|
+
`);for(const m of w)if(m.startsWith("data: ")){const u=m.slice(6);if(u==="[DONE]")continue;try{const v=JSON.parse(u);v.thread_id&&(this.threadId=v.thread_id),v.token&&(d+=v.token,this.updateMessage(e,{content:d,isLoading:!1})),v.sources&&(c=v.sources),v.question_answer_id&&(l=v.question_answer_id)}catch{}}}this.updateMessage(e,{content:d||"I'm sorry, I couldn't generate a response.",sources:c,messageId:l,isLoading:!1})}catch(t){console.error("Chat error:",t),this.updateMessage(e,{content:"Sorry, something went wrong. Please try again.",isLoading:!1})}finally{this.isLoading=!1}}addMessage(o){const e=Date.now().toString();return this.messages.push({id:e,...o}),this.renderMessages(),this.clearBtn.style.display="flex",this.updateModalState(),e}updateMessage(o,e){const t=this.messages.find(s=>s.id===o);t&&(Object.assign(t,e),this.renderMessages())}clearMessages(){this.messages=[],this.threadId=null,this.clearBtn.style.display="none",this.renderMessages(),this.updateModalState()}renderMessages(){if(this.messages.length===0){this.messagesContainer.innerHTML=`
|
|
919
942
|
<div class="cevvo-empty-state">
|
|
920
943
|
<div class="cevvo-empty-icon">${n.sparkle}</div>
|
|
921
944
|
<h3>${this.escapeHtml(this.config.welcomeMessage)}</h3>
|
|
922
945
|
${this.config.modalExampleQuestions.length?`
|
|
923
946
|
<div class="cevvo-example-questions">
|
|
924
|
-
${this.config.modalExampleQuestions.map(
|
|
947
|
+
${this.config.modalExampleQuestions.map(o=>`<button class="cevvo-example-btn">${this.escapeHtml(o)}</button>`).join("")}
|
|
925
948
|
</div>
|
|
926
949
|
`:""}
|
|
927
950
|
</div>
|
|
928
951
|
`;return}this.messagesContainer.innerHTML=`
|
|
929
952
|
<div class="cevvo-messages">
|
|
930
|
-
${this.messages.map(
|
|
953
|
+
${this.messages.map(o=>this.renderMessage(o)).join("")}
|
|
931
954
|
</div>
|
|
932
|
-
`,this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight}renderMessage(
|
|
955
|
+
`,this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight}renderMessage(o){var d;const e=o.role==="assistant"?n.cevvoLogo:n.user,t=o.isLoading?'<div class="cevvo-typing"><span></span><span></span><span></span></div>':this.formatContent(o.content),s=(d=o.sources)!=null&&d.length?`
|
|
933
956
|
<div class="cevvo-sources">
|
|
934
957
|
<div class="cevvo-sources-header">${n.link} Sources</div>
|
|
935
|
-
${
|
|
958
|
+
${o.sources.map((c,l)=>`
|
|
936
959
|
<a href="${this.escapeHtml(c.source_url||c.url||"#")}" target="_blank" rel="noopener" class="cevvo-source-link">
|
|
937
960
|
<span class="cevvo-source-num">${l+1}</span>
|
|
938
961
|
<span class="cevvo-source-title">${this.escapeHtml(c.title||"Source")}</span>
|
|
@@ -940,19 +963,20 @@
|
|
|
940
963
|
</a>
|
|
941
964
|
`).join("")}
|
|
942
965
|
</div>
|
|
943
|
-
`:"",i=
|
|
944
|
-
<div class="cevvo-actions ${i}" data-message-id="${
|
|
966
|
+
`:"",i=o.feedback==="positive"?"feedback-positive":o.feedback==="negative"?"feedback-negative":"",r=o.role==="assistant"&&o.content&&!o.isLoading?`
|
|
967
|
+
<div class="cevvo-actions ${i}" data-message-id="${o.messageId||""}">
|
|
945
968
|
<button class="cevvo-copy-btn" title="Copy">${n.copy}</button>
|
|
946
|
-
<button class="cevvo-thumbs-up ${
|
|
947
|
-
<button class="cevvo-thumbs-down ${
|
|
969
|
+
<button class="cevvo-thumbs-up ${o.feedback==="positive"?"active":""}" title="Helpful">${n.thumbsUp}</button>
|
|
970
|
+
<button class="cevvo-thumbs-down ${o.feedback==="negative"?"active":""}" title="Not helpful">${n.thumbsDown}</button>
|
|
948
971
|
</div>
|
|
949
972
|
`:"";return`
|
|
950
|
-
<div class="cevvo-message ${
|
|
951
|
-
<div class="cevvo-avatar ${
|
|
973
|
+
<div class="cevvo-message ${o.role}" data-msg-id="${o.id}">
|
|
974
|
+
<div class="cevvo-avatar ${o.role}">${e}</div>
|
|
952
975
|
<div class="cevvo-message-content">
|
|
953
|
-
<div class="cevvo-bubble ${
|
|
976
|
+
<div class="cevvo-bubble ${o.role}">${t}</div>
|
|
954
977
|
${s}
|
|
955
978
|
${r}
|
|
956
979
|
</div>
|
|
957
980
|
</div>
|
|
958
|
-
`}async submitFeedback(e
|
|
981
|
+
`}async submitFeedback(o,e){if(o)try{await fetch(`${this.config.apiUrl}/widget/feedback`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:JSON.stringify({message_id:o,rating:e,project_id:this.config.projectId})})}catch(t){console.error("Feedback error:",t)}}formatContent(o){let e=this.escapeHtml(o);return e=e.replace(/```(\w*)\n([\s\S]*?)```/g,(t,s,i)=>`<pre class="cevvo-code-block"><code>${i.trim()}</code></pre>`),e=e.replace(/`([^`]+)`/g,'<code class="cevvo-inline-code">$1</code>'),e=e.replace(/\*\*(.*?)\*\*/g,"<strong>$1</strong>"),e=e.replace(/\*(.*?)\*/g,"<em>$1</em>"),e=e.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener" style="color:var(--cevvo-accent,#2563eb);text-decoration:underline;">$1</a>'),e=e.replace(/^(\d+)\.\s+(.+)$/gm,'<li style="margin-left:1.2em;list-style:decimal;">$2</li>'),e=e.replace(/^[-•]\s+(.+)$/gm,'<li style="margin-left:1.2em;list-style:disc;">$1</li>'),e=e.replace(/\n/g,"<br>"),e=e.replace(/<pre class="cevvo-code-block"><code>([\s\S]*?)<\/code><\/pre>/g,(t,s)=>`<pre class="cevvo-code-block"><code>${s.replace(/<br>/g,`
|
|
982
|
+
`)}</code></pre>`),e}escapeHtml(o){const e=document.createElement("div");return e.textContent=o,e.innerHTML}}async function g(a,o,e){const t=e||p;try{const s=await fetch(`${t}/widget/config?project_id=${encodeURIComponent(a)}`,{method:"GET",headers:{"X-API-Key":o}});if(!s.ok)return console.warn("[CevvoWidget] Failed to fetch config:",s.status),null;const i=await s.json();return{mode:i.mode,buttonText:i.buttonText,buttonPosition:i.position,projectColor:i.primaryColor,buttonBgColor:i.primaryColor,theme:i.theme||"light",modalTitle:i.modalTitle,modalSubtitle:i.modalSubtitle,modalExampleQuestions:i.exampleQuestions||[],welcomeMessage:i.welcomeMessage}}catch(s){return console.warn("[CevvoWidget] Error fetching config:",s.message),null}}window.CevvoWidget={init:async(a={})=>{if(a.projectId&&a.apiKey){const o=await g(a.projectId,a.apiKey,a.apiUrl);if(o)for(const[e,t]of Object.entries(o))t!=null&&t!==""&&(a[e]=t)}return new h(a)},initWithRemoteConfig:async(a,o,e)=>{const t=e||p,s=await g(a,o,t),i={};if(s)for(const[d,c]of Object.entries(s))c!=null&&c!==""&&(i[d]=c);const r={projectId:a,apiKey:o,apiUrl:t,...i};return new h(r)},testCredentials:async(a,o,e)=>{const t=e||p;try{const s=await fetch(`${t}/widget/config?project_id=${encodeURIComponent(a)}`,{method:"GET",headers:{"X-API-Key":o}});return{success:s.ok,status:s.status,data:s.ok?await s.json():null}}catch(s){return{success:!1,status:0,error:s.message}}}},document.addEventListener("DOMContentLoaded",async()=>{const a=document.querySelector("script[data-cevvo-project-id]");if(a){const o=a.getAttribute("data-cevvo-project-id")||"",e=a.getAttribute("data-cevvo-api-key")||"",t=a.getAttribute("data-cevvo-api-url")||void 0;let s=null;o&&e&&(s=await g(o,e,t));const i={projectId:o,apiKey:e,apiUrl:t,...s,...a.getAttribute("data-cevvo-project-name")&&{projectName:a.getAttribute("data-cevvo-project-name")},...a.getAttribute("data-cevvo-project-color")&&{projectColor:a.getAttribute("data-cevvo-project-color")},...a.getAttribute("data-cevvo-button-text")&&{buttonText:a.getAttribute("data-cevvo-button-text")},...a.getAttribute("data-cevvo-button-bg-color")&&{buttonBgColor:a.getAttribute("data-cevvo-button-bg-color")},...a.getAttribute("data-cevvo-modal-title")&&{modalTitle:a.getAttribute("data-cevvo-modal-title")},...a.getAttribute("data-cevvo-modal-placeholder")&&{modalPlaceholder:a.getAttribute("data-cevvo-modal-placeholder")}},r=a.getAttribute("data-cevvo-example-questions");if(r)try{i.modalExampleQuestions=JSON.parse(r)}catch{}new h(i)}})})()})();
|