cevvo-widget 1.0.9 → 1.1.1
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 +53 -23
- 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;
|
|
@@ -578,9 +601,15 @@
|
|
|
578
601
|
display: none;
|
|
579
602
|
}
|
|
580
603
|
|
|
604
|
+
.cevvo-modal-overlay.mode-modal.has-messages .cevvo-modal-description {
|
|
605
|
+
display: none;
|
|
606
|
+
}
|
|
607
|
+
|
|
581
608
|
.cevvo-modal-overlay.mode-modal.has-messages .cevvo-modal-body {
|
|
582
609
|
display: block;
|
|
583
|
-
|
|
610
|
+
flex: 1;
|
|
611
|
+
overflow-y: auto;
|
|
612
|
+
max-height: 60vh;
|
|
584
613
|
}
|
|
585
614
|
|
|
586
615
|
.cevvo-modal-overlay.mode-modal .cevvo-modal-footer {
|
|
@@ -836,7 +865,7 @@
|
|
|
836
865
|
border-color: #38383a;
|
|
837
866
|
color: #e5e5ea;
|
|
838
867
|
}
|
|
839
|
-
`;class h{constructor(
|
|
868
|
+
`;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
869
|
<div class="cevvo-modal">
|
|
841
870
|
<div class="cevvo-modal-header">
|
|
842
871
|
<div class="cevvo-header-info">
|
|
@@ -855,7 +884,7 @@
|
|
|
855
884
|
<div class="cevvo-empty-state">
|
|
856
885
|
<div class="cevvo-empty-icon">${n.sparkle}</div>
|
|
857
886
|
<h3>${this.escapeHtml(this.config.welcomeMessage)}</h3>
|
|
858
|
-
${
|
|
887
|
+
${o?`<div class="cevvo-example-questions">${o}</div>`:""}
|
|
859
888
|
</div>
|
|
860
889
|
</div>
|
|
861
890
|
<div class="cevvo-modal-footer">
|
|
@@ -867,7 +896,7 @@
|
|
|
867
896
|
<p class="cevvo-powered">Powered by <a href="https://cevvo.ai" target="_blank" rel="noopener">Cevvo</a></p>
|
|
868
897
|
</div>
|
|
869
898
|
</div>
|
|
870
|
-
`}renderModalMode(){const
|
|
899
|
+
`}renderModalMode(){const o=this.config.modalDescription||this.config.modalSubtitle||"Ask me anything about the documentation.";return`
|
|
871
900
|
<div class="cevvo-modal">
|
|
872
901
|
<div class="cevvo-modal-header">
|
|
873
902
|
<div class="cevvo-header-info">
|
|
@@ -882,7 +911,7 @@
|
|
|
882
911
|
</div>
|
|
883
912
|
</div>
|
|
884
913
|
<div class="cevvo-modal-description" style="padding: 0 24px;">
|
|
885
|
-
${
|
|
914
|
+
${o}
|
|
886
915
|
</div>
|
|
887
916
|
<div class="cevvo-modal-body">
|
|
888
917
|
</div>
|
|
@@ -914,25 +943,25 @@
|
|
|
914
943
|
</div>
|
|
915
944
|
</div>
|
|
916
945
|
</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(
|
|
946
|
+
`}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(`
|
|
947
|
+
`);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
948
|
<div class="cevvo-empty-state">
|
|
920
949
|
<div class="cevvo-empty-icon">${n.sparkle}</div>
|
|
921
950
|
<h3>${this.escapeHtml(this.config.welcomeMessage)}</h3>
|
|
922
951
|
${this.config.modalExampleQuestions.length?`
|
|
923
952
|
<div class="cevvo-example-questions">
|
|
924
|
-
${this.config.modalExampleQuestions.map(
|
|
953
|
+
${this.config.modalExampleQuestions.map(o=>`<button class="cevvo-example-btn">${this.escapeHtml(o)}</button>`).join("")}
|
|
925
954
|
</div>
|
|
926
955
|
`:""}
|
|
927
956
|
</div>
|
|
928
957
|
`;return}this.messagesContainer.innerHTML=`
|
|
929
958
|
<div class="cevvo-messages">
|
|
930
|
-
${this.messages.map(
|
|
959
|
+
${this.messages.map(o=>this.renderMessage(o)).join("")}
|
|
931
960
|
</div>
|
|
932
|
-
`,this.messagesContainer.scrollTop=this.messagesContainer.scrollHeight}renderMessage(
|
|
961
|
+
`,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
962
|
<div class="cevvo-sources">
|
|
934
963
|
<div class="cevvo-sources-header">${n.link} Sources</div>
|
|
935
|
-
${
|
|
964
|
+
${o.sources.map((c,l)=>`
|
|
936
965
|
<a href="${this.escapeHtml(c.source_url||c.url||"#")}" target="_blank" rel="noopener" class="cevvo-source-link">
|
|
937
966
|
<span class="cevvo-source-num">${l+1}</span>
|
|
938
967
|
<span class="cevvo-source-title">${this.escapeHtml(c.title||"Source")}</span>
|
|
@@ -940,19 +969,20 @@
|
|
|
940
969
|
</a>
|
|
941
970
|
`).join("")}
|
|
942
971
|
</div>
|
|
943
|
-
`:"",i=
|
|
944
|
-
<div class="cevvo-actions ${i}" data-message-id="${
|
|
972
|
+
`:"",i=o.feedback==="positive"?"feedback-positive":o.feedback==="negative"?"feedback-negative":"",r=o.role==="assistant"&&o.content&&!o.isLoading?`
|
|
973
|
+
<div class="cevvo-actions ${i}" data-message-id="${o.messageId||""}">
|
|
945
974
|
<button class="cevvo-copy-btn" title="Copy">${n.copy}</button>
|
|
946
|
-
<button class="cevvo-thumbs-up ${
|
|
947
|
-
<button class="cevvo-thumbs-down ${
|
|
975
|
+
<button class="cevvo-thumbs-up ${o.feedback==="positive"?"active":""}" title="Helpful">${n.thumbsUp}</button>
|
|
976
|
+
<button class="cevvo-thumbs-down ${o.feedback==="negative"?"active":""}" title="Not helpful">${n.thumbsDown}</button>
|
|
948
977
|
</div>
|
|
949
978
|
`:"";return`
|
|
950
|
-
<div class="cevvo-message ${
|
|
951
|
-
<div class="cevvo-avatar ${
|
|
979
|
+
<div class="cevvo-message ${o.role}" data-msg-id="${o.id}">
|
|
980
|
+
<div class="cevvo-avatar ${o.role}">${e}</div>
|
|
952
981
|
<div class="cevvo-message-content">
|
|
953
|
-
<div class="cevvo-bubble ${
|
|
982
|
+
<div class="cevvo-bubble ${o.role}">${t}</div>
|
|
954
983
|
${s}
|
|
955
984
|
${r}
|
|
956
985
|
</div>
|
|
957
986
|
</div>
|
|
958
|
-
`}async submitFeedback(e
|
|
987
|
+
`}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,`
|
|
988
|
+
`)}</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)}})})()})();
|