brainerce 1.28.1 → 1.31.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 +20 -0
- package/dist/bot/bootstrap.global.js +57 -0
- package/dist/bot/index.d.mts +63 -0
- package/dist/bot/index.d.ts +63 -0
- package/dist/bot/index.js +532 -0
- package/dist/bot/index.mjs +505 -0
- package/dist/index.d.mts +131 -0
- package/dist/index.d.ts +131 -0
- package/dist/index.js +102 -0
- package/dist/index.mjs +102 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -4852,6 +4852,26 @@ const forms = await brainerce.contactForms.list();
|
|
|
4852
4852
|
|
|
4853
4853
|
---
|
|
4854
4854
|
|
|
4855
|
+
## Storefront Bot (AI chat widget)
|
|
4856
|
+
|
|
4857
|
+
Add the store's AI shopping assistant with one line. All configuration (name, avatar, colors, greeting, starter questions, guardrails) lives in the merchant dashboard — the widget renders nothing until the bot is switched Live there.
|
|
4858
|
+
|
|
4859
|
+
```html
|
|
4860
|
+
<!-- zero-code embed: keep the tag exactly this bare (no integrity/crossorigin) -->
|
|
4861
|
+
<script src="https://cdn.brainerce.com/bot.js" data-connection-id="vc_abc123" defer></script>
|
|
4862
|
+
```
|
|
4863
|
+
|
|
4864
|
+
```ts
|
|
4865
|
+
// or mount from the SDK (client-side only)
|
|
4866
|
+
import { BrainerceBot } from 'brainerce/bot';
|
|
4867
|
+
|
|
4868
|
+
const bot = await BrainerceBot.mount({ connectionId: 'vc_abc123' });
|
|
4869
|
+
// resolves to null when the bot is disabled — safe to call unconditionally
|
|
4870
|
+
bot?.destroy(); // optional teardown
|
|
4871
|
+
```
|
|
4872
|
+
|
|
4873
|
+
The widget persists an anonymous session in `localStorage`, restores conversations on revisit, streams answers, shows product recommendation cards (linking to `/products/<slug>`), and includes a leave-a-message form that lands in the merchant's Inquiries inbox. It is read-only by design — shoppers can never mutate the store through it.
|
|
4874
|
+
|
|
4855
4875
|
## Webhooks
|
|
4856
4876
|
|
|
4857
4877
|
Receive real-time updates when products, orders, or inventory change.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";(()=>{var N="https://api.brainerce.com",U=new Set(["he","ar"]),M={en:{online:"Online",placeholder:"Ask anything\u2026",error:"Something went wrong \u2014 please try again.",leaveMessage:"Leave a message for the team",yourEmail:"Your email",yourMessage:"Your message",send:"Send",sent:"Thanks! The team will get back to you by email.",close:"Close"},he:{online:"\u05DE\u05D7\u05D5\u05D1\u05E8",placeholder:"\u05E9\u05D0\u05DC\u05D5 \u05D0\u05D5\u05EA\u05D9 \u05D4\u05DB\u05DC\u2026",error:"\u05DE\u05E9\u05D4\u05D5 \u05D4\u05E9\u05EA\u05D1\u05E9 \u2014 \u05E0\u05E1\u05D5 \u05E9\u05D5\u05D1.",leaveMessage:"\u05D4\u05E9\u05D0\u05D9\u05E8\u05D5 \u05D4\u05D5\u05D3\u05E2\u05D4 \u05DC\u05E6\u05D5\u05D5\u05EA",yourEmail:"\u05D4\u05D0\u05D9\u05DE\u05D9\u05D9\u05DC \u05E9\u05DC\u05DB\u05DD",yourMessage:"\u05D4\u05D4\u05D5\u05D3\u05E2\u05D4 \u05E9\u05DC\u05DB\u05DD",send:"\u05E9\u05DC\u05D9\u05D7\u05D4",sent:"\u05EA\u05D5\u05D3\u05D4! \u05D4\u05E6\u05D5\u05D5\u05EA \u05D9\u05D7\u05D6\u05D5\u05E8 \u05D0\u05DC\u05D9\u05DB\u05DD \u05D1\u05DE\u05D9\u05D9\u05DC.",close:"\u05E1\u05D2\u05D9\u05E8\u05D4"}};function $(r){let e=new Uint8Array(16);crypto.getRandomValues(e);let t=btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"");return`${r}${t}`}var S=class r{constructor(e){this.settings={enabled:!1};this.locale="en";this.sessionId=null;this.conversationId=null;this.busy=!1;this.opened=!1;this.destroyed=!1;this.connectionId=e.connectionId,this.baseUrl=(e.baseUrl||N).replace(/\/$/,""),this.storageKey=`brainerce-bot:${this.connectionId}`}static async mount(e){if(!e?.connectionId)return console.warn("[BrainerceBot] connectionId is required"),null;let t=new r(e);return await t.boot(e.target??document.body)?t:null}destroy(){this.destroyed=!0,this.host?.remove()}async boot(e){try{let t=await fetch(`${this.baseUrl}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/settings`);if(!t.ok)return!1;this.settings=await t.json()}catch{return!1}return this.settings.enabled?(this.locale=this.settings.languages?.[0]??"en",this.restoreIds(),this.render(e),this.settings.displayMode==="auto_open"&&setTimeout(()=>!this.destroyed&&this.open(),3e3),!0):!1}t(e){return(M[this.locale]??M.en)[e]??M.en[e]??e}restoreIds(){try{let e=localStorage.getItem(this.storageKey);if(e){let t=JSON.parse(e);this.sessionId=t.sessionId??null,this.conversationId=t.conversationId??null}}catch{}}persistIds(){try{localStorage.setItem(this.storageKey,JSON.stringify({sessionId:this.sessionId,conversationId:this.conversationId}))}catch{}}render(e){let t=this.settings.accentColor||"#6366F1",n=U.has(this.locale)?"rtl":"ltr",d=this.settings.bubbleShape==="square"?"8px":"16px",s=this.settings.position==="start"?"left":"right",l=n==="rtl"?s==="left"?"right":"left":s;this.host=document.createElement("div"),this.host.setAttribute("data-brainerce-bot",this.connectionId),this.root=this.host.attachShadow({mode:"open"});let x=document.createElement("style");x.textContent=`
|
|
2
|
+
:host { all: initial; }
|
|
3
|
+
* { box-sizing: border-box; font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; }
|
|
4
|
+
.bb-root { position: fixed; bottom: 20px; ${l}: 20px; z-index: 2147483000; direction: ${n}; }
|
|
5
|
+
.bb-launcher {
|
|
6
|
+
width: 56px; height: 56px; border: none; cursor: pointer; display: flex;
|
|
7
|
+
align-items: center; justify-content: center; color: #fff; background: ${t};
|
|
8
|
+
border-radius: ${this.settings.bubbleShape==="square"?"14px":"9999px"};
|
|
9
|
+
box-shadow: 0 8px 24px rgba(0,0,0,.22); transition: transform .15s ease;
|
|
10
|
+
overflow: hidden; padding: 0;
|
|
11
|
+
}
|
|
12
|
+
.bb-launcher:hover { transform: scale(1.06); }
|
|
13
|
+
.bb-launcher img { width: 100%; height: 100%; object-fit: cover; }
|
|
14
|
+
.bb-window {
|
|
15
|
+
position: absolute; bottom: 70px; ${l}: 0; width: 360px; max-width: calc(100vw - 32px);
|
|
16
|
+
height: 540px; max-height: calc(100vh - 110px); display: none; flex-direction: column;
|
|
17
|
+
background: #fff; border-radius: 16px; overflow: hidden;
|
|
18
|
+
box-shadow: 0 16px 48px rgba(0,0,0,.24); border: 1px solid rgba(0,0,0,.06);
|
|
19
|
+
}
|
|
20
|
+
.bb-window.open { display: flex; }
|
|
21
|
+
.bb-header { display: flex; align-items: center; gap: 10px; padding: 12px 14px; background: ${t}; color: #fff; }
|
|
22
|
+
.bb-avatar { width: 34px; height: 34px; border-radius: 9999px; background: rgba(255,255,255,.25);
|
|
23
|
+
display: flex; align-items: center; justify-content: center; font-weight: 600; overflow: hidden; flex-shrink: 0; }
|
|
24
|
+
.bb-avatar img { width: 100%; height: 100%; object-fit: cover; }
|
|
25
|
+
.bb-head-main { flex: 1; min-width: 0; }
|
|
26
|
+
.bb-name { font-size: 14px; font-weight: 600; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
27
|
+
.bb-status { font-size: 11px; opacity: .9; display: flex; align-items: center; gap: 5px; }
|
|
28
|
+
.bb-dot { width: 6px; height: 6px; border-radius: 9999px; background: #34d399; }
|
|
29
|
+
.bb-iconbtn { background: none; border: none; color: #fff; cursor: pointer; opacity: .85; font-size: 16px; padding: 4px; }
|
|
30
|
+
.bb-iconbtn:hover { opacity: 1; }
|
|
31
|
+
.bb-messages { flex: 1; overflow-y: auto; padding: 14px; background: #f7f7f9; display: flex; flex-direction: column; gap: 8px; }
|
|
32
|
+
.bb-msg { max-width: 80%; padding: 9px 12px; border-radius: ${d}; font-size: 13.5px; line-height: 1.45; white-space: pre-wrap; word-break: break-word; }
|
|
33
|
+
.bb-msg.bot { align-self: flex-start; background: #fff; border: 1px solid rgba(0,0,0,.07); border-end-start-radius: 4px; }
|
|
34
|
+
.bb-msg.user { align-self: flex-end; background: ${t}; color: #fff; border-end-end-radius: 4px; }
|
|
35
|
+
.bb-msg.err { align-self: flex-start; background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca; }
|
|
36
|
+
.bb-typing { align-self: flex-start; font-size: 11.5px; color: #6b7280; padding: 2px 4px; }
|
|
37
|
+
.bb-card { align-self: flex-start; width: 230px; background: #fff; border: 1px solid rgba(0,0,0,.08);
|
|
38
|
+
border-radius: 12px; overflow: hidden; text-decoration: none; color: inherit; display: block; }
|
|
39
|
+
.bb-card img { width: 100%; height: 120px; object-fit: cover; display: block; background: #eee; }
|
|
40
|
+
.bb-card-body { padding: 9px 11px; }
|
|
41
|
+
.bb-card-title { font-size: 13px; font-weight: 600; margin: 0 0 3px; }
|
|
42
|
+
.bb-card-price { font-size: 13px; color: ${t}; font-weight: 600; }
|
|
43
|
+
.bb-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 14px 10px; background: #f7f7f9; }
|
|
44
|
+
.bb-chip { border: 1px solid ${t}; color: ${t}; background: #fff; border-radius: 9999px;
|
|
45
|
+
font-size: 12px; padding: 5px 11px; cursor: pointer; }
|
|
46
|
+
.bb-inputrow { display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid rgba(0,0,0,.07); background: #fff; }
|
|
47
|
+
.bb-input { flex: 1; border: none; outline: none; font-size: 13.5px; background: #f1f1f4; border-radius: 9999px; padding: 9px 14px; }
|
|
48
|
+
.bb-send { border: none; background: ${t}; color: #fff; width: 36px; height: 36px; border-radius: 9999px; cursor: pointer; font-size: 15px; flex-shrink: 0; }
|
|
49
|
+
.bb-send:disabled { opacity: .5; cursor: default; }
|
|
50
|
+
.bb-esc { padding: 12px 14px; background: #fff; border-top: 1px solid rgba(0,0,0,.07); display: none; flex-direction: column; gap: 8px; }
|
|
51
|
+
.bb-esc.open { display: flex; }
|
|
52
|
+
.bb-esc input, .bb-esc textarea { border: 1px solid rgba(0,0,0,.12); border-radius: 8px; padding: 8px 10px; font-size: 13px; outline: none; resize: none; }
|
|
53
|
+
.bb-esc button { border: none; background: ${t}; color: #fff; border-radius: 8px; padding: 8px; font-size: 13px; cursor: pointer; }
|
|
54
|
+
.bb-esc-note { font-size: 12px; color: #047857; }
|
|
55
|
+
`,this.root.appendChild(x);let o=document.createElement("div");o.className="bb-root",this.root.appendChild(o),this.windowEl=document.createElement("div"),this.windowEl.className="bb-window",o.appendChild(this.windowEl);let m=this.settings.displayName||"Assistant",a=document.createElement("div");a.className="bb-header";let c=document.createElement("span");if(c.className="bb-avatar",this.settings.avatarUrl&&k(this.settings.avatarUrl)){let i=document.createElement("img");i.src=this.settings.avatarUrl,i.alt="",c.appendChild(i)}else c.textContent=m.charAt(0).toUpperCase();let p=document.createElement("span");p.className="bb-head-main";let b=document.createElement("span");b.className="bb-name",b.textContent=m;let v=document.createElement("span");v.className="bb-status";let T=document.createElement("span");T.className="bb-dot",v.appendChild(T),v.appendChild(document.createTextNode(this.t("online"))),p.appendChild(b),p.appendChild(v);let g=document.createElement("button");g.className="bb-iconbtn",g.dataset.act="esc",g.title=this.t("leaveMessage"),g.textContent="\u2709";let f=document.createElement("button");f.className="bb-iconbtn",f.dataset.act="close",f.title=this.t("close"),f.textContent="\u2715",a.appendChild(c),a.appendChild(p),a.appendChild(g),a.appendChild(f),this.windowEl.appendChild(a),a.querySelector('[data-act="close"]')?.addEventListener("click",()=>this.close()),a.querySelector('[data-act="esc"]')?.addEventListener("click",()=>this.toggleEscalation()),this.messagesEl=document.createElement("div"),this.messagesEl.className="bb-messages",this.windowEl.appendChild(this.messagesEl),this.chipsEl=document.createElement("div"),this.chipsEl.className="bb-chips";for(let i of this.settings.starterQuestions??[]){let I=document.createElement("button");I.className="bb-chip",I.textContent=i,I.addEventListener("click",()=>this.send(i)),this.chipsEl.appendChild(I)}this.windowEl.appendChild(this.chipsEl);let h=document.createElement("div");h.className="bb-esc";let E=document.createElement("input");E.type="email",E.name="email",E.placeholder=this.t("yourEmail");let y=document.createElement("textarea");y.name="message",y.rows=2,y.placeholder=this.t("yourMessage");let L=document.createElement("button");L.type="button",L.textContent=this.t("send"),h.appendChild(E),h.appendChild(y),h.appendChild(L),h.querySelector("button")?.addEventListener("click",()=>this.submitEscalation(h)),this.windowEl.appendChild(h);let w=document.createElement("div");w.className="bb-inputrow",this.inputEl=document.createElement("input"),this.inputEl.className="bb-input",this.inputEl.placeholder=this.t("placeholder"),this.inputEl.addEventListener("keydown",i=>{i.key==="Enter"&&this.send(this.inputEl?.value??"")});let C=document.createElement("button");C.className="bb-send",C.textContent="\u27A4",C.addEventListener("click",()=>this.send(this.inputEl?.value??"")),w.appendChild(this.inputEl),w.appendChild(C),this.windowEl.appendChild(w);let u=document.createElement("button");if(u.className="bb-launcher",u.setAttribute("aria-label",m),this.settings.avatarUrl&&k(this.settings.avatarUrl)){let i=document.createElement("img");i.src=this.settings.avatarUrl,i.alt="",u.appendChild(i)}else u.textContent="\u{1F4AC}";u.addEventListener("click",()=>this.opened?this.close():this.open()),o.appendChild(u),e.appendChild(this.host)}open(){!this.windowEl||this.opened||(this.opened=!0,this.windowEl.classList.add("open"),this.messagesEl&&this.messagesEl.childElementCount===0&&this.primeThread(),this.inputEl?.focus())}close(){this.opened=!1,this.windowEl?.classList.remove("open")}async primeThread(){if(this.conversationId&&this.sessionId)try{let e=await fetch(`${this.baseUrl}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/conversations/${encodeURIComponent(this.conversationId)}?limit=50`,{headers:{"X-Bot-Session":this.sessionId}});if(e.ok){let t=await e.json();for(let n of t.data)this.appendMessage(n.role==="assistant"?"bot":"user",n.content);if(t.data.length>0){this.chipsEl?.remove();return}}else this.conversationId=null,this.sessionId=null,this.persistIds()}catch{}this.settings.greeting&&this.appendMessage("bot",this.settings.greeting)}async send(e){let t=e.trim();if(!t||this.busy)return;this.busy=!0,this.inputEl&&(this.inputEl.value=""),this.chipsEl?.remove(),this.appendMessage("user",t);let n=this.appendTyping(),d=null;try{let s=await fetch(`${this.baseUrl}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:t,turnId:$("trn_"),...this.conversationId?{conversationId:this.conversationId}:{},...this.sessionId?{anonymousSessionId:this.sessionId}:{},locale:this.locale})});if(!s.ok||!s.body)throw new Error(`chat failed (${s.status})`);let l=s.body.getReader(),x=new TextDecoder,o="";for(;;){let{value:m,done:a}=await l.read();if(a)break;o+=x.decode(m,{stream:!0});let c;for(;(c=o.indexOf(`
|
|
56
|
+
|
|
57
|
+
`))>=0;){let p=o.slice(0,c);if(o=o.slice(c+2),!p.startsWith("data: "))continue;let b;try{b=JSON.parse(p.slice(6))}catch{continue}d=this.handleFrame(b,n,d)}}}catch{this.appendMessage("err",this.t("error"))}finally{n.remove(),this.busy=!1}}handleFrame(e,t,n){switch(e.type){case"connected":return this.conversationId=e.conversationId||this.conversationId,this.sessionId=e.anonymousSessionId||this.sessionId,this.persistIds(),n;case"token":return n||(t.remove(),n=this.appendMessage("bot","")),n.textContent=(n.textContent??"")+e.text,this.scrollDown(),n;case"tool":return t.textContent=e.status==="running"?"\u22EF":"",n;case"card":return this.appendCard(e.card),n;case"error":return this.appendMessage("err",e.message||this.t("error")),n;case"done":default:return n}}toggleEscalation(){this.root?.querySelector(".bb-esc")?.classList.toggle("open")}async submitEscalation(e){let t=e.querySelector('input[name="email"]')?.value.trim(),n=e.querySelector('textarea[name="message"]')?.value.trim();if(!(!t||!n||!this.conversationId||!this.sessionId))try{if((await fetch(`${this.baseUrl}/api/storefront-bot/${encodeURIComponent(this.connectionId)}/escalate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:t,message:n,conversationId:this.conversationId,anonymousSessionId:this.sessionId,locale:this.locale})})).ok){let s=document.createElement("span");s.className="bb-esc-note",s.textContent=this.t("sent"),e.replaceChildren(s)}}catch{}}appendCard(e){if(!this.messagesEl)return;let t=document.createElement("a");if(t.className="bb-card",t.href=k(e.url)?e.url:"#",e.imageUrl&&k(e.imageUrl)){let l=document.createElement("img");l.src=e.imageUrl,l.alt="",t.appendChild(l)}let n=document.createElement("span");n.className="bb-card-body";let d=document.createElement("p");d.className="bb-card-title",d.textContent=e.title;let s=document.createElement("span");s.className="bb-card-price",s.textContent=e.price.formatted,n.appendChild(d),n.appendChild(s),t.appendChild(n),t.addEventListener("click",()=>{try{navigator.sendBeacon?.(`${this.baseUrl}/api/storefront-bot/attribution/click`,new Blob([JSON.stringify({botRef:e.botRef})],{type:"application/json"}))}catch{}}),this.messagesEl.appendChild(t),this.scrollDown()}appendMessage(e,t){let n=document.createElement("div");return n.className=`bb-msg ${e}`,n.textContent=t,this.messagesEl?.appendChild(n),this.scrollDown(),n}appendTyping(){let e=document.createElement("div");return e.className="bb-typing",e.textContent="\u22EF",this.messagesEl?.appendChild(e),this.scrollDown(),e}scrollDown(){this.messagesEl&&(this.messagesEl.scrollTop=this.messagesEl.scrollHeight)}};function k(r){return/^\/(?!\/)/.test(r)||/^https?:\/\//i.test(r)}(()=>{let r=document.currentScript,e=r?.dataset.connectionId;if(!e){console.warn("[BrainerceBot] missing data-connection-id on the bot.js script tag");return}let t=r?.dataset.apiBase||void 0,n=()=>void S.mount({connectionId:e,baseUrl:t});document.readyState==="loading"?document.addEventListener("DOMContentLoaded",n,{once:!0}):n()})();})();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrainerceBot — the embeddable shopper-facing chat widget.
|
|
3
|
+
*
|
|
4
|
+
* Framework-free (vanilla DOM inside a Shadow Root, zero dependencies) so the
|
|
5
|
+
* same code serves both the `bot.js` one-liner embed and the SDK's
|
|
6
|
+
* `BrainerceBot.mount(...)` API. ALL behavior/config is server-side: the
|
|
7
|
+
* widget boots from `GET /api/storefront-bot/:connectionId/settings` and
|
|
8
|
+
* renders nothing when the bot is disabled.
|
|
9
|
+
*
|
|
10
|
+
* Security model: the widget holds only an opaque server-minted
|
|
11
|
+
* anonymousSessionId (localStorage). The server re-validates it on every call
|
|
12
|
+
* and silently replaces it when stale — the widget always adopts whatever ids
|
|
13
|
+
* arrive on the `connected` frame.
|
|
14
|
+
*/
|
|
15
|
+
interface BrainerceBotOptions {
|
|
16
|
+
/** Public vc_* connection id of the storefront. */
|
|
17
|
+
connectionId: string;
|
|
18
|
+
/** API origin. Defaults to the Brainerce cloud. */
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
/** Mount target. Defaults to document.body. */
|
|
21
|
+
target?: HTMLElement;
|
|
22
|
+
}
|
|
23
|
+
declare class BrainerceBot {
|
|
24
|
+
private readonly connectionId;
|
|
25
|
+
private readonly baseUrl;
|
|
26
|
+
private readonly storageKey;
|
|
27
|
+
private host?;
|
|
28
|
+
private root?;
|
|
29
|
+
private windowEl?;
|
|
30
|
+
private messagesEl?;
|
|
31
|
+
private inputEl?;
|
|
32
|
+
private chipsEl?;
|
|
33
|
+
private settings;
|
|
34
|
+
private locale;
|
|
35
|
+
private sessionId;
|
|
36
|
+
private conversationId;
|
|
37
|
+
private busy;
|
|
38
|
+
private opened;
|
|
39
|
+
private destroyed;
|
|
40
|
+
private constructor();
|
|
41
|
+
/** Boot the widget. Resolves to null when the bot is disabled server-side. */
|
|
42
|
+
static mount(options: BrainerceBotOptions): Promise<BrainerceBot | null>;
|
|
43
|
+
destroy(): void;
|
|
44
|
+
private boot;
|
|
45
|
+
private t;
|
|
46
|
+
private restoreIds;
|
|
47
|
+
private persistIds;
|
|
48
|
+
private render;
|
|
49
|
+
private open;
|
|
50
|
+
private close;
|
|
51
|
+
/** First open: restore the server thread, or show the greeting. */
|
|
52
|
+
private primeThread;
|
|
53
|
+
private send;
|
|
54
|
+
private handleFrame;
|
|
55
|
+
private toggleEscalation;
|
|
56
|
+
private submitEscalation;
|
|
57
|
+
private appendCard;
|
|
58
|
+
private appendMessage;
|
|
59
|
+
private appendTyping;
|
|
60
|
+
private scrollDown;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { BrainerceBot, type BrainerceBotOptions };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrainerceBot — the embeddable shopper-facing chat widget.
|
|
3
|
+
*
|
|
4
|
+
* Framework-free (vanilla DOM inside a Shadow Root, zero dependencies) so the
|
|
5
|
+
* same code serves both the `bot.js` one-liner embed and the SDK's
|
|
6
|
+
* `BrainerceBot.mount(...)` API. ALL behavior/config is server-side: the
|
|
7
|
+
* widget boots from `GET /api/storefront-bot/:connectionId/settings` and
|
|
8
|
+
* renders nothing when the bot is disabled.
|
|
9
|
+
*
|
|
10
|
+
* Security model: the widget holds only an opaque server-minted
|
|
11
|
+
* anonymousSessionId (localStorage). The server re-validates it on every call
|
|
12
|
+
* and silently replaces it when stale — the widget always adopts whatever ids
|
|
13
|
+
* arrive on the `connected` frame.
|
|
14
|
+
*/
|
|
15
|
+
interface BrainerceBotOptions {
|
|
16
|
+
/** Public vc_* connection id of the storefront. */
|
|
17
|
+
connectionId: string;
|
|
18
|
+
/** API origin. Defaults to the Brainerce cloud. */
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
/** Mount target. Defaults to document.body. */
|
|
21
|
+
target?: HTMLElement;
|
|
22
|
+
}
|
|
23
|
+
declare class BrainerceBot {
|
|
24
|
+
private readonly connectionId;
|
|
25
|
+
private readonly baseUrl;
|
|
26
|
+
private readonly storageKey;
|
|
27
|
+
private host?;
|
|
28
|
+
private root?;
|
|
29
|
+
private windowEl?;
|
|
30
|
+
private messagesEl?;
|
|
31
|
+
private inputEl?;
|
|
32
|
+
private chipsEl?;
|
|
33
|
+
private settings;
|
|
34
|
+
private locale;
|
|
35
|
+
private sessionId;
|
|
36
|
+
private conversationId;
|
|
37
|
+
private busy;
|
|
38
|
+
private opened;
|
|
39
|
+
private destroyed;
|
|
40
|
+
private constructor();
|
|
41
|
+
/** Boot the widget. Resolves to null when the bot is disabled server-side. */
|
|
42
|
+
static mount(options: BrainerceBotOptions): Promise<BrainerceBot | null>;
|
|
43
|
+
destroy(): void;
|
|
44
|
+
private boot;
|
|
45
|
+
private t;
|
|
46
|
+
private restoreIds;
|
|
47
|
+
private persistIds;
|
|
48
|
+
private render;
|
|
49
|
+
private open;
|
|
50
|
+
private close;
|
|
51
|
+
/** First open: restore the server thread, or show the greeting. */
|
|
52
|
+
private primeThread;
|
|
53
|
+
private send;
|
|
54
|
+
private handleFrame;
|
|
55
|
+
private toggleEscalation;
|
|
56
|
+
private submitEscalation;
|
|
57
|
+
private appendCard;
|
|
58
|
+
private appendMessage;
|
|
59
|
+
private appendTyping;
|
|
60
|
+
private scrollDown;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export { BrainerceBot, type BrainerceBotOptions };
|