dewee-webchat 0.0.0-stage → 0.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.
@@ -0,0 +1,252 @@
1
+ "use strict";(()=>{var z=Object.defineProperty;var K=(i,r,e)=>r in i?z(i,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[r]=e;var j=(i,r)=>{for(var e in r)z(i,e,{get:r[e],enumerable:!0})};var l=(i,r,e)=>K(i,typeof r!="symbol"?r+"":r,e);var I={};j(I,{ApiClient:()=>y,ApiError:()=>w,DeweeChatElement:()=>E,defineDeweeChat:()=>H,mount:()=>ne,renderMarkdown:()=>k});var _="x-dewee-gateway-url",P="x-dewee-gateway-token";var w=class extends Error{constructor(e,t,n){super(e);l(this,"status",t);l(this,"code",n)}},A={get(i){try{return localStorage.getItem(i)}catch{return null}},set(i,r){try{localStorage.setItem(i,r)}catch{}},del(i){try{localStorage.removeItem(i)}catch{}}},y=class{constructor(r){l(this,"o",r);l(this,"session",null);l(this,"config",null);l(this,"sessionPromise",null)}url(r,e){let t=this.o.endpoint.replace(/\/+$/,""),n=e?"?"+new URLSearchParams(e).toString():"";return`${t}/${r}${n}`}get storageKey(){return`dewee-webchat:session:${this.o.endpoint}`}headers(r=!1){let e={"content-type":"application/json"};return this.o.gatewayUrl&&this.o.gatewayToken&&(e[_]=this.o.gatewayUrl,e[P]=this.o.gatewayToken),r&&this.session&&(e.authorization=`Bearer ${this.session.token}`),e}async parse(r){let e=await r.json().catch(()=>({}));if(!r.ok)throw new w(e?.error?.message||`HTTP ${r.status}`,r.status,e?.error?.code);return e}async getConfig(r=!1){return this.config&&!r?this.config:(this.config=await this.parse(await fetch(this.url("config"),{headers:this.headers()})),this.config)}ensureSession(){if(this.session&&this.session.expiresAt-Date.now()>864e5)return Promise.resolve(this.session);if(!this.session){let r=A.get(this.storageKey);if(r)try{let e=JSON.parse(r);if(e.expiresAt-Date.now()>864e5)return Promise.resolve(this.session=e);this.session=e}catch{}}return this.sessionPromise??(this.sessionPromise=this.createSession().finally(()=>this.sessionPromise=null))}async createSession(){let r=await this.getConfig(),e={};if(this.session?.token&&(e.token=this.session.token),r.turnstileSiteKey){if(!this.o.turnstile)throw new w("Verification is required but no Turnstile handler is set",403,"turnstile_required");e.turnstileToken=await this.o.turnstile(r.turnstileSiteKey)}let t=await this.parse(await fetch(this.url("session"),{method:"POST",headers:this.headers(),body:JSON.stringify(e)}));return this.session=t,A.set(this.storageKey,JSON.stringify(t)),t}forgetSession(){this.session=null,A.del(this.storageKey)}async history(r,e){await this.ensureSession();let t=await fetch(this.url("history",{agent:r,conversationId:e}),{headers:this.headers(!0)});return(await this.parse(t)).messages}async chat(r,e,t){await this.ensureSession();let n=await fetch(this.url("chat"),{method:"POST",headers:this.headers(!0),body:JSON.stringify(r),signal:t});n.status===401&&(this.forgetSession(),await this.ensureSession(),n=await fetch(this.url("chat"),{method:"POST",headers:this.headers(!0),body:JSON.stringify(r),signal:t})),(!n.ok||!n.body)&&await this.parse(n);let s=n.body.pipeThrough(new TextDecoderStream).getReader(),o="";for(;;){let{done:a,value:g}=await s.read();if(a)break;o+=g;let p;for(;(p=o.indexOf(`
2
+
3
+ `))>=0;){let f=o.slice(0,p);o=o.slice(p+2);let h=f.split(`
4
+ `).filter(d=>d.startsWith("data:")).map(d=>d.slice(5).trimStart()).join(`
5
+ `);if(h)try{e(JSON.parse(h))}catch{}}}}};var U={title:"Assistant",greeting:"Hi there \u{1F44B}",greetingBody:"Ask me anything. I can look things up and use tools to help you.",placeholder:"Type a message\u2026",send:"Send",stop:"Stop",newChat:"New conversation",close:"Close chat",open:"Open chat",thinking:"Thinking\u2026",thoughtFor:i=>`Thought for ${i}s`,reasoning:"Reasoning",toolRunning:"running",toolDone:"done",toolError:"error",args:"Arguments",result:"Result",online:"Online",poweredBy:"Powered by",errorGeneric:"Something went wrong. Please try again.",stopped:"Stopped."},F={title:"Tr\u1EE3 l\xFD",greeting:"Xin ch\xE0o \u{1F44B}",greetingBody:"B\u1EA1n c\u1EA7n h\u1ECFi g\xEC c\u1EE9 nh\u1EAFn nh\xE9. M\xECnh c\xF3 th\u1EC3 tra c\u1EE9u v\xE0 d\xF9ng c\xF4ng c\u1EE5 \u0111\u1EC3 h\u1ED7 tr\u1EE3 b\u1EA1n.",placeholder:"Nh\u1EADp tin nh\u1EAFn\u2026",send:"G\u1EEDi",stop:"D\u1EEBng",newChat:"Cu\u1ED9c tr\xF2 chuy\u1EC7n m\u1EDBi",close:"\u0110\xF3ng khung chat",open:"M\u1EDF khung chat",thinking:"\u0110ang suy ngh\u0129\u2026",thoughtFor:i=>`\u0110\xE3 suy ngh\u0129 ${i} gi\xE2y`,reasoning:"Suy lu\u1EADn",toolRunning:"\u0111ang ch\u1EA1y",toolDone:"xong",toolError:"l\u1ED7i",args:"Tham s\u1ED1",result:"K\u1EBFt qu\u1EA3",online:"\u0110ang tr\u1EF1c tuy\u1EBFn",poweredBy:"V\u1EADn h\xE0nh b\u1EDFi",errorGeneric:"C\xF3 l\u1ED7i x\u1EA3y ra. B\u1EA1n th\u1EED l\u1EA1i gi\xFAp m\xECnh nh\xE9.",stopped:"\u0110\xE3 d\u1EEBng."};function L(i){return(i||(typeof document<"u"?document.documentElement.lang:"")||(typeof navigator<"u"?navigator.language:"")||"en").toLowerCase().startsWith("vi")?F:U}var m={chat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a8.5 8.5 0 0 1-12.4 7.6L3 21l1.4-5.1A8.5 8.5 0 1 1 21 12Z"/><path d="M8.5 11h.01M12 11h.01M15.5 11h.01"/></svg>',close:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>',down:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg>',newChat:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"/><path d="M16.4 3.6a2.1 2.1 0 1 1 3 3L7 19l-4 1 1-4Z"/></svg>',send:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg>',stop:'<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="6" width="12" height="12" rx="2.5"/></svg>',brain:'<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24A2.5 2.5 0 0 0 14.5 2Z"/></svg>',tool:'<svg class="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76Z"/></svg>'};function c(i,r,e){let t=i.createElement(r);return e&&(t.className=e),t}function J(i){let r=i.trim();return/^(https?:|mailto:|tel:)/i.test(r)||/^[/#?]/.test(r)&&!r.startsWith("//")?r:null}var W=/(`+)([\s\S]*?[^`])\1(?!`)|\*\*([^*]+?)\*\*|__([^_]+?)__|~~([^~]+?)~~|\*([^*\s][^*]*?)\*|(?<![\w])_([^_\s][^_]*?)_(?![\w])|\[([^\]]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)|(https?:\/\/[^\s<>()]+[^\s<>().,;:!?'"])/;function b(i,r,e){let t=new RegExp(W.source,"g"),n=0;for(let s;s=t.exec(r);)if(s.index>n&&e.appendChild(i.createTextNode(r.slice(n,s.index))),n=s.index+s[0].length,s[2]!==void 0){let o=c(i,"code");o.textContent=s[2],e.appendChild(o)}else if(s[3]!==void 0||s[4]!==void 0){let o=c(i,"strong");b(i,s[3]??s[4],o),e.appendChild(o)}else if(s[5]!==void 0){let o=c(i,"del");b(i,s[5],o),e.appendChild(o)}else if(s[6]!==void 0||s[7]!==void 0){let o=c(i,"em");b(i,s[6]??s[7],o),e.appendChild(o)}else if(s[8]!==void 0){let o=J(s[9]);if(o){let a=c(i,"a");a.href=o,a.target="_blank",a.rel="noopener noreferrer nofollow",b(i,s[8],a),e.appendChild(a)}else e.appendChild(i.createTextNode(s[8]))}else if(s[10]!==void 0){let o=c(i,"a");o.href=s[10],o.target="_blank",o.rel="noopener noreferrer nofollow",o.textContent=s[10],e.appendChild(o)}n<r.length&&e.appendChild(i.createTextNode(r.slice(n)))}var B=i=>/^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/.test(i),D=i=>i.trim().replace(/^\||\|$/g,"").split("|").map(r=>r.trim());function k(i,r){let e=i.createDocumentFragment(),t=r.replace(/\r\n?/g,`
6
+ `).split(`
7
+ `),n=0;for(;n<t.length;){let s=t[n];if(!s.trim()){n++;continue}let o=s.match(/^\s*(```+|~~~+)\s*([\w+-]*)/);if(o){let h=[];for(n++;n<t.length&&!t[n].trim().startsWith(o[1]);)h.push(t[n++]);n++;let d=c(i,"pre"),u=c(i,"code");o[2]&&(u.dataset.lang=o[2]),u.textContent=h.join(`
8
+ `),d.appendChild(u),e.appendChild(d);continue}let a=s.match(/^(#{1,6})\s+(.*)$/);if(a){let h=`h${Math.min(6,a[1].length+2)}`,d=c(i,h);b(i,a[2],d),e.appendChild(d),n++;continue}if(/^\s*([-*_])(\s*\1){2,}\s*$/.test(s)){e.appendChild(c(i,"hr")),n++;continue}if(s.includes("|")&&n+1<t.length&&B(t[n+1])){let h=c(i,"div","table"),d=c(i,"table"),u=c(i,"thead"),v=c(i,"tr");for(let x of D(s)){let T=c(i,"th");b(i,x,T),v.appendChild(T)}u.appendChild(v),d.appendChild(u);let C=c(i,"tbody");for(n+=2;n<t.length&&t[n].includes("|")&&t[n].trim();){let x=c(i,"tr");for(let T of D(t[n])){let R=c(i,"td");b(i,T,R),x.appendChild(R)}C.appendChild(x),n++}d.appendChild(C),h.appendChild(d),e.appendChild(h);continue}if(/^\s*>/.test(s)){let h=[];for(;n<t.length&&/^\s*>/.test(t[n]);)h.push(t[n++].replace(/^\s*>\s?/,""));let d=c(i,"blockquote");d.appendChild(k(i,h.join(`
9
+ `))),e.appendChild(d);continue}let g=s.match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(g){let h=/\d/.test(g[2]),d=c(i,h?"ol":"ul");h&&parseInt(g[2],10)!==1&&d.setAttribute("start",String(parseInt(g[2],10)));let u=g[1].length;for(;n<t.length;){let v=t[n].match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);if(!v||v[1].length<u||/\d/.test(v[2])!==h)break;if(v[1].length>u){let x=[];for(;n<t.length&&/^\s+/.test(t[n])&&t[n].match(/^(\s*)/)[1].length>u;)x.push(t[n++].slice(u+2));d.lastElementChild?.appendChild(k(i,x.join(`
10
+ `)));continue}let C=c(i,"li");b(i,v[3],C),d.appendChild(C),n++}e.appendChild(d);continue}let p=[];for(;n<t.length&&t[n].trim()&&!/^\s*(```|~~~|#{1,6}\s|>|([-*+]|\d+[.)])\s)/.test(t[n])&&!(t[n].includes("|")&&n+1<t.length&&B(t[n+1]));)p.push(t[n++]);p.length||p.push(t[n++]);let f=c(i,"p");p.forEach((h,d)=>{d&&f.appendChild(c(i,"br")),b(i,h,f)}),e.appendChild(f)}return e}var N=`
11
+ :host {
12
+ --dw-accent: #5446e8;
13
+ --dw-accent-contrast: #fff;
14
+ --dw-bg: #ffffff;
15
+ --dw-surface: #f5f4fb;
16
+ --dw-fg: #1b1f4b;
17
+ --dw-muted: #6b6f8f;
18
+ --dw-border: #e4e2f1;
19
+ --dw-radius: 18px;
20
+ --dw-font: "Be Vietnam Pro", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
21
+ --dw-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
22
+ --dw-width: 400px;
23
+ --dw-height: 640px;
24
+ --dw-z: 2147483000;
25
+ --dw-shadow: 0 24px 64px -16px rgba(27, 31, 75, .28), 0 4px 16px -4px rgba(27, 31, 75, .12);
26
+ all: initial;
27
+ font-family: var(--dw-font);
28
+ color: var(--dw-fg);
29
+ }
30
+ :host([data-theme="dark"]) {
31
+ --dw-bg: #14152b;
32
+ --dw-surface: #1e2040;
33
+ --dw-fg: #ecebfa;
34
+ --dw-muted: #a2a4c6;
35
+ --dw-border: #2d2f55;
36
+ --dw-shadow: 0 24px 64px -12px rgba(0, 0, 0, .6);
37
+ }
38
+ * { box-sizing: border-box; }
39
+ button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0; }
40
+ .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
41
+
42
+ /* ---------- launcher ---------- */
43
+ .launcher {
44
+ position: fixed; bottom: 20px; right: 20px; z-index: var(--dw-z);
45
+ width: 58px; height: 58px; border-radius: 50%;
46
+ background: var(--dw-accent); color: var(--dw-accent-contrast);
47
+ display: grid; place-items: center;
48
+ box-shadow: 0 10px 30px -6px color-mix(in srgb, var(--dw-accent) 60%, transparent);
49
+ transition: transform .2s cubic-bezier(.3,1.4,.6,1), box-shadow .2s;
50
+ }
51
+ .launcher:hover { transform: translateY(-2px) scale(1.04); }
52
+ .launcher:focus-visible { outline: 3px solid color-mix(in srgb, var(--dw-accent) 45%, transparent); outline-offset: 3px; }
53
+ .launcher svg { width: 26px; height: 26px; transition: transform .25s, opacity .2s; }
54
+ .launcher img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
55
+ :host([data-position="bottom-left"]) .launcher { right: auto; left: 20px; }
56
+ .launcher .badge {
57
+ position: absolute; top: -2px; right: -2px; width: 14px; height: 14px; border-radius: 50%;
58
+ background: #ff5a7a; border: 2px solid var(--dw-bg); display: none;
59
+ }
60
+ :host([data-unread]) .launcher .badge { display: block; }
61
+
62
+ /* ---------- panel ---------- */
63
+ .panel {
64
+ position: fixed; bottom: 92px; right: 20px; z-index: var(--dw-z);
65
+ width: min(var(--dw-width), calc(100vw - 32px));
66
+ height: min(var(--dw-height), calc(100dvh - 120px));
67
+ background: var(--dw-bg); color: var(--dw-fg);
68
+ border: 1px solid var(--dw-border); border-radius: var(--dw-radius);
69
+ box-shadow: var(--dw-shadow);
70
+ display: flex; flex-direction: column; overflow: hidden;
71
+ transform-origin: bottom right;
72
+ transition: opacity .18s ease, transform .22s cubic-bezier(.2,.9,.3,1.1), visibility .2s;
73
+ }
74
+ :host([data-position="bottom-left"]) .panel { right: auto; left: 20px; transform-origin: bottom left; }
75
+ :host(:not([data-open])) .panel { opacity: 0; transform: translateY(12px) scale(.96); visibility: hidden; pointer-events: none; }
76
+ :host([data-mode="inline"]) .panel {
77
+ position: relative; inset: auto; width: 100%; height: 100%; min-height: 420px;
78
+ opacity: 1; transform: none; visibility: visible; pointer-events: auto; box-shadow: none;
79
+ }
80
+ :host([data-mode="inline"]) { display: block; height: var(--dw-height); }
81
+ :host([data-mode="inline"]) .launcher, :host([data-mode="inline"]) .close { display: none; }
82
+ @media (max-width: 520px) {
83
+ :host(:not([data-mode="inline"])) .panel { inset: 0; width: 100vw; height: 100dvh; border-radius: 0; border: 0; }
84
+ :host([data-open]:not([data-mode="inline"])) .launcher { display: none; }
85
+ }
86
+
87
+ /* ---------- header ---------- */
88
+ .head {
89
+ display: flex; align-items: center; gap: 12px; padding: 14px 14px 14px 16px;
90
+ border-bottom: 1px solid var(--dw-border);
91
+ background: linear-gradient(180deg, color-mix(in srgb, var(--dw-accent) 7%, var(--dw-bg)), var(--dw-bg));
92
+ }
93
+ .avatar {
94
+ flex: none; width: 38px; height: 38px; border-radius: 12px;
95
+ background: var(--dw-accent); color: var(--dw-accent-contrast);
96
+ display: grid; place-items: center; font-weight: 700; font-size: 16px; overflow: hidden;
97
+ }
98
+ .avatar img { width: 100%; height: 100%; object-fit: cover; }
99
+ .who { flex: 1; min-width: 0; }
100
+ .title { font-weight: 650; font-size: 15px; line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
101
+ .subtitle { font-size: 12px; color: var(--dw-muted); display: flex; align-items: center; gap: 6px; margin-top: 2px; }
102
+ .dot { width: 7px; height: 7px; border-radius: 50%; background: #22c58b; box-shadow: 0 0 0 3px color-mix(in srgb, #22c58b 22%, transparent); }
103
+ .agent-select {
104
+ font: inherit; font-size: 12px; color: var(--dw-muted); background: transparent; border: 0;
105
+ padding: 0 14px 0 0; margin: 0; max-width: 170px; cursor: pointer; appearance: none; field-sizing: content; text-overflow: ellipsis;
106
+ background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
107
+ background-position: right 4px top 55%, right 0 top 55%; background-size: 4px 4px; background-repeat: no-repeat;
108
+ }
109
+ .agent-select:focus-visible { outline: 2px solid var(--dw-accent); border-radius: 4px; }
110
+ .icon-btn {
111
+ width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; color: var(--dw-muted);
112
+ transition: background .15s, color .15s;
113
+ }
114
+ .icon-btn:hover { background: var(--dw-surface); color: var(--dw-fg); }
115
+ .icon-btn:focus-visible { outline: 2px solid var(--dw-accent); }
116
+ .icon-btn svg { width: 18px; height: 18px; }
117
+
118
+ /* ---------- log ---------- */
119
+ .log { flex: 1; overflow-y: auto; padding: 18px 16px 8px; scroll-behavior: smooth; overscroll-behavior: contain; }
120
+ .log::-webkit-scrollbar { width: 8px; }
121
+ .log::-webkit-scrollbar-thumb { background: var(--dw-border); border-radius: 8px; }
122
+ .welcome { text-align: left; padding: 6px 2px 10px; animation: rise .35s ease both; }
123
+ .welcome h2 { margin: 0 0 6px; font-size: 20px; line-height: 1.3; font-weight: 700; letter-spacing: -.01em; }
124
+ .welcome p { margin: 0; color: var(--dw-muted); font-size: 14px; line-height: 1.55; }
125
+ .suggestions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
126
+ .chip {
127
+ font-size: 13px; line-height: 1.3; padding: 8px 12px; border-radius: 999px;
128
+ border: 1px solid var(--dw-border); background: var(--dw-bg); text-align: left;
129
+ transition: border-color .15s, background .15s, transform .15s;
130
+ }
131
+ .chip:hover { border-color: var(--dw-accent); background: color-mix(in srgb, var(--dw-accent) 6%, var(--dw-bg)); transform: translateY(-1px); }
132
+
133
+ .msg { display: flex; margin: 0 0 16px; animation: rise .25s ease both; }
134
+ .msg.user { justify-content: flex-end; }
135
+ .bubble { max-width: 86%; font-size: 14.5px; line-height: 1.6; overflow-wrap: anywhere; }
136
+ .msg.user .bubble {
137
+ background: var(--dw-accent); color: var(--dw-accent-contrast);
138
+ padding: 9px 14px; border-radius: 18px 18px 6px 18px; white-space: pre-wrap;
139
+ }
140
+ .msg.assistant .bubble { max-width: 100%; width: 100%; }
141
+ .msg.error .bubble {
142
+ color: #c2334f; background: color-mix(in srgb, #ff5a7a 9%, var(--dw-bg));
143
+ border: 1px solid color-mix(in srgb, #ff5a7a 30%, transparent); padding: 9px 12px; border-radius: 12px; font-size: 13.5px;
144
+ }
145
+ @keyframes rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
146
+
147
+ /* markdown */
148
+ .md > :first-child { margin-top: 0; }
149
+ .md > :last-child { margin-bottom: 0; }
150
+ .md p, .md ul, .md ol, .md blockquote, .md pre, .md .table { margin: 0 0 10px; }
151
+ .md ul, .md ol { padding-left: 22px; }
152
+ .md li { margin: 3px 0; }
153
+ .md h3, .md h4, .md h5, .md h6 { margin: 14px 0 6px; font-size: 15px; line-height: 1.35; }
154
+ .md a { color: var(--dw-accent); text-decoration: underline; text-underline-offset: 2px; }
155
+ .md code { font-family: var(--dw-mono); font-size: .86em; background: var(--dw-surface); padding: 1.5px 5px; border-radius: 5px; }
156
+ .md pre { background: #12132a; color: #e6e6f5; padding: 12px 14px; border-radius: 12px; overflow-x: auto; font-size: 12.5px; line-height: 1.55; }
157
+ .md pre code { background: none; padding: 0; color: inherit; font-size: inherit; }
158
+ .md blockquote { border-left: 3px solid var(--dw-accent); padding: 2px 0 2px 12px; color: var(--dw-muted); }
159
+ .md hr { border: 0; border-top: 1px solid var(--dw-border); margin: 14px 0; }
160
+ .md .table { overflow-x: auto; border: 1px solid var(--dw-border); border-radius: 10px; }
161
+ .md table { border-collapse: collapse; width: 100%; font-size: 13px; }
162
+ .md th, .md td { padding: 7px 10px; border-bottom: 1px solid var(--dw-border); text-align: left; vertical-align: top; overflow-wrap: break-word; min-width: 4.5em; }
163
+ .md th { background: var(--dw-surface); font-weight: 600; }
164
+ .md tr:last-child td { border-bottom: 0; }
165
+ .cursor::after { content: ""; display: inline-block; width: 7px; height: 15px; margin-left: 2px; vertical-align: -2px; background: var(--dw-accent); border-radius: 2px; animation: blink 1s steps(2) infinite; }
166
+ @keyframes blink { 50% { opacity: 0; } }
167
+
168
+ /* reasoning + tools */
169
+ .trace { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
170
+ details.thinking, details.tool {
171
+ border: 1px solid var(--dw-border); border-radius: 12px; background: var(--dw-surface); overflow: hidden;
172
+ }
173
+ details > summary {
174
+ list-style: none; cursor: pointer; display: flex; align-items: center; gap: 8px;
175
+ padding: 7px 10px; font-size: 12.5px; color: var(--dw-muted); user-select: none;
176
+ }
177
+ details > summary::-webkit-details-marker { display: none; }
178
+ details > summary:hover { color: var(--dw-fg); }
179
+ details > summary .chev { margin-left: auto; transition: transform .2s; width: 14px; height: 14px; flex: none; }
180
+ details[open] > summary .chev { transform: rotate(180deg); }
181
+ details > summary svg.ic { width: 14px; height: 14px; flex: none; }
182
+ .thinking-body {
183
+ padding: 2px 12px 10px; font-size: 12.5px; line-height: 1.55; color: var(--dw-muted);
184
+ white-space: pre-wrap; max-height: 220px; overflow-y: auto;
185
+ }
186
+ .tool-name { font-family: var(--dw-mono); font-size: 12px; color: var(--dw-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
187
+ .tool-state { font-size: 11px; padding: 1px 7px; border-radius: 999px; background: var(--dw-bg); border: 1px solid var(--dw-border); flex: none; }
188
+ .tool[data-status="done"] .tool-state { color: #13a06f; border-color: color-mix(in srgb, #13a06f 35%, transparent); }
189
+ .tool[data-status="error"] .tool-state { color: #c2334f; border-color: color-mix(in srgb, #c2334f 35%, transparent); }
190
+ .tool-body { padding: 0 10px 10px; display: grid; gap: 6px; }
191
+ .tool-body .label { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--dw-muted); }
192
+ .tool-body pre {
193
+ margin: 0; font-family: var(--dw-mono); font-size: 11.5px; line-height: 1.5; background: var(--dw-bg);
194
+ border: 1px solid var(--dw-border); border-radius: 8px; padding: 8px; max-height: 180px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere;
195
+ }
196
+ .spin { width: 13px; height: 13px; border-radius: 50%; border: 2px solid color-mix(in srgb, var(--dw-accent) 25%, transparent); border-top-color: var(--dw-accent); animation: spin .7s linear infinite; flex: none; }
197
+ @keyframes spin { to { transform: rotate(360deg); } }
198
+ .typing { display: inline-flex; gap: 4px; padding: 10px 2px; }
199
+ .typing i { width: 7px; height: 7px; border-radius: 50%; background: var(--dw-muted); opacity: .4; animation: bounce 1.2s infinite; }
200
+ .typing i:nth-child(2) { animation-delay: .15s; } .typing i:nth-child(3) { animation-delay: .3s; }
201
+ @keyframes bounce { 0%, 60%, 100% { transform: none; opacity: .35; } 30% { transform: translateY(-4px); opacity: 1; } }
202
+
203
+ /* ---------- composer ---------- */
204
+ .composer { padding: 10px 12px 12px; border-top: 1px solid var(--dw-border); background: var(--dw-bg); }
205
+ .box {
206
+ display: flex; align-items: flex-end; gap: 8px; border: 1px solid var(--dw-border); border-radius: 14px;
207
+ padding: 6px 6px 6px 12px; background: var(--dw-bg); transition: border-color .15s, box-shadow .15s;
208
+ }
209
+ .box:focus-within { border-color: var(--dw-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dw-accent) 16%, transparent); }
210
+ textarea {
211
+ flex: 1; resize: none; border: 0; outline: 0; background: transparent; color: var(--dw-fg);
212
+ font: inherit; font-size: 14.5px; line-height: 1.5; max-height: 140px; padding: 6px 0; min-height: 34px;
213
+ }
214
+ textarea::placeholder { color: var(--dw-muted); }
215
+ .send {
216
+ width: 36px; height: 36px; border-radius: 10px; flex: none; display: grid; place-items: center;
217
+ background: var(--dw-accent); color: var(--dw-accent-contrast); transition: opacity .15s, transform .15s;
218
+ }
219
+ .send:disabled { opacity: .35; cursor: default; }
220
+ .send:not(:disabled):hover { transform: translateY(-1px); }
221
+ .send svg { width: 17px; height: 17px; }
222
+ .foot { display: flex; justify-content: space-between; align-items: center; margin-top: 7px; font-size: 11px; color: var(--dw-muted); }
223
+ .foot a { color: inherit; text-decoration: none; font-weight: 600; }
224
+ .foot a:hover { color: var(--dw-accent); }
225
+ .challenge:empty { display: none; }
226
+ .challenge { display: flex; justify-content: center; padding: 8px 0; }
227
+ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }
228
+ `;var q=["show-reasoning","show-tool-calls","allow-agent-switch","open","persist","hide-branding","launcher-avatar"],$=["endpoint","agent","title","subtitle","greeting","intro","placeholder","suggestions","avatar","position","mode","theme","accent-color","locale","gateway-url","gateway-token","width","height","z-index",...q],O=i=>i.replace(/-([a-z])/g,(r,e)=>e.toUpperCase()),Y=i=>i!==null&&!/^(false|0|off|no)$/i.test(i),V=()=>{let i=crypto.getRandomValues(new Uint8Array(9));return Array.from(i,r=>"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"[r&63]).join("")},G=i=>i.replace(/^mcp_[^_]+(?:_[^_]+)*?__/,"").replace(/_/g," "),M={get(i){try{return localStorage.getItem(i)}catch{return null}},set(i,r){try{localStorage.setItem(i,r)}catch{}}},Z=null;function Q(){return Z??(Z=new Promise((i,r)=>{if(window.turnstile)return i();let e=document.createElement("script");e.src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit",e.async=!0,e.onload=()=>i(),e.onerror=()=>r(new Error("Could not load Turnstile")),document.head.appendChild(e)}))}var X=typeof HTMLElement>"u"?class{}:HTMLElement,E=class extends X{constructor(){super();l(this,"root");l(this,"overrides",{});l(this,"api",null);l(this,"apiKey","");l(this,"server",null);l(this,"agentId","");l(this,"conversationId","");l(this,"messages",[]);l(this,"running",null);l(this,"activated",!1);l(this,"strings",L());l(this,"mql",null);l(this,"els");l(this,"onScheme",()=>this.applyTheme());l(this,"pendingFrame",0);l(this,"following",!0);this.root=this.attachShadow({mode:"open"})}static get observedAttributes(){return $}get config(){let e={endpoint:"/api/dewee"};for(let t of $){let n=this.getAttribute(t);n!==null&&(q.includes(t)?e[O(t)]=Y(n):t==="suggestions"?e.suggestions=n.trim().startsWith("[")?ee(n,[]):n.split("|").map(s=>s.trim()).filter(Boolean):t==="z-index"?e.zIndex=Number(n):e[O(t)]=n)}return{...e,...this.overrides}}configure(e){this.overrides={...this.overrides,...e},this.els&&this.applyConfig()}open(){this.config.mode!=="inline"&&(this.setAttribute("data-open",""),this.removeAttribute("data-unread"),this.els.launcher.setAttribute("aria-expanded","true"),this.activate(),setTimeout(()=>this.els.input.focus(),120),this.emit("dewee:open"))}close(){this.config.mode!=="inline"&&(this.removeAttribute("data-open"),this.els.launcher.setAttribute("aria-expanded","false"),this.emit("dewee:close"))}toggle(){this.hasAttribute("data-open")?this.close():this.open()}async send(e){let t=e.trim();if(!t||this.running)return;this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.open(),await this.activate(),this.els.input.value="",this.autosize(),this.pushMessage({role:"user",content:t});let n={role:"assistant",content:"",status:"streaming",startedAt:Date.now()};this.pushMessage(n),this.setRunning(new AbortController),this.emit("dewee:send",{message:t,agent:this.agentId,conversationId:this.conversationId});try{await this.client().chat({message:t,agent:this.agentId,conversationId:this.conversationId},s=>this.onStreamEvent(n,s),this.running.signal),n.status==="streaming"&&(n.status="done")}catch(s){s.name==="AbortError"?(n.status="done",n.content||(n.content=`_${this.strings.stopped}_`)):(n.status="error",this.showError(n,s))}finally{this.finishThinking(n),this.updateMessage(n),this.setRunning(null),n.status==="done"&&(this.emit("dewee:reply",{content:n.content,agent:this.agentId,conversationId:this.conversationId}),this.config.mode!=="inline"&&!this.hasAttribute("data-open")&&this.setAttribute("data-unread",""))}}stop(){this.running?.abort()}newConversation(){this.stop(),this.conversationId=V(),this.config.persist!==!1&&M.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),this.els.input.focus()}connectedCallback(){this.els||this.renderShell(),this.applyConfig(),(this.config.mode==="inline"||this.config.open)&&(this.config.mode!=="inline"?this.open():this.activate())}disconnectedCallback(){this.stop(),this.mql?.removeEventListener("change",this.onScheme)}attributeChangedCallback(){this.els&&this.applyConfig()}emit(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}client(){let e=this.config,t=[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|");return(!this.api||t!==this.apiKey)&&(this.apiKey=t,this.api=new y({endpoint:e.endpoint,gatewayUrl:e.gatewayUrl,gatewayToken:e.gatewayToken,turnstile:n=>this.challenge(n)})),this.api}convKey(){return`dewee-webchat:conv:${this.config.endpoint}:${this.config.gatewayUrl||""}:${this.agentId}`}applyTheme(){let e=this.config.theme||"light",t=e==="dark"||e==="auto"&&!!this.mql?.matches;this.setAttribute("data-theme",t?"dark":"light")}applyConfig(){let e=this.config;this.strings=L(e.locale);let t=this.strings;this.setAttribute("data-position",e.position||"bottom-right"),this.setAttribute("data-mode",e.mode||"floating"),e.theme==="auto"&&!this.mql&&(this.mql=matchMedia("(prefers-color-scheme: dark)"),this.mql.addEventListener("change",this.onScheme)),this.applyTheme();let n=(o,a)=>a!==void 0&&a!==""?this.style.setProperty(o,String(a)):this.style.removeProperty(o);n("--dw-accent",e.accentColor),n("--dw-width",e.width),n("--dw-height",e.height),n("--dw-z",e.zIndex);let s=this.els;if(s.title.textContent=e.title||t.title,s.avatar.replaceChildren(),e.avatar){let o=document.createElement("img");o.src=e.avatar,o.alt="",s.avatar.appendChild(o)}else s.avatar.textContent=(e.title||t.title).trim().charAt(0).toUpperCase();if(s.launcher.innerHTML=e.avatar&&e.launcherAvatar?"":m.chat,e.avatar&&e.launcherAvatar){let o=document.createElement("img");o.src=e.avatar,o.alt="",s.launcher.appendChild(o)}s.launcher.insertAdjacentHTML("beforeend",'<span class="badge"></span>'),s.launcher.setAttribute("aria-label",t.open),s.close.setAttribute("aria-label",t.close),s.close.title=t.close,s.newChat.setAttribute("aria-label",t.newChat),s.newChat.title=t.newChat,s.input.placeholder=e.placeholder||t.placeholder,s.foot.hidden=!!e.hideBranding,s.foot.querySelector(".by").textContent=t.poweredBy+" ",this.renderSubtitle(),this.updateSendButton(),this.activated&&this.api&&this.apiKey!==[e.endpoint,e.gatewayUrl,e.gatewayToken].join("|")?(this.activated=!1,this.server=null,this.activate()):this.activated&&e.agent&&e.agent!==this.agentId&&this.server?this.switchAgent(e.agent):this.activated&&this.renderLog()}renderShell(){this.root.innerHTML=`
229
+ <style>${N}</style>
230
+ <button class="launcher" part="launcher" aria-expanded="false" aria-haspopup="dialog"></button>
231
+ <section class="panel" part="panel" role="dialog" aria-label="Chat">
232
+ <header class="head" part="header">
233
+ <div class="avatar"></div>
234
+ <div class="who">
235
+ <div class="title"></div>
236
+ <div class="subtitle"><span class="dot"></span><span class="status"></span><select class="agent-select" hidden></select></div>
237
+ </div>
238
+ <button class="icon-btn new">${m.newChat}</button>
239
+ <button class="icon-btn close">${m.close}</button>
240
+ </header>
241
+ <div class="log" part="log" role="log" aria-live="polite"></div>
242
+ <div class="challenge"><slot name="challenge"></slot></div>
243
+ <form class="composer" part="composer">
244
+ <div class="box">
245
+ <textarea rows="1" maxlength="4000"></textarea>
246
+ <button class="send" type="submit"></button>
247
+ </div>
248
+ <div class="foot"><span><span class="by"></span><a href="https://dewee.sh" target="_blank" rel="noopener">dewee</a></span><span class="meta"></span></div>
249
+ </form>
250
+ </section>`;let e=n=>this.root.querySelector(n);this.els={launcher:e(".launcher"),panel:e(".panel"),log:e(".log"),title:e(".title"),subtitle:e(".subtitle"),status:e(".status"),avatar:e(".avatar"),select:e(".agent-select"),input:e("textarea"),send:e(".send"),newChat:e(".new"),close:e(".close"),foot:e(".foot")};let t=this.els;t.launcher.addEventListener("click",()=>this.toggle()),t.close.addEventListener("click",()=>this.close()),t.log.addEventListener("scroll",()=>{this.following=this.nearBottom()},{passive:!0}),t.newChat.addEventListener("click",()=>this.newConversation()),t.select.addEventListener("change",()=>this.switchAgent(t.select.value)),e("form").addEventListener("submit",n=>{n.preventDefault(),this.running?this.stop():this.send(t.input.value)}),t.input.addEventListener("keydown",n=>{n.key==="Enter"&&!n.shiftKey&&!n.isComposing&&n.keyCode!==229&&(n.preventDefault(),this.running||this.send(t.input.value))}),t.input.addEventListener("input",()=>{this.autosize(),this.updateSendButton()}),t.panel.addEventListener("keydown",n=>{n.key==="Escape"&&this.close()})}autosize(){let e=this.els.input;e.style.height="auto",e.style.height=Math.min(e.scrollHeight,140)+"px"}updateSendButton(){let e=this.els.send;e.innerHTML=this.running?m.stop:m.send,e.setAttribute("aria-label",this.running?this.strings.stop:this.strings.send),e.title=e.getAttribute("aria-label"),e.disabled=!this.running&&!this.els.input.value.trim()}setRunning(e){this.running=e,this.updateSendButton()}renderSubtitle(){let e=this.els,t=this.config,n=this.server?.agents??[],s=t.allowAgentSwitch!==!1&&!!this.server?.allowAgentSwitch&&n.length>1;e.select.hidden=!s,e.status.hidden=s,s&&(e.select.replaceChildren(...n.map(a=>new Option(a.name||a.id,a.id,!1,a.id===this.agentId))),e.select.setAttribute("aria-label","Agent"));let o=n.find(a=>a.id===this.agentId);e.status.textContent=t.subtitle||(o?.name&&o.name!==t.title?`${this.strings.online} \xB7 ${o.name}`:this.strings.online)}async activate(){if(!this.activated){this.activated=!0,this.renderLog();try{this.server=await this.client().getConfig(!0),this.els.input.maxLength=this.server.maxMessageLength;let e=this.config.agent,t=n=>!!n&&(this.server.agents.length===0||this.server.agents.some(s=>s.id===n));this.agentId=t(e)?e:this.server.defaultAgent||this.server.agents[0]?.id||e||"",await this.loadConversation()}catch(e){this.activated=!1,this.renderLog(),this.showError(null,e)}this.renderSubtitle(),this.emit("dewee:ready",{agent:this.agentId,config:this.server})}}async switchAgent(e){e!==this.agentId&&(this.stop(),this.agentId=e,this.renderSubtitle(),await this.loadConversation())}async loadConversation(){let e=this.config.persist!==!1,t=e?M.get(this.convKey()):null;if(this.conversationId=t||V(),e&&!t&&M.set(this.convKey(),this.conversationId),this.messages=[],this.renderLog(),!!t)try{let n=await this.client().history(this.agentId,this.conversationId);this.messages=n.map(s=>({...s,status:"done"})),this.renderLog()}catch{}}renderLog(){let e=this.els.log;e.replaceChildren(),this.messages.length||e.appendChild(this.renderWelcome());for(let t of this.messages)e.appendChild(this.renderMessage(t));e.scrollTop=e.scrollHeight}renderWelcome(){let e=this.config,t=this.strings,n=document.createElement("div");n.className="welcome";let s=document.createElement("h2");s.textContent=e.greeting||t.greeting;let o=document.createElement("p");if(o.textContent=e.intro||t.greetingBody,n.append(s,o),e.suggestions?.length){let a=document.createElement("div");a.className="suggestions";for(let g of e.suggestions){let p=document.createElement("button");p.className="chip",p.type="button",p.textContent=g,p.addEventListener("click",()=>this.send(g)),a.appendChild(p)}n.appendChild(a)}return n}pushMessage(e){this.messages.length||this.els.log.replaceChildren(),this.messages.push(e),this.els.log.appendChild(this.renderMessage(e)),this.scrollToBottom(!0)}renderMessage(e){let t=document.createElement("div");t.className=`msg ${e.role}${e.status==="error"?" error":""}`;let n=document.createElement("div");n.className="bubble",t.appendChild(n);let s=document.createElement("div");s.className="trace";let o=document.createElement("div");return o.className=e.role==="assistant"?"md":"",e.role==="assistant"&&n.appendChild(s),n.appendChild(o),e.view={root:t,trace:s,md:o,tools:new Map},this.updateMessage(e),t}updateMessage(e){let t=e.view;if(!t)return;let n=this.config;if(e.role==="user"){t.md.textContent=e.content;return}if(t.root.classList.toggle("error",e.status==="error"),n.showReasoning!==!1&&e.thinking){t.thinking||(t.thinking=document.createElement("details"),t.thinking.className="thinking",t.thinking.innerHTML='<summary></summary><div class="thinking-body"></div>',t.trace.prepend(t.thinking));let o=e.status==="streaming"&&!e.thinkingMs,a=o?this.strings.thinking:e.thinkingMs?this.strings.thoughtFor(Math.max(1,Math.round(e.thinkingMs/1e3))):this.strings.reasoning,g=t.thinking.querySelector("summary");g.innerHTML=`${o?'<span class="spin"></span>':m.brain}<span></span>${m.down.replace("<svg",'<svg class="chev"')}`,g.querySelector("span:not(.spin)").textContent=a,t.thinking.querySelector(".thinking-body").textContent=e.thinking}else t.thinking&&(t.thinking.remove(),t.thinking=void 0);if(n.showToolCalls!==!1)for(let o of e.toolCalls??[])this.renderTool(t,o);else t.tools.forEach(o=>o.remove()),t.tools.clear();t.trace.hidden=!t.trace.childElementCount,e.status==="streaming"&&!e.content?t.md.innerHTML=`<span class="typing" aria-label="${this.strings.thinking}"><i></i><i></i><i></i></span>`:(t.md.replaceChildren(k(document,e.content)),t.md.classList.toggle("cursor",e.status==="streaming"))}renderTool(e,t){let n=e.tools.get(t.id);n||(n=document.createElement("details"),n.className="tool",n.innerHTML=`<summary><span class="lead"></span><span class="tool-name"></span><span class="tool-state"></span>${m.down.replace("<svg",'<svg class="chev"')}</summary><div class="tool-body"></div>`,e.tools.set(t.id,n),e.trace.appendChild(n)),n.dataset.status=t.status,n.querySelector(".lead").innerHTML=t.status==="running"?'<span class="spin"></span>':m.tool;let s=n.querySelector(".tool-name");s.textContent=G(t.name),s.setAttribute("title",t.name),n.querySelector(".tool-state").textContent=t.status==="running"?this.strings.toolRunning:t.status==="error"?this.strings.toolError:this.strings.toolDone;let o=n.querySelector(".tool-body");o.replaceChildren();let a=(g,p)=>{if(p==null||p==="")return;let f=document.createElement("div");f.className="label",f.textContent=g;let h=document.createElement("pre");h.textContent=typeof p=="string"?te(p):JSON.stringify(p,null,2),o.append(f,h)};a(this.strings.args,t.arguments),a(this.strings.result,t.result)}onStreamEvent(e,t){switch(t.type){case"start":t.conversationId&&t.conversationId!==this.conversationId&&(this.conversationId=t.conversationId,this.config.persist!==!1&&M.set(this.convKey(),t.conversationId));break;case"thinking":e.thinking=(e.thinking||"")+t.text;break;case"delta":this.finishThinking(e),e.content+=t.text;break;case"tool_call":this.finishThinking(e),(e.toolCalls??(e.toolCalls=[])).push({id:t.id,name:t.name,arguments:t.arguments,status:"running"});break;case"tool_result":{let n=e.toolCalls?.find(s=>s.id===t.id)??(e.toolCalls??(e.toolCalls=[]))[e.toolCalls.push({id:t.id,name:t.name,status:"running"})-1];n.status=t.isError?"error":"done",n.result=t.result,n.isError=t.isError;break}case"done":this.finishThinking(e),t.content&&t.content.length>=e.content.length&&(e.content=t.content),e.status="done";break;case"error":e.status="error",this.showError(e,new w(t.message,500,t.code));break}this.scheduleUpdate(e)}scheduleUpdate(e){this.pendingFrame||(this.pendingFrame=requestAnimationFrame(()=>{this.pendingFrame=0,this.updateMessage(e),this.following&&this.scrollToBottom(!0)}))}finishThinking(e){e.thinking&&!e.thinkingMs&&e.startedAt&&(e.thinkingMs=Date.now()-e.startedAt)}nearBottom(){let e=this.els.log;return e.scrollHeight-e.scrollTop-e.clientHeight<96}scrollToBottom(e=!1){let t=this.els.log;(e||this.nearBottom())&&(t.scrollTop=t.scrollHeight,this.following=!0)}showError(e,t){let n=t.message||this.strings.errorGeneric;if(this.emit("dewee:error",{message:n,code:t.code}),e){e.content=e.content?`${e.content}
251
+
252
+ > \u26A0\uFE0F ${n}`:`\u26A0\uFE0F ${n}`,e.status="error",this.updateMessage(e);return}let s=document.createElement("div");s.className="msg error",s.innerHTML='<div class="bubble"></div>',s.firstElementChild.textContent=`\u26A0\uFE0F ${n}`,this.els.log.appendChild(s)}async challenge(e){await Q();let t=document.createElement("div");t.slot="challenge",this.appendChild(t);try{return await new Promise((n,s)=>{window.turnstile.render(t,{sitekey:e,appearance:"interaction-only",theme:this.getAttribute("data-theme")==="dark"?"dark":"light",callback:n,"error-callback":()=>s(new w("Verification failed",403,"turnstile_failed"))})})}finally{t.remove()}}};function ee(i,r){try{return JSON.parse(i)}catch{return r}}function te(i){let r=i.trim();if(!/^[[{]/.test(r))return i;try{return JSON.stringify(JSON.parse(r),null,2)}catch{return i}}function H(i="dewee-chat"){typeof window>"u"||!window.customElements||customElements.get(i)||customElements.define(i,class extends E{})}function ne(i={},r){H();let e=document.createElement("dewee-chat");return e.configure(i),((typeof r=="string"?document.querySelector(r):r)??document.body).appendChild(e),e}H();var ie=document.currentScript,S=ie?.dataset;if(S&&S.endpoint&&S.autoMount!=="false"){let i=()=>{let r=document.createElement("dewee-chat");for(let[e,t]of Object.entries(S))e==="autoMount"||t===void 0||r.setAttribute(e.replace(/[A-Z]/g,n=>"-"+n.toLowerCase()),t);document.body.appendChild(r)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",i):i()}window.DeweeWebchat=I;})();
@@ -0,0 +1,161 @@
1
+ import { P as PublicConfig, S as SessionResponse, H as HistoryMessage, C as ChatStreamEvent } from './protocol-CS8_4zC0.js';
2
+ export { A as AgentInfo, T as ToolCallView } from './protocol-CS8_4zC0.js';
3
+
4
+ interface DeweeChatConfig {
5
+ /** Backend base URL (the server handler). Default: "/api/dewee". */
6
+ endpoint: string;
7
+ /** Agent key to talk to. Default: the backend's default agent. */
8
+ agent?: string;
9
+ title?: string;
10
+ subtitle?: string;
11
+ /** Welcome heading and text shown before the first message. */
12
+ greeting?: string;
13
+ intro?: string;
14
+ placeholder?: string;
15
+ /** Quick-reply prompts shown on the welcome screen. */
16
+ suggestions?: string[];
17
+ /** Image URL used for the header avatar (and launcher when `launcherAvatar`). */
18
+ avatar?: string;
19
+ launcherAvatar?: boolean;
20
+ position?: "bottom-right" | "bottom-left";
21
+ /** "floating" (launcher + popup) or "inline" (fills its container). */
22
+ mode?: "floating" | "inline";
23
+ theme?: "light" | "dark" | "auto";
24
+ accentColor?: string;
25
+ locale?: string;
26
+ /** Show the model's reasoning stream. Default true. */
27
+ showReasoning?: boolean;
28
+ /** Show tool calls and their results. Default true. */
29
+ showToolCalls?: boolean;
30
+ /** Show an agent picker when the backend allows switching. Default true. */
31
+ allowAgentSwitch?: boolean;
32
+ /** Start opened. */
33
+ open?: boolean;
34
+ /** Remember the conversation across page loads. Default true. */
35
+ persist?: boolean;
36
+ hideBranding?: boolean;
37
+ /** Bring-your-own gateway credentials (backend must enable allowClientCredentials). */
38
+ gatewayUrl?: string;
39
+ gatewayToken?: string;
40
+ width?: string;
41
+ height?: string;
42
+ zIndex?: number;
43
+ }
44
+ declare const ElementBase: typeof HTMLElement;
45
+ declare class DeweeChatElement extends ElementBase {
46
+ static get observedAttributes(): string[];
47
+ private root;
48
+ private overrides;
49
+ private api;
50
+ private apiKey;
51
+ private server;
52
+ private agentId;
53
+ private conversationId;
54
+ private messages;
55
+ private running;
56
+ private activated;
57
+ private strings;
58
+ private mql;
59
+ private els;
60
+ constructor();
61
+ /** Current effective configuration (attributes merged with `configure()` overrides). */
62
+ get config(): DeweeChatConfig;
63
+ /** Updates configuration at runtime (wins over attributes). */
64
+ configure(partial: Partial<DeweeChatConfig>): void;
65
+ open(): void;
66
+ close(): void;
67
+ toggle(): void;
68
+ /** Sends a message programmatically (opens the widget first). */
69
+ send(text: string): Promise<void>;
70
+ /** Stops the running reply. */
71
+ stop(): void;
72
+ /** Starts a fresh conversation with the current agent. */
73
+ newConversation(): void;
74
+ connectedCallback(): void;
75
+ disconnectedCallback(): void;
76
+ attributeChangedCallback(): void;
77
+ private emit;
78
+ private client;
79
+ private convKey;
80
+ private onScheme;
81
+ private applyTheme;
82
+ private applyConfig;
83
+ private renderShell;
84
+ private autosize;
85
+ private updateSendButton;
86
+ private setRunning;
87
+ private renderSubtitle;
88
+ /** Loads backend config and the persisted conversation once. */
89
+ private activate;
90
+ private switchAgent;
91
+ private loadConversation;
92
+ private renderLog;
93
+ private renderWelcome;
94
+ private pushMessage;
95
+ private renderMessage;
96
+ private updateMessage;
97
+ private renderTool;
98
+ private onStreamEvent;
99
+ private pendingFrame;
100
+ private following;
101
+ private scheduleUpdate;
102
+ private finishThinking;
103
+ private nearBottom;
104
+ private scrollToBottom;
105
+ private showError;
106
+ /** Renders a Turnstile challenge in light DOM (slotted) and resolves with its token. */
107
+ private challenge;
108
+ }
109
+
110
+ interface ApiClientOptions {
111
+ /** Backend base URL, e.g. "/api/dewee" or "https://example.com/api/dewee". */
112
+ endpoint: string;
113
+ /** Bring-your-own gateway credentials (only if the backend allows it). */
114
+ gatewayUrl?: string;
115
+ gatewayToken?: string;
116
+ /** Called when the backend requires a Turnstile challenge; must resolve to a token. */
117
+ turnstile?: (siteKey: string) => Promise<string>;
118
+ }
119
+ declare class ApiError extends Error {
120
+ status: number;
121
+ code?: string | undefined;
122
+ constructor(message: string, status: number, code?: string | undefined);
123
+ }
124
+ declare class ApiClient {
125
+ private o;
126
+ private session;
127
+ private config;
128
+ private sessionPromise;
129
+ constructor(o: ApiClientOptions);
130
+ private url;
131
+ private get storageKey();
132
+ private headers;
133
+ private parse;
134
+ getConfig(force?: boolean): Promise<PublicConfig>;
135
+ /** Returns a valid visitor session, creating or refreshing it when needed. */
136
+ ensureSession(): Promise<SessionResponse>;
137
+ private createSession;
138
+ forgetSession(): void;
139
+ history(agent: string, conversationId: string): Promise<HistoryMessage[]>;
140
+ /** Sends a message and calls `onEvent` for every streamed event. Resolves when the run ends. */
141
+ chat(input: {
142
+ message: string;
143
+ agent: string;
144
+ conversationId: string;
145
+ }, onEvent: (e: ChatStreamEvent) => void, signal?: AbortSignal): Promise<void>;
146
+ }
147
+
148
+ type Doc = Document;
149
+ declare function renderMarkdown(doc: Doc, src: string): DocumentFragment;
150
+
151
+ /** Registers the `<dewee-chat>` custom element (idempotent, SSR-safe). */
152
+ declare function defineDeweeChat(tag?: string): void;
153
+ /** Creates a widget and appends it to `target` (default: document.body). */
154
+ declare function mount(config?: Partial<DeweeChatConfig>, target?: HTMLElement | string): DeweeChatElement;
155
+ declare global {
156
+ interface HTMLElementTagNameMap {
157
+ "dewee-chat": DeweeChatElement;
158
+ }
159
+ }
160
+
161
+ export { ApiClient, type ApiClientOptions, ApiError, ChatStreamEvent, type DeweeChatConfig, DeweeChatElement, HistoryMessage, PublicConfig, SessionResponse, defineDeweeChat, mount, renderMarkdown };
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ import {
2
+ ApiClient,
3
+ ApiError,
4
+ DeweeChatElement,
5
+ defineDeweeChat,
6
+ mount,
7
+ renderMarkdown
8
+ } from "./chunk-RGAYYTL7.js";
9
+ import "./chunk-MKTHKYLY.js";
10
+ export {
11
+ ApiClient,
12
+ ApiError,
13
+ DeweeChatElement,
14
+ defineDeweeChat,
15
+ mount,
16
+ renderMarkdown
17
+ };
@@ -0,0 +1,71 @@
1
+ /** Server-Sent Events emitted by `POST {basePath}/chat`. */
2
+ type ChatStreamEvent = {
3
+ type: "start";
4
+ runId?: string;
5
+ conversationId: string;
6
+ agent: string;
7
+ } | {
8
+ type: "thinking";
9
+ text: string;
10
+ } | {
11
+ type: "delta";
12
+ text: string;
13
+ } | {
14
+ type: "tool_call";
15
+ id: string;
16
+ name: string;
17
+ arguments?: unknown;
18
+ } | {
19
+ type: "tool_result";
20
+ id: string;
21
+ name: string;
22
+ isError: boolean;
23
+ result?: string;
24
+ } | {
25
+ type: "done";
26
+ content: string;
27
+ usage?: Record<string, number>;
28
+ } | {
29
+ type: "error";
30
+ message: string;
31
+ code?: string;
32
+ };
33
+ interface AgentInfo {
34
+ id: string;
35
+ name?: string;
36
+ description?: string;
37
+ }
38
+ /** Response of `GET {basePath}/config` — safe to expose publicly. */
39
+ interface PublicConfig {
40
+ agents: AgentInfo[];
41
+ defaultAgent: string;
42
+ allowAgentSwitch: boolean;
43
+ allowClientCredentials: boolean;
44
+ turnstileSiteKey?: string;
45
+ maxMessageLength: number;
46
+ version: string;
47
+ }
48
+ /** Response of `POST {basePath}/session`. */
49
+ interface SessionResponse {
50
+ visitorId: string;
51
+ token: string;
52
+ expiresAt: number;
53
+ }
54
+ interface ToolCallView {
55
+ id: string;
56
+ name: string;
57
+ arguments?: unknown;
58
+ result?: string;
59
+ isError?: boolean;
60
+ status: "running" | "done" | "error";
61
+ }
62
+ /** A transcript message as returned by `GET {basePath}/history`. */
63
+ interface HistoryMessage {
64
+ role: "user" | "assistant";
65
+ content: string;
66
+ thinking?: string;
67
+ toolCalls?: ToolCallView[];
68
+ createdAt?: string;
69
+ }
70
+
71
+ export type { AgentInfo as A, ChatStreamEvent as C, HistoryMessage as H, PublicConfig as P, SessionResponse as S, ToolCallView as T };
@@ -0,0 +1,26 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+ import { DeweeChatConfig, DeweeChatElement } from './index.js';
4
+ import './protocol-CS8_4zC0.js';
5
+
6
+ interface DeweeChatProps extends Partial<DeweeChatConfig> {
7
+ className?: string;
8
+ style?: CSSProperties;
9
+ onReady?: (e: CustomEvent) => void;
10
+ onSend?: (e: CustomEvent) => void;
11
+ onReply?: (e: CustomEvent) => void;
12
+ onError?: (e: CustomEvent) => void;
13
+ onOpen?: (e: CustomEvent) => void;
14
+ onClose?: (e: CustomEvent) => void;
15
+ }
16
+ declare const DeweeChat: react.ForwardRefExoticComponent<DeweeChatProps & react.RefAttributes<DeweeChatElement | null>>;
17
+
18
+ declare module "react" {
19
+ namespace JSX {
20
+ interface IntrinsicElements {
21
+ "dewee-chat": Record<string, unknown>;
22
+ }
23
+ }
24
+ }
25
+
26
+ export { DeweeChat, DeweeChatConfig, DeweeChatElement, type DeweeChatProps, DeweeChat as default };
package/dist/react.js ADDED
@@ -0,0 +1,44 @@
1
+ import {
2
+ defineDeweeChat
3
+ } from "./chunk-RGAYYTL7.js";
4
+ import "./chunk-MKTHKYLY.js";
5
+
6
+ // src/react.ts
7
+ import { createElement, forwardRef, useEffect, useImperativeHandle, useRef } from "react";
8
+ var EVENTS = {
9
+ onReady: "dewee:ready",
10
+ onSend: "dewee:send",
11
+ onReply: "dewee:reply",
12
+ onError: "dewee:error",
13
+ onOpen: "dewee:open",
14
+ onClose: "dewee:close"
15
+ };
16
+ var DeweeChat = forwardRef(function DeweeChat2(props, ref) {
17
+ const el = useRef(null);
18
+ const { className, style, onReady, onSend, onReply, onError, onOpen, onClose, ...config } = props;
19
+ const handlers = { onReady, onSend, onReply, onError, onOpen, onClose };
20
+ const handlersRef = useRef(handlers);
21
+ handlersRef.current = handlers;
22
+ useImperativeHandle(ref, () => el.current, []);
23
+ const configKey = JSON.stringify(config);
24
+ useEffect(() => {
25
+ defineDeweeChat();
26
+ el.current?.configure(JSON.parse(configKey));
27
+ }, [configKey]);
28
+ useEffect(() => {
29
+ const node = el.current;
30
+ if (!node) return;
31
+ const offs = Object.keys(EVENTS).map((k) => {
32
+ const fn = (e) => handlersRef.current[k]?.(e);
33
+ node.addEventListener(EVENTS[k], fn);
34
+ return () => node.removeEventListener(EVENTS[k], fn);
35
+ });
36
+ return () => offs.forEach((off) => off());
37
+ }, []);
38
+ return createElement("dewee-chat", { ref: el, class: className, style, endpoint: config.endpoint, mode: config.mode });
39
+ });
40
+ var react_default = DeweeChat;
41
+ export {
42
+ DeweeChat,
43
+ react_default as default
44
+ };