@jidou-ai/chat-widget 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/widget.js +237 -14
- package/package.json +1 -1
- package/src/components/ChatWindow.ts +4 -1
- package/src/components/InputArea.ts +3 -0
- package/src/components/MessageList.ts +229 -1
- package/src/core/Widget.ts +10 -13
- package/src/services/ChatHistoryManager.ts +34 -19
- package/src/services/WebSocketClient.ts +10 -2
- package/src/styles/chatWindow.ts +16 -7
- package/src/styles/launcher.ts +9 -0
- package/src/styles/messages.ts +205 -0
- package/src/types/index.ts +2 -0
package/dist/widget.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var JidouChatWidget=(()=>{var U=Object.defineProperty;var Me=Object.getOwnPropertyDescriptor;var ke=Object.getOwnPropertyNames;var _e=Object.prototype.hasOwnProperty;var Le=(i,t)=>{for(var e in t)U(i,e,{get:t[e],enumerable:!0})},Ie=(i,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of ke(t))!_e.call(i,s)&&s!==e&&U(i,s,{get:()=>t[s],enumerable:!(n=Me(t,s))||n.enumerable});return i};var Ae=i=>Ie(U({},"__esModule",{value:!0}),i);var qe={};Le(qe,{Widget:()=>A});var E=class{constructor(){this.listeners=new Map}on(t,e){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e)}off(t,e){let n=this.listeners.get(t);n&&n.delete(e)}emit(t,e){let n=this.listeners.get(t);if(n)for(let s of n)try{s(e)}catch(o){console.error(`Error in event listener for ${String(t)}:`,o)}}once(t,e){let n=s=>{this.off(t,n),e(s)};this.on(t,n)}removeAllListeners(t){t?this.listeners.delete(t):this.listeners.clear()}};var ye={isOpen:!1,displayMode:"floating",connectionState:"disconnected",messages:[],isTyping:!1,unreadCount:0,sessionId:null},O=class extends E{constructor(t){super(),this.state={...ye,...t}}getState(){return{...this.state}}get(t){return this.state[t]}set(t,e){let n=this.state[t];if(n!==e)switch(this.state[t]=e,this.emit("change",{key:t,value:e,prev:n}),t){case"isOpen":this.emit("openChange",e);break;case"displayMode":this.emit("displayModeChange",e);break;case"connectionState":this.emit("connectionStateChange",e);break;case"unreadCount":this.emit("unreadCountChange",e);break;case"messages":this.emit("messagesChange",e);break;case"isTyping":this.emit("isTypingChange",e);break}}addMessage(t){let e=[...this.state.messages,t];this.set("messages",e),!this.state.isOpen&&t.role==="assistant"&&this.set("unreadCount",this.state.unreadCount+1)}updateMessage(t,e){let n=this.state.messages.map(s=>s.id===t?{...s,...e}:s);this.set("messages",n)}removeMessage(t){let e=this.state.messages.filter(n=>n.id!==t);this.set("messages",e)}setMessages(t){this.set("messages",t)}clearMessages(){this.set("messages",[])}open(){this.set("isOpen",!0),this.set("unreadCount",0)}close(){this.set("isOpen",!1)}toggle(){this.state.isOpen?this.close():this.open()}setDisplayMode(t){this.set("displayMode",t)}setConnectionState(t){this.set("connectionState",t)}setTyping(t){this.set("isTyping",t)}setSessionId(t){this.set("sessionId",t)}reset(){this.state={...ye}}};function He(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{let t=Math.random()*16|0;return(i==="x"?t:t&3|8).toString(16)})}function Ne(){return Math.random().toString(36).substring(2,10)}function V(){return`sess_${He()}`}function xe(){return`msg_${Ne()}`}var q="jidou_";function T(i){try{let t=localStorage.getItem(q+i);return t===null?null:JSON.parse(t)}catch{return null}}function L(i,t){try{return localStorage.setItem(q+i,JSON.stringify(t)),!0}catch{return!1}}function J(i){try{localStorage.removeItem(q+i)}catch{}}var G="session_id",Re=3e4,We=1e4,Oe=5,Q=[1e3,2e3,4e3,8e3,16e3],B=class extends E{constructor(e,n){super();this.ws=null;this.connectionState="disconnected";this.reconnectAttempts=0;this.reconnectTimeoutId=null;this.heartbeatIntervalId=null;this.heartbeatTimeoutId=null;this.isManualClose=!1;this.clientId=e,this.wsUrl=n;let s=T(G);s?this.sessionId=s:(this.sessionId=V(),L(G,this.sessionId))}getSessionId(){return this.sessionId}getConnectionState(){return this.connectionState}connect(){if(this.ws&&this.ws.readyState===WebSocket.OPEN)return;this.isManualClose=!1,this.setConnectionState("connecting");let e=`${this.wsUrl}?clientId=${encodeURIComponent(this.clientId)}&sessionId=${encodeURIComponent(this.sessionId)}`;try{this.ws=new WebSocket(e),this.setupEventHandlers()}catch(n){this.handleError(n instanceof Error?n:new Error(String(n)))}}disconnect(){this.isManualClose=!0,this.cleanup(),this.ws&&(this.ws.close(1e3,"Manual disconnect"),this.ws=null),this.setConnectionState("disconnected"),this.emit("disconnected",void 0)}send(e,n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN){console.warn("WebSocket is not connected");return}let s={type:e,payload:n,timestamp:Date.now()};try{this.ws.send(JSON.stringify(s))}catch(o){this.handleError(o instanceof Error?o:new Error(String(o)))}}sendMessage(e){this.send("message:send",{content:e})}sendTypingStart(){this.send("typing:start")}sendTypingStop(){this.send("typing:stop")}setupEventHandlers(){this.ws&&(this.ws.onopen=()=>{this.reconnectAttempts=0,this.startHeartbeat(),this.send("session:init",{sessionId:this.sessionId,clientId:this.clientId})},this.ws.onmessage=e=>{this.handleMessage(e.data)},this.ws.onclose=e=>{this.stopHeartbeat(),this.isManualClose||(this.setConnectionState("disconnected"),this.attemptReconnect())},this.ws.onerror=()=>{})}handleMessage(e){try{let n=JSON.parse(e);switch(n.type){case"connection:ack":{let s=n.payload;this.setConnectionState("connected"),this.emit("connected",{sessionId:s.sessionId,isResumed:s.isResumed});break}case"session:expired":this.sessionId=V(),L(G,this.sessionId),this.emit("sessionExpired",void 0);break;case"message:receive":{let s=n.payload;this.emit("message",s);break}case"bot:typing":this.emit("typing",!0);break;case"pong":this.handlePong();break;case"error":{let s=n.payload;this.handleError(new Error(s.message));break}}}catch(n){console.error("Failed to parse WebSocket message:",n)}}setConnectionState(e){this.connectionState!==e&&(this.connectionState=e,this.emit("connectionStateChange",e))}attemptReconnect(){if(this.isManualClose)return;if(this.reconnectAttempts>=Oe){this.setConnectionState("failed"),this.emit("error",new Error("Max reconnection attempts reached"));return}let e=Q[this.reconnectAttempts]||Q[Q.length-1];this.reconnectAttempts++,this.setConnectionState("reconnecting"),this.emit("reconnect",this.reconnectAttempts),this.reconnectTimeoutId=setTimeout(()=>{this.connect()},e)}startHeartbeat(){this.stopHeartbeat(),this.heartbeatIntervalId=setInterval(()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&(this.send("ping"),this.startHeartbeatTimeout())},Re)}stopHeartbeat(){this.heartbeatIntervalId&&(clearInterval(this.heartbeatIntervalId),this.heartbeatIntervalId=null),this.clearHeartbeatTimeout()}startHeartbeatTimeout(){this.clearHeartbeatTimeout(),this.heartbeatTimeoutId=setTimeout(()=>{console.warn("Heartbeat timeout, reconnecting..."),this.ws&&this.ws.close()},We)}clearHeartbeatTimeout(){this.heartbeatTimeoutId&&(clearTimeout(this.heartbeatTimeoutId),this.heartbeatTimeoutId=null)}handlePong(){this.clearHeartbeatTimeout()}handleError(e){console.error("WebSocket error:",e),this.emit("error",e)}cleanup(){this.stopHeartbeat(),this.reconnectTimeoutId&&(clearTimeout(this.reconnectTimeoutId),this.reconnectTimeoutId=null),this.reconnectAttempts=0}destroy(){this.disconnect(),this.removeAllListeners()}};var M="chat_history",ve=100,je=7*24*60*60*1e3,D=class{constructor(){this.sessionId=null}setSessionId(t){this.sessionId=t}getSessionId(){return this.sessionId}getValidHistory(t){let e=T(M);return e?Date.now()-e.savedAt>je?(this.clear(),null):t&&e.sessionId!==t?null:e:null}save(t){if(!this.sessionId)return!1;let e={sessionId:this.sessionId,messages:t.slice(-ve),savedAt:Date.now()};return L(M,e)}load(){let t=this.getValidHistory(this.sessionId||void 0);return(t==null?void 0:t.messages)||null}loadForSession(t){let e=this.getValidHistory(t);return(e==null?void 0:e.messages)||null}appendMessage(t){if(!this.sessionId)return!1;let e=this.getValidHistory(this.sessionId),n=e?[...e.messages,t].slice(-ve):[t];return this.save(n)}updateMessage(t,e){let n=this.getValidHistory(this.sessionId||void 0);if(!n)return!1;let s=n.messages.map(o=>o.id===t?{...o,...e}:o);return this.save(s)}clear(){J(M)}hasHistory(){return!!this.getValidHistory(this.sessionId||void 0)}getMessageCount(){let t=T(M);return(t==null?void 0:t.messages.length)||0}static cleanup(){let t=T(M);t&&Date.now()-t.savedAt>je&&J(M)}};var u=class{constructor(t){this.options=t,this.element=this.render()}getElement(){return this.element}mount(t){t.appendChild(this.element)}unmount(){this.element.parentNode&&this.element.parentNode.removeChild(this.element)}update(t){this.options={...this.options,...t},this.refresh()}refresh(){let t=this.render();this.element.parentNode&&this.element.parentNode.replaceChild(t,this.element),this.element=t}destroy(){this.unmount()}};var we="http://www.w3.org/2000/svg";function y(...i){let t=document.createElementNS(we,"svg");return t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.setAttribute("stroke","currentColor"),t.setAttribute("stroke-width","2"),t.setAttribute("stroke-linecap","round"),t.setAttribute("stroke-linejoin","round"),i.forEach(e=>t.appendChild(e)),t}function k(i,t){let e=document.createElementNS(we,i);return Object.entries(t).forEach(([n,s])=>e.setAttribute(n,s)),e}var I=i=>k("path",{d:i}),x=(i,t,e,n)=>k("line",{x1:i,y1:t,x2:e,y2:n}),X=(i,t,e)=>k("circle",{cx:i,cy:t,r:e}),Be=(i,t,e,n,s="")=>k("rect",{x:i,y:t,width:e,height:n,...s&&{rx:s}}),C=i=>k("polyline",{points:i}),De=i=>k("polygon",{points:i}),ze={chat:()=>y(I("M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z")),close:()=>y(x("18","6","6","18"),x("6","6","18","18")),send:()=>y(x("22","2","11","13"),De("22 2 15 22 11 13 2 9 22 2")),minimize:()=>y(x("5","12","19","12")),expand:()=>y(C("15 3 21 3 21 9"),C("9 21 3 21 3 15"),x("21","3","14","10"),x("3","21","10","14")),collapse:()=>y(C("4 14 10 14 10 20"),C("20 10 14 10 14 4"),x("14","10","21","3"),x("3","21","10","14")),bot:()=>y(Be("3","11","18","10","2"),X("12","5","2"),I("M12 7v4"),x("8","16","8","16"),x("16","16","16","16")),check:()=>y(C("20 6 9 17 4 12")),checkDouble:()=>y(I("M18 6L7 17l-5-5"),I("M22 10l-7.5 7.5L13 16")),clock:()=>y(X("12","12","10"),C("12 6 12 12 16 14")),alert:()=>y(X("12","12","10"),x("12","8","12","12"),x("12","16","12.01","16")),refresh:()=>y(C("23 4 23 10 17 10"),I("M20.49 15a9 9 0 1 1-2.12-9.36L23 10"))};function m(i){return ze[i]()}var Z={en:{"status.connecting":"Connecting...","status.connected":"Connected","status.disconnected":"Disconnected","status.reconnecting":"Reconnecting...","status.failed":"Connection failed","chat.window":"Chat window","chat.closeChat":"Close chat","chat.openChat":"Open chat","chat.collapse":"Minimize","chat.expand":"Fullscreen","chat.title":"AI Assistant","input.placeholder":"Type a message...","input.ariaLabel":"Message input","input.send":"Send message","branding.poweredBy":"Powered by Jidou","message.today":"Today","message.yesterday":"Yesterday"},"zh-TW":{"status.connecting":"\u9023\u7DDA\u4E2D...","status.connected":"\u5DF2\u9023\u7DDA","status.disconnected":"\u5DF2\u65B7\u7DDA","status.reconnecting":"\u91CD\u65B0\u9023\u7DDA\u4E2D...","status.failed":"\u9023\u7DDA\u5931\u6557","chat.window":"\u804A\u5929\u8996\u7A97","chat.closeChat":"\u95DC\u9589\u804A\u5929","chat.openChat":"\u958B\u555F\u804A\u5929","chat.collapse":"\u7E2E\u5C0F","chat.expand":"\u5168\u87A2\u5E55","chat.title":"AI \u52A9\u7406","input.placeholder":"\u8F38\u5165\u8A0A\u606F...","input.ariaLabel":"\u8A0A\u606F\u8F38\u5165","input.send":"\u50B3\u9001\u8A0A\u606F","branding.poweredBy":"\u7531 Jidou \u63D0\u4F9B","message.today":"\u4ECA\u5929","message.yesterday":"\u6628\u5929"},"zh-CN":{"status.connecting":"\u8FDE\u63A5\u4E2D...","status.connected":"\u5DF2\u8FDE\u63A5","status.disconnected":"\u5DF2\u65AD\u5F00","status.reconnecting":"\u91CD\u65B0\u8FDE\u63A5\u4E2D...","status.failed":"\u8FDE\u63A5\u5931\u8D25","chat.window":"\u804A\u5929\u7A97\u53E3","chat.closeChat":"\u5173\u95ED\u804A\u5929","chat.openChat":"\u6253\u5F00\u804A\u5929","chat.collapse":"\u6700\u5C0F\u5316","chat.expand":"\u5168\u5C4F","chat.title":"AI \u52A9\u624B","input.placeholder":"\u8F93\u5165\u6D88\u606F...","input.ariaLabel":"\u6D88\u606F\u8F93\u5165","input.send":"\u53D1\u9001\u6D88\u606F","branding.poweredBy":"\u7531 Jidou \u63D0\u4F9B","message.today":"\u4ECA\u5929","message.yesterday":"\u6628\u5929"},ja:{"status.connecting":"\u63A5\u7D9A\u4E2D...","status.connected":"\u63A5\u7D9A\u6E08\u307F","status.disconnected":"\u5207\u65AD","status.reconnecting":"\u518D\u63A5\u7D9A\u4E2D...","status.failed":"\u63A5\u7D9A\u5931\u6557","chat.window":"\u30C1\u30E3\u30C3\u30C8\u30A6\u30A3\u30F3\u30C9\u30A6","chat.closeChat":"\u30C1\u30E3\u30C3\u30C8\u3092\u9589\u3058\u308B","chat.openChat":"\u30C1\u30E3\u30C3\u30C8\u3092\u958B\u304F","chat.collapse":"\u6700\u5C0F\u5316","chat.expand":"\u30D5\u30EB\u30B9\u30AF\u30EA\u30FC\u30F3","chat.title":"AI\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8","input.placeholder":"\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u5165\u529B...","input.ariaLabel":"\u30E1\u30C3\u30BB\u30FC\u30B8\u5165\u529B","input.send":"\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1","branding.poweredBy":"Powered by Jidou","message.today":"\u4ECA\u65E5","message.yesterday":"\u6628\u65E5"}};var ee="en";function Fe(){if(typeof navigator>"u")return"en";let t=(navigator.language||navigator.userLanguage||"en").toLowerCase();return t.startsWith("zh-tw")||t.startsWith("zh-hant")?"zh-TW":t.startsWith("zh")?"zh-CN":t.startsWith("ja")?"ja":(t.startsWith("en"),"en")}function te(i){i==="auto"?ee=Fe():ee=i}function h(i){var n;let t=(n=Z[ee])==null?void 0:n[i];if(t)return t;let e=Z.en[i];return e||(console.warn(`Missing translation for key: ${i}`),i)}te("auto");var z=class extends u{render(){var b,f,w;let{config:t,isOpen:e,unreadCount:n,onClick:s}=this.options,o=t.position||"bottom-right",a=t.size||"medium",r=t.shape||"circle",c=document.createElement("button");if(c.className=this.getClassName(o,a,r,e),c.setAttribute("aria-label",e?h("chat.closeChat"):h("chat.openChat")),c.setAttribute("type","button"),t.offsetX!==void 0){let d=o.includes("right");c.style[d?"right":"left"]=`${t.offsetX}px`}t.offsetY!==void 0&&(c.style.bottom=`${t.offsetY}px`);let p=document.createElement("span");if(p.className="jd-launcher__icon",((b=t.icon)==null?void 0:b.type)==="custom"&&t.icon.url){let d=document.createElement("img");d.className="jd-launcher__img",d.src=t.icon.url,d.alt="",p.appendChild(d),c.appendChild(p)}else if(((f=t.icon)==null?void 0:f.type)==="text"&&t.icon.text){let d=document.createElement("span");d.className="jd-launcher__text",d.textContent=t.icon.text,c.appendChild(d)}else{let d=e?m("close"):m("chat");p.appendChild(d),c.appendChild(p)}if(n>0&&!e){let d=document.createElement("span");d.className="jd-launcher__badge",d.textContent=n>99?"99+":String(n),d.setAttribute("aria-label",`${n} unread messages`),c.appendChild(d)}if((w=t.tooltip)!=null&&w.show&&t.tooltip.text&&!e){let d=document.createElement("span");d.className="jd-launcher__tooltip",d.textContent=t.tooltip.text,c.appendChild(d)}return c.addEventListener("click",s),c}getClassName(t,e,n,s){let o=["jd-launcher",`jd-launcher--${t}`,`jd-launcher--${e}`,`jd-launcher--${n}`];return s&&o.push("jd-launcher--open"),o.join(" ")}show(){this.element.classList.remove("jd-launcher--hidden")}hide(){this.element.classList.add("jd-launcher--hidden")}setOpen(t){this.update({...this.options,isOpen:t})}setUnreadCount(t){this.update({...this.options,unreadCount:t})}};function ne(i,t=!0){i.scrollTo({top:i.scrollHeight,behavior:t?"smooth":"auto"})}function Ce(i,t=50){return i.scrollHeight-i.scrollTop-i.clientHeight<t}var Pe=["__proto__","constructor","prototype"];function se(i,...t){let e={...i};for(let n of t)if(n)for(let s of Object.keys(n)){if(Pe.includes(s))continue;let o=n[s],a=e[s];o!==void 0&&typeof o=="object"&&o!==null&&!Array.isArray(o)&&typeof a=="object"&&a!==null&&!Array.isArray(a)?e[s]=se(a,o):o!==void 0&&(e[s]=o)}return e}function Se(i){return new Date(i).toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})}function ie(){return Date.now()}var F=class extends u{constructor(){super(...arguments);this.shouldAutoScroll=!0}render(){let{messages:e,isTyping:n}=this.options,s=document.createElement("div");if(s.className="jd-message-list jd-scrollbar",s.addEventListener("scroll",()=>{this.shouldAutoScroll=Ce(s)}),e.length===0&&!n){let o=this.renderEmptyState();return s.appendChild(o),s}for(let o of e){let a=this.renderMessage(o);s.appendChild(a)}if(n){let o=this.renderTypingIndicator();s.appendChild(o)}return requestAnimationFrame(()=>{this.shouldAutoScroll&&ne(s,!1)}),s}renderMessage(e){let n=document.createElement("div");n.className=`jd-message jd-message--${e.role}`,n.setAttribute("data-message-id",e.id);let s=document.createElement("div");s.className="jd-message__bubble",s.textContent=e.content,n.appendChild(s);let o=document.createElement("div");if(o.className="jd-message__time",o.textContent=Se(e.timestamp),n.appendChild(o),e.role==="user"&&e.status){let a=document.createElement("div");a.className=`jd-message__status jd-message__status--${e.status}`;let r=document.createElement("span");e.status==="sending"?r.appendChild(m("clock")):e.status==="sent"?r.appendChild(m("check")):e.status==="error"&&r.appendChild(m("alert")),a.appendChild(r),n.appendChild(a)}return n}renderTypingIndicator(){let e=document.createElement("div");e.className="jd-typing-indicator";for(let n=0;n<3;n++){let s=document.createElement("span");s.className="jd-typing-indicator__dot",e.appendChild(s)}return e}renderEmptyState(){let e=document.createElement("div");e.className="jd-welcome";let n=document.createElement("div");n.className="jd-welcome__icon",n.appendChild(m("bot")),e.appendChild(n);let s=document.createElement("h3");s.className="jd-welcome__title",s.textContent="Hi! How can I help you?",e.appendChild(s);let o=document.createElement("p");return o.className="jd-welcome__subtitle",o.textContent="Ask me anything",e.appendChild(o),e}setMessages(e){this.options.messages=e,this.refresh()}addMessage(e){this.options.messages=[...this.options.messages,e],this.refresh()}updateMessage(e,n){this.options.messages=this.options.messages.map(s=>s.id===e?{...s,...n}:s),this.refresh()}setTyping(e){this.options.isTyping=e,this.refresh()}scrollToBottom(){ne(this.element)}};var P=class extends u{constructor(){super(...arguments);this.isTyping=!1;this.typingTimeout=null}render(){let{placeholder:e,maxLength:n,disabled:s}=this.options,o=document.createElement("div");o.className="jd-input-area";let a=document.createElement("div");return a.className="jd-input-wrapper",this.textarea=document.createElement("textarea"),this.textarea.className="jd-input",this.textarea.placeholder=e||h("input.placeholder"),this.textarea.rows=1,this.textarea.disabled=s||!1,n&&(this.textarea.maxLength=n),this.textarea.setAttribute("aria-label",h("input.ariaLabel")),this.textarea.addEventListener("input",this.handleInput.bind(this)),this.textarea.addEventListener("keydown",this.handleKeyDown.bind(this)),a.appendChild(this.textarea),o.appendChild(a),this.sendButton=document.createElement("button"),this.sendButton.className="jd-send-btn",this.sendButton.type="button",this.sendButton.disabled=!0,this.sendButton.setAttribute("aria-label",h("input.send")),this.sendButton.appendChild(m("send")),this.sendButton.addEventListener("click",this.handleSend.bind(this)),o.appendChild(this.sendButton),o}handleInput(){var n,s,o,a;if(!this.textarea||!this.sendButton)return;let e=this.textarea.value.trim();this.sendButton.disabled=e.length===0,this.textarea.style.height="auto",this.textarea.style.height=`${Math.min(this.textarea.scrollHeight,120)}px`,e.length>0&&!this.isTyping&&(this.isTyping=!0,(s=(n=this.options).onTypingStart)==null||s.call(n)),this.typingTimeout&&clearTimeout(this.typingTimeout),e.length>0?this.typingTimeout=setTimeout(()=>{var r,c;this.isTyping&&(this.isTyping=!1,(c=(r=this.options).onTypingStop)==null||c.call(r))},1e3):this.isTyping&&(this.isTyping=!1,(a=(o=this.options).onTypingStop)==null||a.call(o))}handleKeyDown(e){e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),this.handleSend())}handleSend(){var n,s;if(!this.textarea)return;let e=this.textarea.value.trim();e.length!==0&&(this.isTyping&&(this.isTyping=!1,(s=(n=this.options).onTypingStop)==null||s.call(n)),this.typingTimeout&&(clearTimeout(this.typingTimeout),this.typingTimeout=null),this.textarea.value="",this.textarea.style.height="auto",this.sendButton&&(this.sendButton.disabled=!0),this.options.onSend(e))}focus(){var e;(e=this.textarea)==null||e.focus()}clear(){this.textarea&&(this.textarea.value="",this.textarea.style.height="auto"),this.sendButton&&(this.sendButton.disabled=!0)}setDisabled(e){var n;this.textarea&&(this.textarea.disabled=e),this.sendButton&&(this.sendButton.disabled=e||!((n=this.textarea)!=null&&n.value.trim()))}getValue(){var e;return((e=this.textarea)==null?void 0:e.value.trim())||""}destroy(){this.typingTimeout&&clearTimeout(this.typingTimeout),super.destroy()}};var Ke={connecting:"status.connecting",connected:"status.connected",disconnected:"status.disconnected",reconnecting:"status.reconnecting",failed:"status.failed"},K=class extends u{render(){let{state:t,reconnectAttempt:e}=this.options,n=document.createElement("div");n.className=`jd-connection-status jd-connection-status--${t}`;let s=document.createElement("span");s.className="jd-connection-status__dot",n.appendChild(s);let o=document.createElement("span");o.className="jd-connection-status__text";let a=h(Ke[t]);return t==="reconnecting"&&e&&(a=`${h("status.reconnecting").replace("...","")} (${e}/5)...`),o.textContent=a,n.appendChild(o),t==="connected"&&(n.style.display="none"),n}setState(t,e){this.update({state:t,reconnectAttempt:e})}};var $=class extends u{render(){let{items:t,onSelect:e}=this.options,n=document.createElement("div");n.className="jd-quick-replies";for(let s of t){let o=document.createElement("button");if(o.className="jd-quick-reply",o.type="button",s.icon){let r=document.createElement("span");r.className="jd-quick-reply__icon",r.textContent=s.icon,o.appendChild(r)}let a=document.createElement("span");a.textContent=s.text,o.appendChild(a),o.addEventListener("click",()=>e(s)),n.appendChild(o)}return n}setVisible(t){this.element.style.display=t?"flex":"none"}};var Y=class extends u{constructor(){super(...arguments);this.quickReplies=null}render(){var v,ce,le,he,pe,ue,ge,me,fe;let{config:e,displayMode:n,position:s,connectionState:o,reconnectAttempt:a,messages:r,isTyping:c,quickReplies:p,showBranding:b=!0,onClose:f,onCollapse:w,onExpand:d,onSend:H,onTypingStart:N,onTypingStop:R,onQuickReplySelect:_}=this.options,l=document.createElement("div");if(l.className=this.getClassName(n,s),l.setAttribute("role","dialog"),l.setAttribute("aria-label",h("chat.window")),n==="floating"){let j=e.width||380,S=e.height||600;l.style.width=typeof j=="number"?`${j}px`:j,l.style.height=typeof S=="number"?`${S}px`:S,e.borderRadius&&(l.style.borderRadius=`${e.borderRadius}px`)}if(((v=e.header)==null?void 0:v.show)!==!1){let j=this.renderHeader(e,n,f,w,d);l.appendChild(j)}let g=document.createElement("div");g.className="jd-chat-body";let W=new K({state:o,reconnectAttempt:a});if(this.connectionStatus=W,g.appendChild(W.getElement()),this.messageList=new F({messages:r,isTyping:c}),g.appendChild(this.messageList.getElement()),p&&p.length>0&&_&&(this.quickReplies=new $({items:p,onSelect:j=>{var S;_(j),(S=this.quickReplies)==null||S.setVisible(!1)}}),g.appendChild(this.quickReplies.getElement())),l.appendChild(g),this.inputArea=new P({placeholder:((le=(ce=e.footer)==null?void 0:ce.input)==null?void 0:le.placeholder)||h("input.placeholder"),maxLength:(pe=(he=e.footer)==null?void 0:he.input)==null?void 0:pe.maxLength,disabled:o!=="connected",onSend:H,onTypingStart:N,onTypingStop:R}),l.appendChild(this.inputArea.getElement()),b&&((ge=(ue=e.footer)==null?void 0:ue.branding)==null?void 0:ge.show)!==!1){let j=this.renderBranding((fe=(me=e.footer)==null?void 0:me.branding)==null?void 0:fe.text);l.appendChild(j)}return l}getClassName(e,n){let s=["jd-chat-window",`jd-chat-window--${e}`];return e==="floating"&&s.push(`jd-chat-window--${n}`),s.join(" ")}renderHeader(e,n,s,o,a){var w,d,H,N,R,_;let r=document.createElement("header");if(r.className="jd-chat-header",((d=(w=e.header)==null?void 0:w.logo)==null?void 0:d.show)!==!1){let l=document.createElement("div");if(l.className="jd-chat-header__logo",(N=(H=e.header)==null?void 0:H.logo)!=null&&N.url){let g=document.createElement("img");g.src=e.header.logo.url,g.alt="",e.header.logo.size&&(g.style.width=`${e.header.logo.size}px`,g.style.height=`${e.header.logo.size}px`),l.appendChild(g)}else l.appendChild(m("bot"));r.appendChild(l)}let c=document.createElement("div");c.className="jd-chat-header__content";let p=document.createElement("h2");if(p.className="jd-chat-header__title",p.textContent=((R=e.header)==null?void 0:R.title)||h("chat.title"),c.appendChild(p),(_=e.header)!=null&&_.subtitle){let l=document.createElement("p");l.className="jd-chat-header__subtitle",l.textContent=e.header.subtitle,c.appendChild(l)}r.appendChild(c);let b=document.createElement("div");b.className="jd-chat-header__actions";let f=(l,g,W)=>{let v=document.createElement("button");v.className="jd-chat-header__btn",v.type="button",v.setAttribute("aria-label",g),v.appendChild(m(l)),v.addEventListener("click",W),b.appendChild(v)};return n==="fullscreen"&&o&&f("collapse",h("chat.collapse"),o),n==="floating"&&(a&&f("expand",h("chat.expand"),a),f("close",h("chat.closeChat"),s)),r.appendChild(b),r}renderBranding(e){let n=document.createElement("div");n.className="jd-branding";let s=document.createElement("a");return s.href="https://jidou.ai",s.target="_blank",s.rel="noopener noreferrer",s.textContent=e||h("branding.poweredBy"),n.appendChild(s),n}show(){var e;this.element.classList.remove("jd-chat-window--hidden"),(e=this.inputArea)==null||e.focus()}hide(){this.element.classList.add("jd-chat-window--hidden")}setMessages(e){var n;(n=this.messageList)==null||n.setMessages(e)}addMessage(e){var n;(n=this.messageList)==null||n.addMessage(e)}updateMessage(e,n){var s;(s=this.messageList)==null||s.updateMessage(e,n)}setTyping(e){var n;(n=this.messageList)==null||n.setTyping(e)}setConnectionState(e,n){var s,o;(s=this.connectionStatus)==null||s.setState(e,n),(o=this.inputArea)==null||o.setDisabled(e!=="connected")}focusInput(){var e;(e=this.inputArea)==null||e.focus()}destroy(){var e,n,s,o;(e=this.messageList)==null||e.destroy(),(n=this.inputArea)==null||n.destroy(),(s=this.connectionStatus)==null||s.destroy(),(o=this.quickReplies)==null||o.destroy(),super.destroy()}};var oe=`
|
|
1
|
+
"use strict";var JidouChatWidget=(()=>{var U=Object.defineProperty;var ke=Object.getOwnPropertyDescriptor;var _e=Object.getOwnPropertyNames;var Le=Object.prototype.hasOwnProperty;var Ie=(o,t)=>{for(var e in t)U(o,e,{get:t[e],enumerable:!0})},Ae=(o,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of _e(t))!Le.call(o,s)&&s!==e&&U(o,s,{get:()=>t[s],enumerable:!(n=ke(t,s))||n.enumerable});return o};var Ne=o=>Ae(U({},"__esModule",{value:!0}),o);var Je={};Ie(Je,{Widget:()=>L});var T=class{constructor(){this.listeners=new Map}on(t,e){this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e)}off(t,e){let n=this.listeners.get(t);n&&n.delete(e)}emit(t,e){let n=this.listeners.get(t);if(n)for(let s of n)try{s(e)}catch(i){console.error(`Error in event listener for ${String(t)}:`,i)}}once(t,e){let n=s=>{this.off(t,n),e(s)};this.on(t,n)}removeAllListeners(t){t?this.listeners.delete(t):this.listeners.clear()}};var ye={isOpen:!1,displayMode:"floating",connectionState:"disconnected",messages:[],isTyping:!1,unreadCount:0,sessionId:null},B=class extends T{constructor(t){super(),this.state={...ye,...t}}getState(){return{...this.state}}get(t){return this.state[t]}set(t,e){let n=this.state[t];if(n!==e)switch(this.state[t]=e,this.emit("change",{key:t,value:e,prev:n}),t){case"isOpen":this.emit("openChange",e);break;case"displayMode":this.emit("displayModeChange",e);break;case"connectionState":this.emit("connectionStateChange",e);break;case"unreadCount":this.emit("unreadCountChange",e);break;case"messages":this.emit("messagesChange",e);break;case"isTyping":this.emit("isTypingChange",e);break}}addMessage(t){let e=[...this.state.messages,t];this.set("messages",e),!this.state.isOpen&&t.role==="assistant"&&this.set("unreadCount",this.state.unreadCount+1)}updateMessage(t,e){let n=this.state.messages.map(s=>s.id===t?{...s,...e}:s);this.set("messages",n)}removeMessage(t){let e=this.state.messages.filter(n=>n.id!==t);this.set("messages",e)}setMessages(t){this.set("messages",t)}clearMessages(){this.set("messages",[])}open(){this.set("isOpen",!0),this.set("unreadCount",0)}close(){this.set("isOpen",!1)}toggle(){this.state.isOpen?this.close():this.open()}setDisplayMode(t){this.set("displayMode",t)}setConnectionState(t){this.set("connectionState",t)}setTyping(t){this.set("isTyping",t)}setSessionId(t){this.set("sessionId",t)}reset(){this.state={...ye}}};function He(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,o=>{let t=Math.random()*16|0;return(o==="x"?t:t&3|8).toString(16)})}function Be(){return Math.random().toString(36).substring(2,10)}function V(){return`sess_${He()}`}function xe(){return`msg_${Be()}`}var q="jidou_";function R(o){try{let t=localStorage.getItem(q+o);return t===null?null:JSON.parse(t)}catch{return null}}function M(o,t){try{return localStorage.setItem(q+o,JSON.stringify(t)),!0}catch{return!1}}function be(o){try{localStorage.removeItem(q+o)}catch{}}var W="session_id",Re=3e4,We=1e4,ze=5,J=[1e3,2e3,4e3,8e3,16e3],z=class extends T{constructor(e,n){super();this.ws=null;this.connectionState="disconnected";this.reconnectAttempts=0;this.reconnectTimeoutId=null;this.heartbeatIntervalId=null;this.heartbeatTimeoutId=null;this.isManualClose=!1;this.clientId=e,this.wsUrl=n;let s=R(W);s?this.sessionId=s:(this.sessionId=V(),M(W,this.sessionId))}getSessionId(){return this.sessionId}getConnectionState(){return this.connectionState}connect(){if(this.ws&&this.ws.readyState===WebSocket.OPEN)return;this.isManualClose=!1,this.setConnectionState("connecting");let e=`${this.wsUrl}?clientId=${encodeURIComponent(this.clientId)}&sessionId=${encodeURIComponent(this.sessionId)}`;try{this.ws=new WebSocket(e),this.setupEventHandlers()}catch(n){this.handleError(n instanceof Error?n:new Error(String(n)))}}disconnect(){this.isManualClose=!0,this.cleanup(),this.ws&&(this.ws.close(1e3,"Manual disconnect"),this.ws=null),this.setConnectionState("disconnected"),this.emit("disconnected",void 0)}send(e,n){if(!this.ws||this.ws.readyState!==WebSocket.OPEN){console.warn("WebSocket is not connected");return}let s={type:e,payload:n,timestamp:Date.now()};try{this.ws.send(JSON.stringify(s))}catch(i){this.handleError(i instanceof Error?i:new Error(String(i)))}}sendMessage(e){this.send("message:send",{content:e})}sendTypingStart(){this.send("typing:start")}sendTypingStop(){this.send("typing:stop")}setupEventHandlers(){this.ws&&(this.ws.onopen=()=>{this.reconnectAttempts=0,this.startHeartbeat(),this.send("session:init",{sessionId:this.sessionId,clientId:this.clientId})},this.ws.onmessage=e=>{this.handleMessage(e.data)},this.ws.onclose=e=>{this.stopHeartbeat(),this.isManualClose||(this.setConnectionState("disconnected"),this.attemptReconnect())},this.ws.onerror=()=>{})}handleMessage(e){try{let n=JSON.parse(e);switch(n.type){case"connection:ack":{this.setConnectionState("connected");break}case"session:ready":{let s=n.payload;this.sessionId=s.sessionId,M(W,this.sessionId),this.emit("connected",{sessionId:s.sessionId,isResumed:s.restored});break}case"session:expired":this.sessionId=V(),M(W,this.sessionId),this.emit("sessionExpired",void 0);break;case"message:receive":{let s=n.payload;this.emit("message",s);break}case"bot:typing":this.emit("typing",!0);break;case"pong":this.handlePong();break;case"error":{let s=n.payload;this.handleError(new Error(s.message));break}}}catch(n){console.error("Failed to parse WebSocket message:",n)}}setConnectionState(e){this.connectionState!==e&&(this.connectionState=e,this.emit("connectionStateChange",e))}attemptReconnect(){if(this.isManualClose)return;if(this.reconnectAttempts>=ze){this.setConnectionState("failed"),this.emit("error",new Error("Max reconnection attempts reached"));return}let e=J[this.reconnectAttempts]||J[J.length-1];this.reconnectAttempts++,this.setConnectionState("reconnecting"),this.emit("reconnect",this.reconnectAttempts),this.reconnectTimeoutId=setTimeout(()=>{this.connect()},e)}startHeartbeat(){this.stopHeartbeat(),this.heartbeatIntervalId=setInterval(()=>{this.ws&&this.ws.readyState===WebSocket.OPEN&&(this.send("ping"),this.startHeartbeatTimeout())},Re)}stopHeartbeat(){this.heartbeatIntervalId&&(clearInterval(this.heartbeatIntervalId),this.heartbeatIntervalId=null),this.clearHeartbeatTimeout()}startHeartbeatTimeout(){this.clearHeartbeatTimeout(),this.heartbeatTimeoutId=setTimeout(()=>{console.warn("Heartbeat timeout, reconnecting..."),this.ws&&this.ws.close()},We)}clearHeartbeatTimeout(){this.heartbeatTimeoutId&&(clearTimeout(this.heartbeatTimeoutId),this.heartbeatTimeoutId=null)}handlePong(){this.clearHeartbeatTimeout()}handleError(e){console.error("WebSocket error:",e),this.emit("error",e)}cleanup(){this.stopHeartbeat(),this.reconnectTimeoutId&&(clearTimeout(this.reconnectTimeoutId),this.reconnectTimeoutId=null),this.reconnectAttempts=0}destroy(){this.disconnect(),this.removeAllListeners()}};var G="chat_history_",je=100,we=7*24*60*60*1e3,O=class{constructor(){this.sessionId=null}getStorageKey(t){let e=t||this.sessionId;return e?`${G}${e}`:G}setSessionId(t){this.sessionId=t}getSessionId(){return this.sessionId}getValidHistory(t){let e=this.getStorageKey(t),n=R(e);return n?Date.now()-n.savedAt>we?(this.clear(t),null):n:null}save(t){if(!this.sessionId)return!1;let e={sessionId:this.sessionId,messages:t.slice(-je),savedAt:Date.now()};return M(this.getStorageKey(),e)}load(){let t=this.getValidHistory();return(t==null?void 0:t.messages)||null}loadForSession(t){let e=this.getValidHistory(t);return(e==null?void 0:e.messages)||null}appendMessage(t){if(!this.sessionId)return!1;let e=this.getValidHistory(),n=e?[...e.messages,t].slice(-je):[t];return this.save(n)}updateMessage(t,e){let n=this.getValidHistory();if(!n)return!1;let s=n.messages.map(i=>i.id===t?{...i,...e}:i);return this.save(s)}clear(t){be(this.getStorageKey(t))}hasHistory(){return!!this.getValidHistory()}getMessageCount(){let t=this.getValidHistory();return(t==null?void 0:t.messages.length)||0}static cleanup(){Object.keys(localStorage).filter(e=>e.startsWith(G)).forEach(e=>{try{let n=localStorage.getItem(e);if(n){let s=JSON.parse(n);Date.now()-s.savedAt>we&&localStorage.removeItem(e)}}catch{}})}};var m=class{constructor(t){this.options=t,this.element=this.render()}getElement(){return this.element}mount(t){t.appendChild(this.element)}unmount(){this.element.parentNode&&this.element.parentNode.removeChild(this.element)}update(t){this.options={...this.options,...t},this.refresh()}refresh(){let t=this.render();this.element.parentNode&&this.element.parentNode.replaceChild(t,this.element),this.element=t}destroy(){this.unmount()}};var Ce="http://www.w3.org/2000/svg";function y(...o){let t=document.createElementNS(Ce,"svg");return t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.setAttribute("stroke","currentColor"),t.setAttribute("stroke-width","2"),t.setAttribute("stroke-linecap","round"),t.setAttribute("stroke-linejoin","round"),o.forEach(e=>t.appendChild(e)),t}function k(o,t){let e=document.createElementNS(Ce,o);return Object.entries(t).forEach(([n,s])=>e.setAttribute(n,s)),e}var _=o=>k("path",{d:o}),x=(o,t,e,n)=>k("line",{x1:o,y1:t,x2:e,y2:n}),Q=(o,t,e)=>k("circle",{cx:o,cy:t,r:e}),Oe=(o,t,e,n,s="")=>k("rect",{x:o,y:t,width:e,height:n,...s&&{rx:s}}),S=o=>k("polyline",{points:o}),De=o=>k("polygon",{points:o}),Fe={chat:()=>y(_("M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z")),close:()=>y(x("18","6","6","18"),x("6","6","18","18")),send:()=>y(x("22","2","11","13"),De("22 2 15 22 11 13 2 9 22 2")),minimize:()=>y(x("5","12","19","12")),expand:()=>y(S("15 3 21 3 21 9"),S("9 21 3 21 3 15"),x("21","3","14","10"),x("3","21","10","14")),collapse:()=>y(S("4 14 10 14 10 20"),S("20 10 14 10 14 4"),x("14","10","21","3"),x("3","21","10","14")),bot:()=>y(Oe("3","11","18","10","2"),Q("12","5","2"),_("M12 7v4"),x("8","16","8","16"),x("16","16","16","16")),check:()=>y(S("20 6 9 17 4 12")),checkDouble:()=>y(_("M18 6L7 17l-5-5"),_("M22 10l-7.5 7.5L13 16")),clock:()=>y(Q("12","12","10"),S("12 6 12 12 16 14")),alert:()=>y(Q("12","12","10"),x("12","8","12","12"),x("12","16","12.01","16")),refresh:()=>y(S("23 4 23 10 17 10"),_("M20.49 15a9 9 0 1 1-2.12-9.36L23 10"))};function f(o){return Fe[o]()}var X={en:{"status.connecting":"Connecting...","status.connected":"Connected","status.disconnected":"Disconnected","status.reconnecting":"Reconnecting...","status.failed":"Connection failed","chat.window":"Chat window","chat.closeChat":"Close chat","chat.openChat":"Open chat","chat.collapse":"Minimize","chat.expand":"Fullscreen","chat.title":"AI Assistant","input.placeholder":"Type a message...","input.ariaLabel":"Message input","input.send":"Send message","branding.poweredBy":"Powered by Jidou","message.today":"Today","message.yesterday":"Yesterday"},"zh-TW":{"status.connecting":"\u9023\u7DDA\u4E2D...","status.connected":"\u5DF2\u9023\u7DDA","status.disconnected":"\u5DF2\u65B7\u7DDA","status.reconnecting":"\u91CD\u65B0\u9023\u7DDA\u4E2D...","status.failed":"\u9023\u7DDA\u5931\u6557","chat.window":"\u804A\u5929\u8996\u7A97","chat.closeChat":"\u95DC\u9589\u804A\u5929","chat.openChat":"\u958B\u555F\u804A\u5929","chat.collapse":"\u7E2E\u5C0F","chat.expand":"\u5168\u87A2\u5E55","chat.title":"AI \u52A9\u7406","input.placeholder":"\u8F38\u5165\u8A0A\u606F...","input.ariaLabel":"\u8A0A\u606F\u8F38\u5165","input.send":"\u50B3\u9001\u8A0A\u606F","branding.poweredBy":"\u7531 Jidou \u63D0\u4F9B","message.today":"\u4ECA\u5929","message.yesterday":"\u6628\u5929"},"zh-CN":{"status.connecting":"\u8FDE\u63A5\u4E2D...","status.connected":"\u5DF2\u8FDE\u63A5","status.disconnected":"\u5DF2\u65AD\u5F00","status.reconnecting":"\u91CD\u65B0\u8FDE\u63A5\u4E2D...","status.failed":"\u8FDE\u63A5\u5931\u8D25","chat.window":"\u804A\u5929\u7A97\u53E3","chat.closeChat":"\u5173\u95ED\u804A\u5929","chat.openChat":"\u6253\u5F00\u804A\u5929","chat.collapse":"\u6700\u5C0F\u5316","chat.expand":"\u5168\u5C4F","chat.title":"AI \u52A9\u624B","input.placeholder":"\u8F93\u5165\u6D88\u606F...","input.ariaLabel":"\u6D88\u606F\u8F93\u5165","input.send":"\u53D1\u9001\u6D88\u606F","branding.poweredBy":"\u7531 Jidou \u63D0\u4F9B","message.today":"\u4ECA\u5929","message.yesterday":"\u6628\u5929"},ja:{"status.connecting":"\u63A5\u7D9A\u4E2D...","status.connected":"\u63A5\u7D9A\u6E08\u307F","status.disconnected":"\u5207\u65AD","status.reconnecting":"\u518D\u63A5\u7D9A\u4E2D...","status.failed":"\u63A5\u7D9A\u5931\u6557","chat.window":"\u30C1\u30E3\u30C3\u30C8\u30A6\u30A3\u30F3\u30C9\u30A6","chat.closeChat":"\u30C1\u30E3\u30C3\u30C8\u3092\u9589\u3058\u308B","chat.openChat":"\u30C1\u30E3\u30C3\u30C8\u3092\u958B\u304F","chat.collapse":"\u6700\u5C0F\u5316","chat.expand":"\u30D5\u30EB\u30B9\u30AF\u30EA\u30FC\u30F3","chat.title":"AI\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8","input.placeholder":"\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u5165\u529B...","input.ariaLabel":"\u30E1\u30C3\u30BB\u30FC\u30B8\u5165\u529B","input.send":"\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1","branding.poweredBy":"Powered by Jidou","message.today":"\u4ECA\u65E5","message.yesterday":"\u6628\u65E5"}};var Z="en";function Ke(){if(typeof navigator>"u")return"en";let t=(navigator.language||navigator.userLanguage||"en").toLowerCase();return t.startsWith("zh-tw")||t.startsWith("zh-hant")?"zh-TW":t.startsWith("zh")?"zh-CN":t.startsWith("ja")?"ja":(t.startsWith("en"),"en")}function ee(o){o==="auto"?Z=Ke():Z=o}function h(o){var n;let t=(n=X[Z])==null?void 0:n[o];if(t)return t;let e=X.en[o];return e||(console.warn(`Missing translation for key: ${o}`),o)}ee("auto");var D=class extends m{render(){var b,u,w;let{config:t,isOpen:e,unreadCount:n,onClick:s}=this.options,i=t.position||"bottom-right",a=t.size||"medium",r=t.shape||"circle",d=document.createElement("button");if(d.className=this.getClassName(i,a,r,e),d.setAttribute("aria-label",e?h("chat.closeChat"):h("chat.openChat")),d.setAttribute("type","button"),t.offsetX!==void 0){let c=i.includes("right");d.style[c?"right":"left"]=`${t.offsetX}px`}t.offsetY!==void 0&&(d.style.bottom=`${t.offsetY}px`);let l=document.createElement("span");if(l.className="jd-launcher__icon",((b=t.icon)==null?void 0:b.type)==="custom"&&t.icon.url){let c=document.createElement("img");c.className="jd-launcher__img",c.src=t.icon.url,c.alt="",l.appendChild(c),d.appendChild(l)}else if(((u=t.icon)==null?void 0:u.type)==="text"&&t.icon.text){let c=document.createElement("span");c.className="jd-launcher__text",c.textContent=t.icon.text,d.appendChild(c)}else{let c=e?f("close"):f("chat");l.appendChild(c),d.appendChild(l)}if(n>0&&!e){let c=document.createElement("span");c.className="jd-launcher__badge",c.textContent=n>99?"99+":String(n),c.setAttribute("aria-label",`${n} unread messages`),d.appendChild(c)}if((w=t.tooltip)!=null&&w.show&&t.tooltip.text&&!e){let c=document.createElement("span");c.className="jd-launcher__tooltip",c.textContent=t.tooltip.text,d.appendChild(c)}return d.addEventListener("click",s),d}getClassName(t,e,n,s){let i=["jd-launcher",`jd-launcher--${t}`,`jd-launcher--${e}`,`jd-launcher--${n}`];return s&&i.push("jd-launcher--open"),i.join(" ")}show(){this.element.classList.remove("jd-launcher--hidden")}hide(){this.element.classList.add("jd-launcher--hidden")}setOpen(t){this.update({...this.options,isOpen:t})}setUnreadCount(t){this.update({...this.options,unreadCount:t})}};function te(o,t=!0){o.scrollTo({top:o.scrollHeight,behavior:t?"smooth":"auto"})}function Se(o,t=50){return o.scrollHeight-o.scrollTop-o.clientHeight<t}var Pe=["__proto__","constructor","prototype"];function ne(o,...t){let e={...o};for(let n of t)if(n)for(let s of Object.keys(n)){if(Pe.includes(s))continue;let i=n[s],a=e[s];i!==void 0&&typeof i=="object"&&i!==null&&!Array.isArray(i)&&typeof a=="object"&&a!==null&&!Array.isArray(a)?e[s]=ne(a,i):i!==void 0&&(e[s]=i)}return e}function Ee(o){return new Date(o).toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"})}function se(){return Date.now()}var F=class extends m{constructor(){super(...arguments);this.shouldAutoScroll=!0}render(){let{messages:e,isTyping:n}=this.options,s=document.createElement("div");if(s.className="jd-message-list jd-scrollbar",s.addEventListener("scroll",()=>{this.shouldAutoScroll=Se(s)}),e.length===0&&!n){let i=this.renderEmptyState();return s.appendChild(i),s}for(let i of e){let a=this.renderMessage(i);s.appendChild(a)}if(n){let i=this.renderTypingIndicator();s.appendChild(i)}return requestAnimationFrame(()=>{this.shouldAutoScroll&&te(s,!1)}),s}renderMessage(e){let n=document.createElement("div");n.className=`jd-message jd-message--${e.role}`,n.setAttribute("data-message-id",e.id);let s=document.createElement("div");if(s.className="jd-message__bubble",e.role==="assistant"&&e.structContent){let a=this.renderStructContent(e.structContent);s.appendChild(a)}else s.textContent=e.content;n.appendChild(s);let i=document.createElement("div");if(i.className="jd-message__time",i.textContent=Ee(e.timestamp),n.appendChild(i),e.role==="user"&&e.status){let a=document.createElement("div");a.className=`jd-message__status jd-message__status--${e.status}`;let r=document.createElement("span");e.status==="sending"?r.appendChild(f("clock")):e.status==="sent"?r.appendChild(f("check")):e.status==="error"&&r.appendChild(f("alert")),a.appendChild(r),n.appendChild(a)}return n}renderStructContent(e){let n=document.createElement("div");n.className="jd-struct";let s=(e==null?void 0:e.blocks)||[];for(let i of s){let a=this.renderBlock(i);a&&n.appendChild(a)}return n}renderBlock(e){switch(e.type){case"text":return this.renderTextBlock(e);case"image":return this.renderImageBlock(e);case"button":return this.renderButtonBlock(e);case"actions":return this.renderActionsBlock(e);case"divider":return this.renderDividerBlock();case"header":return this.renderHeaderBlock(e);case"card":return this.renderCardBlock(e);case"carousel":return this.renderCarouselBlock(e);case"container":return this.renderContainerBlock(e);default:return null}}renderTextBlock(e){var s,i,a,r,d;let n=document.createElement("p");return n.className="jd-struct-text",n.textContent=e.text||"",(s=e.style)!=null&&s.size&&n.setAttribute("data-size",e.style.size),(i=e.style)!=null&&i.bold&&(n.style.fontWeight="700"),(a=e.style)!=null&&a.italic&&(n.style.fontStyle="italic"),(r=e.style)!=null&&r.strike&&(n.style.textDecoration="line-through"),(d=e.style)!=null&&d.color&&(n.style.color=e.style.color),e.align&&(n.style.textAlign=e.align),n}renderImageBlock(e){let n=document.createElement("img");return n.className="jd-struct-image",n.src=e.url||"",n.alt=e.alt_text||"",e.title&&(n.title=e.title),n}renderButtonBlock(e){let n=document.createElement("button");n.className="jd-struct-button",n.textContent=e.text||"";let s=e.style||"primary";n.setAttribute("data-style",s);let i=e.action;return i&&n.addEventListener("click",()=>{var a,r,d,l;i.type==="url"&&i.url?window.open(i.url,"_blank","noopener,noreferrer"):i.type==="message"&&i.text?(r=(a=this.options).onAction)==null||r.call(a,i.text):i.type==="postback"&&((l=(d=this.options).onAction)==null||l.call(d,i.display_text||i.data||""))}),n}renderActionsBlock(e){let n=document.createElement("div");n.className="jd-struct-actions";let s=e.layout||"vertical";n.setAttribute("data-layout",s);let i=e.buttons||[];for(let a of i)n.appendChild(this.renderButtonBlock(a));return n}renderDividerBlock(){let e=document.createElement("hr");return e.className="jd-struct-divider",e}renderHeaderBlock(e){let n=document.createElement("div");n.className="jd-struct-header";let s=document.createElement("div");if(s.className="jd-struct-header__title",s.textContent=e.title||"",n.appendChild(s),e.subtitle){let i=document.createElement("div");i.className="jd-struct-header__subtitle",i.textContent=e.subtitle,n.appendChild(i)}return n}renderCardBlock(e){let n=document.createElement("div");if(n.className="jd-struct-card",e.header){let s=document.createElement("div");s.className="jd-struct-card__header",e.header.background_color&&(s.style.backgroundColor=e.header.background_color);let i=document.createElement("div");if(i.className="jd-struct-card__title",i.textContent=e.header.title||"",s.appendChild(i),e.header.subtitle){let a=document.createElement("div");a.className="jd-struct-card__subtitle",a.textContent=e.header.subtitle,s.appendChild(a)}n.appendChild(s)}if(e.hero){let s=this.renderImageBlock(e.hero);s.className="jd-struct-card__hero",n.appendChild(s)}if(e.body){let s=document.createElement("div");s.className="jd-struct-card__body";for(let i of e.body){let a=this.renderBlock(i);a&&s.appendChild(a)}n.appendChild(s)}if(e.footer){let s=document.createElement("div");s.className="jd-struct-card__footer";for(let i of e.footer){let a=this.renderBlock(i);a&&s.appendChild(a)}n.appendChild(s)}if(e.actions&&e.actions.length>0){let s=document.createElement("div");s.className="jd-struct-card__actions";for(let i of e.actions)s.appendChild(this.renderButtonBlock(i));n.appendChild(s)}return n}renderCarouselBlock(e){let n=document.createElement("div");n.className="jd-struct-carousel";let s=e.cards||[];for(let i of s)n.appendChild(this.renderCardBlock(i));return n}renderContainerBlock(e){let n=document.createElement("div");n.className="jd-struct-container",n.setAttribute("data-layout",e.layout||"vertical"),e.style&&(e.style.background_color&&(n.style.backgroundColor=e.style.background_color),e.style.border_color&&(n.style.borderColor=e.style.border_color),e.style.border_radius&&(n.style.borderRadius=e.style.border_radius),e.style.padding&&(n.style.padding=e.style.padding));let s=e.contents||[];for(let i of s){let a=this.renderBlock(i);a&&n.appendChild(a)}return n}renderTypingIndicator(){let e=document.createElement("div");e.className="jd-typing-indicator";for(let n=0;n<3;n++){let s=document.createElement("span");s.className="jd-typing-indicator__dot",e.appendChild(s)}return e}renderEmptyState(){let e=document.createElement("div");e.className="jd-welcome";let n=document.createElement("div");n.className="jd-welcome__icon",n.appendChild(f("bot")),e.appendChild(n);let s=document.createElement("h3");s.className="jd-welcome__title",s.textContent="Hi! How can I help you?",e.appendChild(s);let i=document.createElement("p");return i.className="jd-welcome__subtitle",i.textContent="Ask me anything",e.appendChild(i),e}setMessages(e){this.options.messages=e,this.refresh()}addMessage(e){this.options.messages=[...this.options.messages,e],this.refresh()}updateMessage(e,n){this.options.messages=this.options.messages.map(s=>s.id===e?{...s,...n}:s),this.refresh()}setTyping(e){this.options.isTyping=e,this.refresh()}scrollToBottom(){te(this.element)}};var K=class extends m{constructor(){super(...arguments);this.isTyping=!1;this.typingTimeout=null}render(){let{placeholder:e,maxLength:n,disabled:s}=this.options,i=document.createElement("div");i.className="jd-input-area";let a=document.createElement("div");return a.className="jd-input-wrapper",this.textarea=document.createElement("textarea"),this.textarea.className="jd-input",this.textarea.placeholder=e||h("input.placeholder"),this.textarea.rows=1,this.textarea.disabled=s||!1,n&&(this.textarea.maxLength=n),this.textarea.setAttribute("aria-label",h("input.ariaLabel")),this.textarea.addEventListener("input",this.handleInput.bind(this)),this.textarea.addEventListener("keydown",this.handleKeyDown.bind(this)),a.appendChild(this.textarea),i.appendChild(a),this.sendButton=document.createElement("button"),this.sendButton.className="jd-send-btn",this.sendButton.type="button",this.sendButton.disabled=!0,this.sendButton.setAttribute("aria-label",h("input.send")),this.sendButton.appendChild(f("send")),this.sendButton.addEventListener("click",this.handleSend.bind(this)),i.appendChild(this.sendButton),i}handleInput(){var n,s,i,a;if(!this.textarea||!this.sendButton)return;let e=this.textarea.value.trim();this.sendButton.disabled=e.length===0,this.textarea.style.height="auto",this.textarea.style.height=`${Math.min(this.textarea.scrollHeight,120)}px`,e.length>0&&!this.isTyping&&(this.isTyping=!0,(s=(n=this.options).onTypingStart)==null||s.call(n)),this.typingTimeout&&clearTimeout(this.typingTimeout),e.length>0?this.typingTimeout=setTimeout(()=>{var r,d;this.isTyping&&(this.isTyping=!1,(d=(r=this.options).onTypingStop)==null||d.call(r))},1e3):this.isTyping&&(this.isTyping=!1,(a=(i=this.options).onTypingStop)==null||a.call(i))}handleKeyDown(e){e.isComposing||e.keyCode===229||e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),this.handleSend())}handleSend(){var n,s;if(!this.textarea)return;let e=this.textarea.value.trim();e.length!==0&&(this.isTyping&&(this.isTyping=!1,(s=(n=this.options).onTypingStop)==null||s.call(n)),this.typingTimeout&&(clearTimeout(this.typingTimeout),this.typingTimeout=null),this.textarea.value="",this.textarea.style.height="auto",this.sendButton&&(this.sendButton.disabled=!0),this.options.onSend(e))}focus(){var e;(e=this.textarea)==null||e.focus()}clear(){this.textarea&&(this.textarea.value="",this.textarea.style.height="auto"),this.sendButton&&(this.sendButton.disabled=!0)}setDisabled(e){var n;this.textarea&&(this.textarea.disabled=e),this.sendButton&&(this.sendButton.disabled=e||!((n=this.textarea)!=null&&n.value.trim()))}getValue(){var e;return((e=this.textarea)==null?void 0:e.value.trim())||""}destroy(){this.typingTimeout&&clearTimeout(this.typingTimeout),super.destroy()}};var $e={connecting:"status.connecting",connected:"status.connected",disconnected:"status.disconnected",reconnecting:"status.reconnecting",failed:"status.failed"},P=class extends m{render(){let{state:t,reconnectAttempt:e}=this.options,n=document.createElement("div");n.className=`jd-connection-status jd-connection-status--${t}`;let s=document.createElement("span");s.className="jd-connection-status__dot",n.appendChild(s);let i=document.createElement("span");i.className="jd-connection-status__text";let a=h($e[t]);return t==="reconnecting"&&e&&(a=`${h("status.reconnecting").replace("...","")} (${e}/5)...`),i.textContent=a,n.appendChild(i),t==="connected"&&(n.style.display="none"),n}setState(t,e){this.update({state:t,reconnectAttempt:e})}};var $=class extends m{render(){let{items:t,onSelect:e}=this.options,n=document.createElement("div");n.className="jd-quick-replies";for(let s of t){let i=document.createElement("button");if(i.className="jd-quick-reply",i.type="button",s.icon){let r=document.createElement("span");r.className="jd-quick-reply__icon",r.textContent=s.icon,i.appendChild(r)}let a=document.createElement("span");a.textContent=s.text,i.appendChild(a),i.addEventListener("click",()=>e(s)),n.appendChild(i)}return n}setVisible(t){this.element.style.display=t?"flex":"none"}};var Y=class extends m{constructor(){super(...arguments);this.quickReplies=null}render(){var de,ce,le,pe,he,ue,ge,me,fe;let{config:e,displayMode:n,position:s,connectionState:i,reconnectAttempt:a,messages:r,isTyping:d,quickReplies:l,showBranding:b=!0,onClose:u,onCollapse:w,onExpand:c,onSend:I,onAction:A,onTypingStart:N,onTypingStop:H,onQuickReplySelect:g}=this.options,p=document.createElement("div");if(p.className=this.getClassName(n,s),p.setAttribute("role","dialog"),p.setAttribute("aria-label",h("chat.window")),n==="floating"){let j=e.width||380,E=e.height||600;p.style.width=typeof j=="number"?`${j}px`:j,p.style.height=typeof E=="number"?`${E}px`:E,e.borderRadius&&(p.style.borderRadius=`${e.borderRadius}px`)}if(((de=e.header)==null?void 0:de.show)!==!1){let j=this.renderHeader(e,n,u,w,c);p.appendChild(j)}let C=document.createElement("div");C.className="jd-chat-body";let v=new P({state:i,reconnectAttempt:a});if(this.connectionStatus=v,C.appendChild(v.getElement()),this.messageList=new F({messages:r,isTyping:d,onAction:A}),C.appendChild(this.messageList.getElement()),l&&l.length>0&&g&&(this.quickReplies=new $({items:l,onSelect:j=>{var E;g(j),(E=this.quickReplies)==null||E.setVisible(!1)}}),C.appendChild(this.quickReplies.getElement())),p.appendChild(C),this.inputArea=new K({placeholder:((le=(ce=e.footer)==null?void 0:ce.input)==null?void 0:le.placeholder)||h("input.placeholder"),maxLength:(he=(pe=e.footer)==null?void 0:pe.input)==null?void 0:he.maxLength,disabled:i!=="connected",onSend:I,onTypingStart:N,onTypingStop:H}),p.appendChild(this.inputArea.getElement()),b&&((ge=(ue=e.footer)==null?void 0:ue.branding)==null?void 0:ge.show)!==!1){let j=this.renderBranding((fe=(me=e.footer)==null?void 0:me.branding)==null?void 0:fe.text);p.appendChild(j)}return p}getClassName(e,n){let s=["jd-chat-window",`jd-chat-window--${e}`];return e==="floating"&&s.push(`jd-chat-window--${n}`),s.join(" ")}renderHeader(e,n,s,i,a){var w,c,I,A,N,H;let r=document.createElement("header");if(r.className="jd-chat-header",((c=(w=e.header)==null?void 0:w.logo)==null?void 0:c.show)!==!1){let g=document.createElement("div");if(g.className="jd-chat-header__logo",(A=(I=e.header)==null?void 0:I.logo)!=null&&A.url){let p=document.createElement("img");p.src=e.header.logo.url,p.alt="",e.header.logo.size&&(p.style.width=`${e.header.logo.size}px`,p.style.height=`${e.header.logo.size}px`),g.appendChild(p)}else g.appendChild(f("bot"));r.appendChild(g)}let d=document.createElement("div");d.className="jd-chat-header__content";let l=document.createElement("h2");if(l.className="jd-chat-header__title",l.textContent=((N=e.header)==null?void 0:N.title)||h("chat.title"),d.appendChild(l),(H=e.header)!=null&&H.subtitle){let g=document.createElement("p");g.className="jd-chat-header__subtitle",g.textContent=e.header.subtitle,d.appendChild(g)}r.appendChild(d);let b=document.createElement("div");b.className="jd-chat-header__actions";let u=(g,p,C)=>{let v=document.createElement("button");v.className=`jd-chat-header__btn jd-chat-header__btn--${g}`,v.type="button",v.setAttribute("aria-label",p),v.appendChild(f(g)),v.addEventListener("click",C),b.appendChild(v)};return n==="fullscreen"&&i&&u("collapse",h("chat.collapse"),i),n==="floating"&&(a&&u("expand",h("chat.expand"),a),u("close",h("chat.closeChat"),s)),r.appendChild(b),r}renderBranding(e){let n=document.createElement("div");n.className="jd-branding";let s=document.createElement("a");return s.href="https://jidou.ai",s.target="_blank",s.rel="noopener noreferrer",s.textContent=e||h("branding.poweredBy"),n.appendChild(s),n}show(){var e;this.element.classList.remove("jd-chat-window--hidden"),(e=this.inputArea)==null||e.focus()}hide(){this.element.classList.add("jd-chat-window--hidden")}setMessages(e){var n;(n=this.messageList)==null||n.setMessages(e)}addMessage(e){var n;(n=this.messageList)==null||n.addMessage(e)}updateMessage(e,n){var s;(s=this.messageList)==null||s.updateMessage(e,n)}setTyping(e){var n;(n=this.messageList)==null||n.setTyping(e)}setConnectionState(e,n){var s,i;(s=this.connectionStatus)==null||s.setState(e,n),(i=this.inputArea)==null||i.setDisabled(e!=="connected")}focusInput(){var e;(e=this.inputArea)==null||e.focus()}destroy(){var e,n,s,i;(e=this.messageList)==null||e.destroy(),(n=this.inputArea)==null||n.destroy(),(s=this.connectionStatus)==null||s.destroy(),(i=this.quickReplies)==null||i.destroy(),super.destroy()}};var ie=`
|
|
2
2
|
/* CSS Reset for Shadow DOM */
|
|
3
3
|
*,
|
|
4
4
|
*::before,
|
|
@@ -223,7 +223,7 @@
|
|
|
223
223
|
white-space: nowrap;
|
|
224
224
|
border: 0;
|
|
225
225
|
}
|
|
226
|
-
`;var
|
|
226
|
+
`;var oe=`
|
|
227
227
|
.jd-launcher {
|
|
228
228
|
position: fixed;
|
|
229
229
|
z-index: var(--jd-z-base);
|
|
@@ -404,7 +404,16 @@
|
|
|
404
404
|
visibility: hidden;
|
|
405
405
|
transform: scale(0.8);
|
|
406
406
|
}
|
|
407
|
-
|
|
407
|
+
|
|
408
|
+
/* Mobile: hide launcher when chat is open */
|
|
409
|
+
@media (max-width: 480px) {
|
|
410
|
+
.jd-launcher--open {
|
|
411
|
+
opacity: 0;
|
|
412
|
+
visibility: hidden;
|
|
413
|
+
pointer-events: none;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
`;var ae=`
|
|
408
417
|
/* Container */
|
|
409
418
|
.jd-chat-window {
|
|
410
419
|
position: fixed;
|
|
@@ -465,13 +474,22 @@
|
|
|
465
474
|
/* Mobile responsive */
|
|
466
475
|
@media (max-width: 480px) {
|
|
467
476
|
.jd-chat-window--floating {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
477
|
+
position: fixed !important;
|
|
478
|
+
top: 0 !important;
|
|
479
|
+
left: 0 !important;
|
|
480
|
+
right: 0 !important;
|
|
481
|
+
bottom: 0 !important;
|
|
482
|
+
width: 100vw !important;
|
|
483
|
+
height: 100vh !important;
|
|
484
|
+
max-width: 100vw !important;
|
|
485
|
+
max-height: 100vh !important;
|
|
486
|
+
border-radius: 0 !important;
|
|
487
|
+
z-index: 999999 !important;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* Hide expand button on mobile - already fullscreen */
|
|
491
|
+
.jd-chat-header__btn--expand {
|
|
492
|
+
display: none !important;
|
|
475
493
|
}
|
|
476
494
|
}
|
|
477
495
|
|
|
@@ -638,7 +656,7 @@
|
|
|
638
656
|
.jd-connection-status--failed .jd-connection-status__dot {
|
|
639
657
|
background: #EF4444;
|
|
640
658
|
}
|
|
641
|
-
`;var
|
|
659
|
+
`;var re=`
|
|
642
660
|
/* Message List Container */
|
|
643
661
|
.jd-message-list {
|
|
644
662
|
flex: 1;
|
|
@@ -944,6 +962,211 @@
|
|
|
944
962
|
height: 20px;
|
|
945
963
|
}
|
|
946
964
|
|
|
965
|
+
/* ============================================
|
|
966
|
+
Structured Content (structContent)
|
|
967
|
+
============================================ */
|
|
968
|
+
|
|
969
|
+
/* Container for all struct blocks */
|
|
970
|
+
.jd-struct {
|
|
971
|
+
display: flex;
|
|
972
|
+
flex-direction: column;
|
|
973
|
+
gap: 8px;
|
|
974
|
+
width: 100%;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/* Text block */
|
|
978
|
+
.jd-struct-text {
|
|
979
|
+
margin: 0;
|
|
980
|
+
font-family: var(--jd-font);
|
|
981
|
+
font-size: 14px;
|
|
982
|
+
line-height: 1.5;
|
|
983
|
+
color: inherit;
|
|
984
|
+
word-wrap: break-word;
|
|
985
|
+
}
|
|
986
|
+
.jd-struct-text[data-size="xs"] { font-size: 11px; }
|
|
987
|
+
.jd-struct-text[data-size="sm"] { font-size: 13px; }
|
|
988
|
+
.jd-struct-text[data-size="md"] { font-size: 14px; }
|
|
989
|
+
.jd-struct-text[data-size="lg"] { font-size: 16px; }
|
|
990
|
+
.jd-struct-text[data-size="xl"] { font-size: 18px; }
|
|
991
|
+
.jd-struct-text[data-size="xxl"] { font-size: 22px; }
|
|
992
|
+
|
|
993
|
+
/* Image block */
|
|
994
|
+
.jd-struct-image {
|
|
995
|
+
width: 100%;
|
|
996
|
+
border-radius: 8px;
|
|
997
|
+
display: block;
|
|
998
|
+
object-fit: cover;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/* Button block */
|
|
1002
|
+
.jd-struct-button {
|
|
1003
|
+
display: inline-flex;
|
|
1004
|
+
align-items: center;
|
|
1005
|
+
justify-content: center;
|
|
1006
|
+
padding: 8px 16px;
|
|
1007
|
+
border: none;
|
|
1008
|
+
border-radius: 8px;
|
|
1009
|
+
font-family: var(--jd-font);
|
|
1010
|
+
font-size: 14px;
|
|
1011
|
+
font-weight: 500;
|
|
1012
|
+
cursor: pointer;
|
|
1013
|
+
transition: opacity var(--jd-transition), background var(--jd-transition);
|
|
1014
|
+
width: 100%;
|
|
1015
|
+
text-align: center;
|
|
1016
|
+
}
|
|
1017
|
+
.jd-struct-button:hover { opacity: 0.85; }
|
|
1018
|
+
.jd-struct-button[data-style="primary"] {
|
|
1019
|
+
background: var(--jd-primary);
|
|
1020
|
+
color: #fff;
|
|
1021
|
+
}
|
|
1022
|
+
.jd-struct-button[data-style="secondary"] {
|
|
1023
|
+
background: var(--jd-surface);
|
|
1024
|
+
color: var(--jd-text);
|
|
1025
|
+
border: 1px solid var(--jd-border);
|
|
1026
|
+
}
|
|
1027
|
+
.jd-struct-button[data-style="danger"] {
|
|
1028
|
+
background: #EF4444;
|
|
1029
|
+
color: #fff;
|
|
1030
|
+
}
|
|
1031
|
+
.jd-struct-button[data-style="link"] {
|
|
1032
|
+
background: transparent;
|
|
1033
|
+
color: var(--jd-primary);
|
|
1034
|
+
padding: 4px 0;
|
|
1035
|
+
text-decoration: underline;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/* Actions block (button group) */
|
|
1039
|
+
.jd-struct-actions {
|
|
1040
|
+
display: flex;
|
|
1041
|
+
gap: 8px;
|
|
1042
|
+
}
|
|
1043
|
+
.jd-struct-actions[data-layout="vertical"] {
|
|
1044
|
+
flex-direction: column;
|
|
1045
|
+
}
|
|
1046
|
+
.jd-struct-actions[data-layout="horizontal"] {
|
|
1047
|
+
flex-direction: row;
|
|
1048
|
+
}
|
|
1049
|
+
.jd-struct-actions[data-layout="horizontal"] .jd-struct-button {
|
|
1050
|
+
flex: 1;
|
|
1051
|
+
width: auto;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/* Divider */
|
|
1055
|
+
.jd-struct-divider {
|
|
1056
|
+
border: none;
|
|
1057
|
+
border-top: 1px solid var(--jd-border);
|
|
1058
|
+
margin: 4px 0;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/* Header block */
|
|
1062
|
+
.jd-struct-header {
|
|
1063
|
+
display: flex;
|
|
1064
|
+
flex-direction: column;
|
|
1065
|
+
gap: 2px;
|
|
1066
|
+
}
|
|
1067
|
+
.jd-struct-header__title {
|
|
1068
|
+
font-family: var(--jd-font);
|
|
1069
|
+
font-size: 16px;
|
|
1070
|
+
font-weight: 600;
|
|
1071
|
+
color: inherit;
|
|
1072
|
+
}
|
|
1073
|
+
.jd-struct-header__subtitle {
|
|
1074
|
+
font-family: var(--jd-font);
|
|
1075
|
+
font-size: 13px;
|
|
1076
|
+
color: var(--jd-text-secondary);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
/* Card block */
|
|
1080
|
+
.jd-struct-card {
|
|
1081
|
+
border: 1px solid var(--jd-border);
|
|
1082
|
+
border-radius: 12px;
|
|
1083
|
+
overflow: hidden;
|
|
1084
|
+
background: var(--jd-background);
|
|
1085
|
+
}
|
|
1086
|
+
.jd-struct-card__header {
|
|
1087
|
+
padding: 12px 16px;
|
|
1088
|
+
}
|
|
1089
|
+
.jd-struct-card__title {
|
|
1090
|
+
font-family: var(--jd-font);
|
|
1091
|
+
font-size: 15px;
|
|
1092
|
+
font-weight: 600;
|
|
1093
|
+
color: var(--jd-text);
|
|
1094
|
+
}
|
|
1095
|
+
.jd-struct-card__subtitle {
|
|
1096
|
+
font-family: var(--jd-font);
|
|
1097
|
+
font-size: 13px;
|
|
1098
|
+
color: var(--jd-text-secondary);
|
|
1099
|
+
margin-top: 2px;
|
|
1100
|
+
}
|
|
1101
|
+
.jd-struct-card__hero {
|
|
1102
|
+
width: 100%;
|
|
1103
|
+
border-radius: 0;
|
|
1104
|
+
display: block;
|
|
1105
|
+
object-fit: cover;
|
|
1106
|
+
}
|
|
1107
|
+
.jd-struct-card__body {
|
|
1108
|
+
padding: 12px 16px;
|
|
1109
|
+
display: flex;
|
|
1110
|
+
flex-direction: column;
|
|
1111
|
+
gap: 8px;
|
|
1112
|
+
}
|
|
1113
|
+
.jd-struct-card__footer {
|
|
1114
|
+
padding: 8px 16px 12px;
|
|
1115
|
+
display: flex;
|
|
1116
|
+
flex-direction: column;
|
|
1117
|
+
gap: 6px;
|
|
1118
|
+
border-top: 1px solid var(--jd-border);
|
|
1119
|
+
}
|
|
1120
|
+
.jd-struct-card__actions {
|
|
1121
|
+
padding: 8px 16px 12px;
|
|
1122
|
+
display: flex;
|
|
1123
|
+
flex-direction: column;
|
|
1124
|
+
gap: 6px;
|
|
1125
|
+
border-top: 1px solid var(--jd-border);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/* Carousel block */
|
|
1129
|
+
.jd-struct-carousel {
|
|
1130
|
+
display: flex;
|
|
1131
|
+
gap: 12px;
|
|
1132
|
+
overflow-x: auto;
|
|
1133
|
+
scroll-snap-type: x mandatory;
|
|
1134
|
+
-webkit-overflow-scrolling: touch;
|
|
1135
|
+
scrollbar-width: none;
|
|
1136
|
+
padding-bottom: 4px;
|
|
1137
|
+
}
|
|
1138
|
+
.jd-struct-carousel::-webkit-scrollbar { display: none; }
|
|
1139
|
+
.jd-struct-carousel > .jd-struct-card {
|
|
1140
|
+
min-width: 240px;
|
|
1141
|
+
max-width: 280px;
|
|
1142
|
+
flex-shrink: 0;
|
|
1143
|
+
scroll-snap-align: start;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/* Container block (generic layout) */
|
|
1147
|
+
.jd-struct-container {
|
|
1148
|
+
display: flex;
|
|
1149
|
+
gap: 8px;
|
|
1150
|
+
}
|
|
1151
|
+
.jd-struct-container[data-layout="vertical"] {
|
|
1152
|
+
flex-direction: column;
|
|
1153
|
+
}
|
|
1154
|
+
.jd-struct-container[data-layout="horizontal"] {
|
|
1155
|
+
flex-direction: row;
|
|
1156
|
+
}
|
|
1157
|
+
.jd-struct-container[data-layout="baseline"] {
|
|
1158
|
+
flex-direction: row;
|
|
1159
|
+
align-items: baseline;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/* Wider bubble for struct content */
|
|
1163
|
+
.jd-message--assistant .jd-message__bubble:has(.jd-struct) {
|
|
1164
|
+
padding: 12px;
|
|
1165
|
+
}
|
|
1166
|
+
.jd-message--assistant:has(.jd-struct) {
|
|
1167
|
+
max-width: 95%;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
947
1170
|
/* Empty State */
|
|
948
1171
|
.jd-empty-state {
|
|
949
1172
|
flex: 1;
|
|
@@ -962,8 +1185,8 @@
|
|
|
962
1185
|
margin-bottom: 16px;
|
|
963
1186
|
opacity: 0.5;
|
|
964
1187
|
}
|
|
965
|
-
`;function
|
|
966
|
-
`)}var
|
|
1188
|
+
`;function Te(){return[ie,oe,ae,re].join(`
|
|
1189
|
+
`)}var Ye=/^(#[0-9A-Fa-f]{3,8}|rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)|rgba\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*[\d.]+\s*\)|hsl\(\s*\d{1,3}\s*,\s*[\d.]+%\s*,\s*[\d.]+%\s*\)|hsla\(\s*\d{1,3}\s*,\s*[\d.]+%\s*,\s*[\d.]+%\s*,\s*[\d.]+\s*\)|[a-zA-Z]+)$/;function Ue(o){return typeof o=="string"&&Ye.test(o.trim())}function Ve(o){return typeof o=="number"&&Number.isFinite(o)&&o>=0&&o<=2147483647}var qe={displayMode:"floating",language:"auto",theme:"light",colors:{primary:"#4F46E5"},launcher:{show:!0,position:"bottom-right",size:"medium",shape:"circle"},chatWindow:{width:380,height:600,borderRadius:16,header:{logo:{show:!0}},footer:{branding:{show:!0}}},behavior:{persistence:{enabled:!0,duration:7}},zIndex:9999,wsUrl:"wss://chat.jidou.ai/ws"},L=class{constructor(t){this.shadowRoot=null;this.launcher=null;this.chatWindow=null;this.container=null;this.eventListeners=new Map;this.config=ne(qe,t),ee(this.config.language||"auto"),this.stateManager=new B({displayMode:this.config.displayMode||"floating"}),this.historyManager=new O,this.wsClient=new z(this.config.clientId,this.config.wsUrl||"wss://chat.jidou.ai/ws"),this.setupStateListeners(),this.setupWSListeners()}init(){var t,e;if(this.createContainer(),this.render(),this.triggerHook("onLoad"),this.wsClient.connect(),this.config.displayMode!=="floating"&&this.open(),(e=(t=this.config.behavior)==null?void 0:t.autoOpen)!=null&&e.enabled&&this.config.displayMode==="floating"){let n=this.config.behavior.autoOpen.delay||0;setTimeout(()=>this.open(),n)}this.triggerHook("onReady")}createContainer(){this.container=document.createElement("div"),this.container.id="jidou-chat-widget";let t=Ve(this.config.zIndex)?this.config.zIndex:9999;this.config.displayMode==="embedded"?this.container.style.cssText=`
|
|
967
1190
|
position: relative;
|
|
968
1191
|
width: 100%;
|
|
969
1192
|
height: 100%;
|
|
@@ -977,4 +1200,4 @@
|
|
|
977
1200
|
z-index: ${t};
|
|
978
1201
|
pointer-events: none;
|
|
979
1202
|
overflow: visible;
|
|
980
|
-
`,this.shadowRoot=this.container.attachShadow({mode:"open"});let e=document.createElement("style");if(e.textContent=
|
|
1203
|
+
`,this.shadowRoot=this.container.attachShadow({mode:"open"});let e=document.createElement("style");if(e.textContent=Te(),this.config.colors&&(e.textContent+=this.generateCustomStyles()),this.config.theme==="dark"){let n=document.createElement("div");n.className="theme-dark",this.shadowRoot.appendChild(e),this.shadowRoot.appendChild(n)}else this.shadowRoot.appendChild(e);if(this.config.displayMode==="embedded"&&this.config.container){let n=typeof this.config.container=="string"?document.querySelector(this.config.container):this.config.container;if(n){n.appendChild(this.container);return}}document.body.appendChild(this.container)}generateCustomStyles(){let{colors:t}=this.config;if(!t)return"";let e={primary:t.primary,background:t.background,text:t.text,"bot-bubble":t.botBubble,"user-bubble":t.userBubble},n=Object.entries(e).filter(([,s])=>Ue(s)).map(([s,i])=>`--jd-${s}: ${i}`);return n.length>0?`:host { ${n.join("; ")}; }`:""}render(){var n,s,i,a,r,d,l,b;if(!this.shadowRoot)return;let t=this.stateManager.getState(),e=((n=this.config.launcher)==null?void 0:n.position)||"bottom-right";t.displayMode==="floating"&&((s=this.config.launcher)==null?void 0:s.show)!==!1&&(this.launcher=new D({config:this.config.launcher||{},isOpen:t.isOpen,unreadCount:t.unreadCount,onClick:()=>this.toggle()}),this.launcher.mount(this.shadowRoot)),this.chatWindow=new Y({config:this.config.chatWindow||{},displayMode:t.displayMode,position:e,connectionState:t.connectionState,messages:t.messages,isTyping:t.isTyping,quickReplies:(r=(a=(i=this.config.chatWindow)==null?void 0:i.welcomeScreen)==null?void 0:a.quickReplies)==null?void 0:r.items,showBranding:(b=(l=(d=this.config.chatWindow)==null?void 0:d.footer)==null?void 0:l.branding)==null?void 0:b.show,onClose:()=>this.close(),onCollapse:()=>this.setDisplayMode("floating"),onExpand:()=>this.setDisplayMode("fullscreen"),onSend:u=>this.handleSendMessage(u),onAction:u=>this.handleSendMessage(u),onTypingStart:()=>this.wsClient.sendTypingStart(),onTypingStop:()=>this.wsClient.sendTypingStop(),onQuickReplySelect:u=>this.handleQuickReply(u)}),this.chatWindow.mount(this.shadowRoot),t.displayMode==="floating"&&!t.isOpen&&this.chatWindow.hide()}setupStateListeners(){this.stateManager.on("openChange",t=>{var e,n,s;(e=this.launcher)==null||e.setOpen(t),t?((n=this.chatWindow)==null||n.show(),this.triggerHook("onOpen")):((s=this.chatWindow)==null||s.hide(),this.triggerHook("onClose"))}),this.stateManager.on("unreadCountChange",t=>{var e;(e=this.launcher)==null||e.setUnreadCount(t)}),this.stateManager.on("connectionStateChange",t=>{var e;(e=this.chatWindow)==null||e.setConnectionState(t),this.triggerHook("onConnectionStateChange",t)}),this.stateManager.on("messagesChange",t=>{var e;(e=this.chatWindow)==null||e.setMessages(t),this.historyManager.save(t)}),this.stateManager.on("isTypingChange",t=>{var e;(e=this.chatWindow)==null||e.setTyping(t)})}setupWSListeners(){this.wsClient.on("connectionStateChange",t=>{this.stateManager.setConnectionState(t)}),this.wsClient.on("connected",({sessionId:t,isResumed:e})=>{if(this.stateManager.setSessionId(t),this.historyManager.setSessionId(t),e){let n=this.historyManager.load();n&&n.length>0&&this.stateManager.setMessages(n)}else this.stateManager.clearMessages(),this.triggerHook("onConversationStarted",t)}),this.wsClient.on("reconnect",t=>{var e;(e=this.chatWindow)==null||e.setConnectionState("reconnecting",t),this.triggerHook("onReconnect",t)}),this.wsClient.on("message",t=>{let e={id:t.id,role:"assistant",content:t.content,structContent:t.structContent,timestamp:t.timestamp||se()};this.stateManager.addMessage(e),this.stateManager.setTyping(!1),this.triggerHook("onMessageReceived",t.content)}),this.wsClient.on("typing",t=>{this.stateManager.setTyping(t)}),this.wsClient.on("sessionExpired",()=>{this.historyManager.clear(),this.stateManager.clearMessages()}),this.wsClient.on("error",t=>{this.triggerHook("onError",t)})}handleSendMessage(t){let e={id:xe(),role:"user",content:t,timestamp:se(),status:"sending"};this.stateManager.addMessage(e),this.triggerHook("onMessageSent",t),this.wsClient.sendMessage(t),this.stateManager.updateMessage(e.id,{status:"sent"})}handleQuickReply(t){let e=t.value||t.text;this.handleSendMessage(e)}triggerHook(t,...e){var i;let n=(i=this.config.hooks)==null?void 0:i[t];if(n)try{n(...e)}catch(a){console.error(`Error in ${t} hook:`,a)}let s=this.eventListeners.get(t);if(s)for(let a of s)try{a(...e)}catch(r){console.error(`Error in ${t} listener:`,r)}}open(){this.stateManager.open()}close(){this.stateManager.close()}toggle(){this.stateManager.toggle()}setDisplayMode(t,e){t!==this.stateManager.get("displayMode")&&(this.config.displayMode=t,e!=null&&e.container&&(this.config.container=e.container),this.stateManager.setDisplayMode(t),this.rerenderUI(),this.triggerHook("onDisplayModeChange",t))}rerenderUI(){this.cleanupUI(),this.createContainer(),this.render(),this.config.displayMode!=="floating"&&this.open()}cleanupUI(){var t,e,n;(t=this.launcher)==null||t.destroy(),(e=this.chatWindow)==null||e.destroy(),(n=this.container)==null||n.remove(),this.shadowRoot=null,this.container=null,this.launcher=null,this.chatWindow=null}getDisplayMode(){return this.stateManager.get("displayMode")}sendMessage(t){this.handleSendMessage(t)}on(t,e){this.eventListeners.has(t)||this.eventListeners.set(t,new Set),this.eventListeners.get(t).add(e)}off(t,e){var n;(n=this.eventListeners.get(t))==null||n.delete(e)}isOpen(){return this.stateManager.get("isOpen")}getState(){return this.stateManager.getState()}destroy(){this.cleanupUI(),this.wsClient.destroy(),this.stateManager.removeAllListeners(),this.eventListeners.clear()}};function Me(){if(typeof window>"u")return;let o=window.JidouChatSettings;if(!(o!=null&&o.clientId)){console.warn("[JidouChat] Missing clientId in JidouChatSettings");return}try{let t=new L(o);t.init(),window.JidouChat={open:()=>t.open(),close:()=>t.close(),toggle:()=>t.toggle(),setDisplayMode:(e,n)=>t.setDisplayMode(e,n),getDisplayMode:()=>t.getDisplayMode(),sendMessage:e=>t.sendMessage(e),on:(e,n)=>t.on(e,n),off:(e,n)=>t.off(e,n),isOpen:()=>t.isOpen(),getState:()=>t.getState(),destroy:()=>t.destroy()}}catch(t){console.error("[JidouChat] Failed to initialize widget:",t)}}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",Me):Me());return Ne(Je);})();
|
package/package.json
CHANGED
|
@@ -28,6 +28,7 @@ export interface ChatWindowOptions {
|
|
|
28
28
|
onCollapse?: () => void;
|
|
29
29
|
onExpand?: () => void;
|
|
30
30
|
onSend: (message: string) => void;
|
|
31
|
+
onAction?: (text: string) => void;
|
|
31
32
|
onTypingStart?: () => void;
|
|
32
33
|
onTypingStop?: () => void;
|
|
33
34
|
onQuickReplySelect?: (item: QuickReply) => void;
|
|
@@ -54,6 +55,7 @@ export class ChatWindow extends BaseComponent<ChatWindowOptions> {
|
|
|
54
55
|
onCollapse,
|
|
55
56
|
onExpand,
|
|
56
57
|
onSend,
|
|
58
|
+
onAction,
|
|
57
59
|
onTypingStart,
|
|
58
60
|
onTypingStop,
|
|
59
61
|
onQuickReplySelect,
|
|
@@ -97,6 +99,7 @@ export class ChatWindow extends BaseComponent<ChatWindowOptions> {
|
|
|
97
99
|
this.messageList = new MessageList({
|
|
98
100
|
messages,
|
|
99
101
|
isTyping,
|
|
102
|
+
onAction,
|
|
100
103
|
});
|
|
101
104
|
body.appendChild(this.messageList.getElement());
|
|
102
105
|
|
|
@@ -203,7 +206,7 @@ export class ChatWindow extends BaseComponent<ChatWindowOptions> {
|
|
|
203
206
|
|
|
204
207
|
const addButton = (icon: Parameters<typeof createIcon>[0], label: string, onClick: () => void) => {
|
|
205
208
|
const btn = document.createElement('button');
|
|
206
|
-
btn.className =
|
|
209
|
+
btn.className = `jd-chat-header__btn jd-chat-header__btn--${icon}`;
|
|
207
210
|
btn.type = 'button';
|
|
208
211
|
btn.setAttribute('aria-label', label);
|
|
209
212
|
btn.appendChild(createIcon(icon));
|
|
@@ -94,6 +94,9 @@ export class InputArea extends BaseComponent<InputAreaOptions> {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
private handleKeyDown(e: KeyboardEvent): void {
|
|
97
|
+
// Ignore Enter during IME composition (e.g. Zhuyin, Pinyin, Japanese)
|
|
98
|
+
if (e.isComposing || e.keyCode === 229) return;
|
|
99
|
+
|
|
97
100
|
// Send on Enter (without Shift)
|
|
98
101
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
99
102
|
e.preventDefault();
|
|
@@ -6,6 +6,7 @@ import type { Message } from '../types';
|
|
|
6
6
|
export interface MessageListOptions {
|
|
7
7
|
messages: Message[];
|
|
8
8
|
isTyping: boolean;
|
|
9
|
+
onAction?: (text: string) => void;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export class MessageList extends BaseComponent<MessageListOptions> {
|
|
@@ -57,7 +58,15 @@ export class MessageList extends BaseComponent<MessageListOptions> {
|
|
|
57
58
|
|
|
58
59
|
const bubble = document.createElement('div');
|
|
59
60
|
bubble.className = 'jd-message__bubble';
|
|
60
|
-
|
|
61
|
+
|
|
62
|
+
// Use structured content for assistant messages when available
|
|
63
|
+
if (message.role === 'assistant' && message.structContent) {
|
|
64
|
+
const structEl = this.renderStructContent(message.structContent);
|
|
65
|
+
bubble.appendChild(structEl);
|
|
66
|
+
} else {
|
|
67
|
+
bubble.textContent = message.content;
|
|
68
|
+
}
|
|
69
|
+
|
|
61
70
|
messageEl.appendChild(bubble);
|
|
62
71
|
|
|
63
72
|
// Time
|
|
@@ -87,6 +96,225 @@ export class MessageList extends BaseComponent<MessageListOptions> {
|
|
|
87
96
|
return messageEl;
|
|
88
97
|
}
|
|
89
98
|
|
|
99
|
+
// ============================================
|
|
100
|
+
// Structured Content Renderers
|
|
101
|
+
// ============================================
|
|
102
|
+
|
|
103
|
+
private renderStructContent(unified: any): HTMLElement {
|
|
104
|
+
const container = document.createElement('div');
|
|
105
|
+
container.className = 'jd-struct';
|
|
106
|
+
|
|
107
|
+
const blocks: any[] = unified?.blocks || [];
|
|
108
|
+
for (const block of blocks) {
|
|
109
|
+
const el = this.renderBlock(block);
|
|
110
|
+
if (el) container.appendChild(el);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return container;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private renderBlock(block: any): HTMLElement | null {
|
|
117
|
+
switch (block.type) {
|
|
118
|
+
case 'text': return this.renderTextBlock(block);
|
|
119
|
+
case 'image': return this.renderImageBlock(block);
|
|
120
|
+
case 'button': return this.renderButtonBlock(block);
|
|
121
|
+
case 'actions': return this.renderActionsBlock(block);
|
|
122
|
+
case 'divider': return this.renderDividerBlock();
|
|
123
|
+
case 'header': return this.renderHeaderBlock(block);
|
|
124
|
+
case 'card': return this.renderCardBlock(block);
|
|
125
|
+
case 'carousel': return this.renderCarouselBlock(block);
|
|
126
|
+
case 'container': return this.renderContainerBlock(block);
|
|
127
|
+
default: return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private renderTextBlock(block: any): HTMLElement {
|
|
132
|
+
const el = document.createElement('p');
|
|
133
|
+
el.className = 'jd-struct-text';
|
|
134
|
+
el.textContent = block.text || '';
|
|
135
|
+
|
|
136
|
+
if (block.style?.size) el.setAttribute('data-size', block.style.size);
|
|
137
|
+
if (block.style?.bold) el.style.fontWeight = '700';
|
|
138
|
+
if (block.style?.italic) el.style.fontStyle = 'italic';
|
|
139
|
+
if (block.style?.strike) el.style.textDecoration = 'line-through';
|
|
140
|
+
if (block.style?.color) el.style.color = block.style.color;
|
|
141
|
+
if (block.align) el.style.textAlign = block.align;
|
|
142
|
+
|
|
143
|
+
return el;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private renderImageBlock(block: any): HTMLElement {
|
|
147
|
+
const img = document.createElement('img');
|
|
148
|
+
img.className = 'jd-struct-image';
|
|
149
|
+
img.src = block.url || '';
|
|
150
|
+
img.alt = block.alt_text || '';
|
|
151
|
+
if (block.title) img.title = block.title;
|
|
152
|
+
return img;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private renderButtonBlock(block: any): HTMLElement {
|
|
156
|
+
const btn = document.createElement('button');
|
|
157
|
+
btn.className = 'jd-struct-button';
|
|
158
|
+
btn.textContent = block.text || '';
|
|
159
|
+
|
|
160
|
+
const style = block.style || 'primary';
|
|
161
|
+
btn.setAttribute('data-style', style);
|
|
162
|
+
|
|
163
|
+
const action = block.action;
|
|
164
|
+
if (action) {
|
|
165
|
+
btn.addEventListener('click', () => {
|
|
166
|
+
if (action.type === 'url' && action.url) {
|
|
167
|
+
window.open(action.url, '_blank', 'noopener,noreferrer');
|
|
168
|
+
} else if (action.type === 'message' && action.text) {
|
|
169
|
+
this.options.onAction?.(action.text);
|
|
170
|
+
} else if (action.type === 'postback') {
|
|
171
|
+
this.options.onAction?.(action.display_text || action.data || '');
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return btn;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private renderActionsBlock(block: any): HTMLElement {
|
|
180
|
+
const container = document.createElement('div');
|
|
181
|
+
container.className = 'jd-struct-actions';
|
|
182
|
+
const layout = block.layout || 'vertical';
|
|
183
|
+
container.setAttribute('data-layout', layout);
|
|
184
|
+
|
|
185
|
+
const buttons: any[] = block.buttons || [];
|
|
186
|
+
for (const btn of buttons) {
|
|
187
|
+
container.appendChild(this.renderButtonBlock(btn));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return container;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private renderDividerBlock(): HTMLElement {
|
|
194
|
+
const hr = document.createElement('hr');
|
|
195
|
+
hr.className = 'jd-struct-divider';
|
|
196
|
+
return hr;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private renderHeaderBlock(block: any): HTMLElement {
|
|
200
|
+
const container = document.createElement('div');
|
|
201
|
+
container.className = 'jd-struct-header';
|
|
202
|
+
|
|
203
|
+
const title = document.createElement('div');
|
|
204
|
+
title.className = 'jd-struct-header__title';
|
|
205
|
+
title.textContent = block.title || '';
|
|
206
|
+
container.appendChild(title);
|
|
207
|
+
|
|
208
|
+
if (block.subtitle) {
|
|
209
|
+
const subtitle = document.createElement('div');
|
|
210
|
+
subtitle.className = 'jd-struct-header__subtitle';
|
|
211
|
+
subtitle.textContent = block.subtitle;
|
|
212
|
+
container.appendChild(subtitle);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return container;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private renderCardBlock(block: any): HTMLElement {
|
|
219
|
+
const card = document.createElement('div');
|
|
220
|
+
card.className = 'jd-struct-card';
|
|
221
|
+
|
|
222
|
+
// Header
|
|
223
|
+
if (block.header) {
|
|
224
|
+
const header = document.createElement('div');
|
|
225
|
+
header.className = 'jd-struct-card__header';
|
|
226
|
+
if (block.header.background_color) {
|
|
227
|
+
header.style.backgroundColor = block.header.background_color;
|
|
228
|
+
}
|
|
229
|
+
const title = document.createElement('div');
|
|
230
|
+
title.className = 'jd-struct-card__title';
|
|
231
|
+
title.textContent = block.header.title || '';
|
|
232
|
+
header.appendChild(title);
|
|
233
|
+
|
|
234
|
+
if (block.header.subtitle) {
|
|
235
|
+
const subtitle = document.createElement('div');
|
|
236
|
+
subtitle.className = 'jd-struct-card__subtitle';
|
|
237
|
+
subtitle.textContent = block.header.subtitle;
|
|
238
|
+
header.appendChild(subtitle);
|
|
239
|
+
}
|
|
240
|
+
card.appendChild(header);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Hero image
|
|
244
|
+
if (block.hero) {
|
|
245
|
+
const hero = this.renderImageBlock(block.hero);
|
|
246
|
+
hero.className = 'jd-struct-card__hero';
|
|
247
|
+
card.appendChild(hero);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Body
|
|
251
|
+
if (block.body) {
|
|
252
|
+
const body = document.createElement('div');
|
|
253
|
+
body.className = 'jd-struct-card__body';
|
|
254
|
+
for (const child of block.body) {
|
|
255
|
+
const el = this.renderBlock(child);
|
|
256
|
+
if (el) body.appendChild(el);
|
|
257
|
+
}
|
|
258
|
+
card.appendChild(body);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Footer
|
|
262
|
+
if (block.footer) {
|
|
263
|
+
const footer = document.createElement('div');
|
|
264
|
+
footer.className = 'jd-struct-card__footer';
|
|
265
|
+
for (const child of block.footer) {
|
|
266
|
+
const el = this.renderBlock(child);
|
|
267
|
+
if (el) footer.appendChild(el);
|
|
268
|
+
}
|
|
269
|
+
card.appendChild(footer);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Actions
|
|
273
|
+
if (block.actions && block.actions.length > 0) {
|
|
274
|
+
const actions = document.createElement('div');
|
|
275
|
+
actions.className = 'jd-struct-card__actions';
|
|
276
|
+
for (const btn of block.actions) {
|
|
277
|
+
actions.appendChild(this.renderButtonBlock(btn));
|
|
278
|
+
}
|
|
279
|
+
card.appendChild(actions);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return card;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private renderCarouselBlock(block: any): HTMLElement {
|
|
286
|
+
const container = document.createElement('div');
|
|
287
|
+
container.className = 'jd-struct-carousel';
|
|
288
|
+
|
|
289
|
+
const cards: any[] = block.cards || [];
|
|
290
|
+
for (const cardBlock of cards) {
|
|
291
|
+
container.appendChild(this.renderCardBlock(cardBlock));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return container;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private renderContainerBlock(block: any): HTMLElement {
|
|
298
|
+
const container = document.createElement('div');
|
|
299
|
+
container.className = 'jd-struct-container';
|
|
300
|
+
container.setAttribute('data-layout', block.layout || 'vertical');
|
|
301
|
+
|
|
302
|
+
if (block.style) {
|
|
303
|
+
if (block.style.background_color) container.style.backgroundColor = block.style.background_color;
|
|
304
|
+
if (block.style.border_color) container.style.borderColor = block.style.border_color;
|
|
305
|
+
if (block.style.border_radius) container.style.borderRadius = block.style.border_radius;
|
|
306
|
+
if (block.style.padding) container.style.padding = block.style.padding;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const contents: any[] = block.contents || [];
|
|
310
|
+
for (const child of contents) {
|
|
311
|
+
const el = this.renderBlock(child);
|
|
312
|
+
if (el) container.appendChild(el);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return container;
|
|
316
|
+
}
|
|
317
|
+
|
|
90
318
|
private renderTypingIndicator(): HTMLElement {
|
|
91
319
|
const container = document.createElement('div');
|
|
92
320
|
container.className = 'jd-typing-indicator';
|
package/src/core/Widget.ts
CHANGED
|
@@ -96,7 +96,7 @@ export class Widget implements JidouChatAPI {
|
|
|
96
96
|
init(): void {
|
|
97
97
|
this.createContainer();
|
|
98
98
|
this.render();
|
|
99
|
-
|
|
99
|
+
// History is now loaded after WebSocket connects in the 'connected' event handler
|
|
100
100
|
this.triggerHook('onLoad');
|
|
101
101
|
|
|
102
102
|
// Connect WebSocket
|
|
@@ -233,6 +233,7 @@ export class Widget implements JidouChatAPI {
|
|
|
233
233
|
onCollapse: () => this.setDisplayMode('floating'),
|
|
234
234
|
onExpand: () => this.setDisplayMode('fullscreen'),
|
|
235
235
|
onSend: (message) => this.handleSendMessage(message),
|
|
236
|
+
onAction: (text) => this.handleSendMessage(text),
|
|
236
237
|
onTypingStart: () => this.wsClient.sendTypingStart(),
|
|
237
238
|
onTypingStop: () => this.wsClient.sendTypingStop(),
|
|
238
239
|
onQuickReplySelect: (item) => this.handleQuickReply(item),
|
|
@@ -287,9 +288,14 @@ export class Widget implements JidouChatAPI {
|
|
|
287
288
|
this.historyManager.setSessionId(sessionId);
|
|
288
289
|
|
|
289
290
|
if (isResumed) {
|
|
290
|
-
// Session was resumed
|
|
291
|
+
// Session was resumed - load history for this session
|
|
292
|
+
const messages = this.historyManager.load();
|
|
293
|
+
if (messages && messages.length > 0) {
|
|
294
|
+
this.stateManager.setMessages(messages);
|
|
295
|
+
}
|
|
291
296
|
} else {
|
|
292
|
-
// New session
|
|
297
|
+
// New session - clear any old messages and start fresh
|
|
298
|
+
this.stateManager.clearMessages();
|
|
293
299
|
this.triggerHook('onConversationStarted', sessionId);
|
|
294
300
|
}
|
|
295
301
|
});
|
|
@@ -304,6 +310,7 @@ export class Widget implements JidouChatAPI {
|
|
|
304
310
|
id: payload.id,
|
|
305
311
|
role: 'assistant',
|
|
306
312
|
content: payload.content,
|
|
313
|
+
structContent: payload.structContent,
|
|
307
314
|
timestamp: payload.timestamp || now(),
|
|
308
315
|
};
|
|
309
316
|
|
|
@@ -327,16 +334,6 @@ export class Widget implements JidouChatAPI {
|
|
|
327
334
|
});
|
|
328
335
|
}
|
|
329
336
|
|
|
330
|
-
private loadHistory(): void {
|
|
331
|
-
const sessionId = this.wsClient.getSessionId();
|
|
332
|
-
this.historyManager.setSessionId(sessionId);
|
|
333
|
-
|
|
334
|
-
const messages = this.historyManager.load();
|
|
335
|
-
if (messages && messages.length > 0) {
|
|
336
|
-
this.stateManager.setMessages(messages);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
337
|
private handleSendMessage(content: string): void {
|
|
341
338
|
const message: Message = {
|
|
342
339
|
id: generateMessageId(),
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import type { Message, LocalChatHistory } from '../types';
|
|
2
2
|
import * as storage from '../utils/storage';
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const STORAGE_KEY_PREFIX = 'chat_history_';
|
|
5
5
|
const MAX_MESSAGES = 100;
|
|
6
6
|
const EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
7
7
|
|
|
8
8
|
export class ChatHistoryManager {
|
|
9
9
|
private sessionId: string | null = null;
|
|
10
10
|
|
|
11
|
+
private getStorageKey(sessionId?: string): string {
|
|
12
|
+
const id = sessionId || this.sessionId;
|
|
13
|
+
return id ? `${STORAGE_KEY_PREFIX}${id}` : STORAGE_KEY_PREFIX;
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
setSessionId(sessionId: string): void {
|
|
12
17
|
this.sessionId = sessionId;
|
|
13
18
|
}
|
|
@@ -17,15 +22,12 @@ export class ChatHistoryManager {
|
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
private getValidHistory(sessionId?: string): LocalChatHistory | null {
|
|
20
|
-
const
|
|
25
|
+
const key = this.getStorageKey(sessionId);
|
|
26
|
+
const history = storage.getItem<LocalChatHistory>(key);
|
|
21
27
|
if (!history) return null;
|
|
22
28
|
|
|
23
29
|
if (Date.now() - history.savedAt > EXPIRY_MS) {
|
|
24
|
-
this.clear();
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (sessionId && history.sessionId !== sessionId) {
|
|
30
|
+
this.clear(sessionId);
|
|
29
31
|
return null;
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -41,11 +43,11 @@ export class ChatHistoryManager {
|
|
|
41
43
|
savedAt: Date.now(),
|
|
42
44
|
};
|
|
43
45
|
|
|
44
|
-
return storage.setItem(
|
|
46
|
+
return storage.setItem(this.getStorageKey(), history);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
load(): Message[] | null {
|
|
48
|
-
const history = this.getValidHistory(
|
|
50
|
+
const history = this.getValidHistory();
|
|
49
51
|
return history?.messages || null;
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -57,7 +59,7 @@ export class ChatHistoryManager {
|
|
|
57
59
|
appendMessage(message: Message): boolean {
|
|
58
60
|
if (!this.sessionId) return false;
|
|
59
61
|
|
|
60
|
-
const history = this.getValidHistory(
|
|
62
|
+
const history = this.getValidHistory();
|
|
61
63
|
const messages = history
|
|
62
64
|
? [...history.messages, message].slice(-MAX_MESSAGES)
|
|
63
65
|
: [message];
|
|
@@ -66,7 +68,7 @@ export class ChatHistoryManager {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
updateMessage(messageId: string, updates: Partial<Message>): boolean {
|
|
69
|
-
const history = this.getValidHistory(
|
|
71
|
+
const history = this.getValidHistory();
|
|
70
72
|
if (!history) return false;
|
|
71
73
|
|
|
72
74
|
const messages = history.messages.map((msg) =>
|
|
@@ -76,23 +78,36 @@ export class ChatHistoryManager {
|
|
|
76
78
|
return this.save(messages);
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
clear(): void {
|
|
80
|
-
storage.removeItem(
|
|
81
|
+
clear(sessionId?: string): void {
|
|
82
|
+
storage.removeItem(this.getStorageKey(sessionId));
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
hasHistory(): boolean {
|
|
84
|
-
return !!this.getValidHistory(
|
|
86
|
+
return !!this.getValidHistory();
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
getMessageCount(): number {
|
|
88
|
-
const history =
|
|
90
|
+
const history = this.getValidHistory();
|
|
89
91
|
return history?.messages.length || 0;
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
static cleanup(): void {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
// Clean up expired histories
|
|
96
|
+
// This is a simplified version - in production you might want to
|
|
97
|
+
// iterate through all chat_history_* keys
|
|
98
|
+
const keys = Object.keys(localStorage).filter(k => k.startsWith(STORAGE_KEY_PREFIX));
|
|
99
|
+
keys.forEach(key => {
|
|
100
|
+
try {
|
|
101
|
+
const data = localStorage.getItem(key);
|
|
102
|
+
if (data) {
|
|
103
|
+
const history = JSON.parse(data) as LocalChatHistory;
|
|
104
|
+
if (Date.now() - history.savedAt > EXPIRY_MS) {
|
|
105
|
+
localStorage.removeItem(key);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
// Ignore parse errors
|
|
110
|
+
}
|
|
111
|
+
});
|
|
97
112
|
}
|
|
98
113
|
}
|
|
@@ -163,11 +163,19 @@ export class WebSocketClient extends EventEmitter<WebSocketEvents> {
|
|
|
163
163
|
|
|
164
164
|
switch (message.type) {
|
|
165
165
|
case 'connection:ack': {
|
|
166
|
-
|
|
166
|
+
// Connection acknowledged, wait for session:ready
|
|
167
167
|
this.setConnectionState('connected');
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
case 'session:ready': {
|
|
172
|
+
const payload = message.payload as { sessionId: string; restored: boolean };
|
|
173
|
+
// Update session ID from server
|
|
174
|
+
this.sessionId = payload.sessionId;
|
|
175
|
+
storage.setItem(STORAGE_SESSION_KEY, this.sessionId);
|
|
168
176
|
this.emit('connected', {
|
|
169
177
|
sessionId: payload.sessionId,
|
|
170
|
-
isResumed: payload.
|
|
178
|
+
isResumed: payload.restored,
|
|
171
179
|
});
|
|
172
180
|
break;
|
|
173
181
|
}
|
package/src/styles/chatWindow.ts
CHANGED
|
@@ -63,13 +63,22 @@ export const CHAT_WINDOW_STYLES = `
|
|
|
63
63
|
/* Mobile responsive */
|
|
64
64
|
@media (max-width: 480px) {
|
|
65
65
|
.jd-chat-window--floating {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
position: fixed !important;
|
|
67
|
+
top: 0 !important;
|
|
68
|
+
left: 0 !important;
|
|
69
|
+
right: 0 !important;
|
|
70
|
+
bottom: 0 !important;
|
|
71
|
+
width: 100vw !important;
|
|
72
|
+
height: 100vh !important;
|
|
73
|
+
max-width: 100vw !important;
|
|
74
|
+
max-height: 100vh !important;
|
|
75
|
+
border-radius: 0 !important;
|
|
76
|
+
z-index: 999999 !important;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Hide expand button on mobile - already fullscreen */
|
|
80
|
+
.jd-chat-header__btn--expand {
|
|
81
|
+
display: none !important;
|
|
73
82
|
}
|
|
74
83
|
}
|
|
75
84
|
|
package/src/styles/launcher.ts
CHANGED
|
@@ -183,4 +183,13 @@ export const LAUNCHER_STYLES = `
|
|
|
183
183
|
visibility: hidden;
|
|
184
184
|
transform: scale(0.8);
|
|
185
185
|
}
|
|
186
|
+
|
|
187
|
+
/* Mobile: hide launcher when chat is open */
|
|
188
|
+
@media (max-width: 480px) {
|
|
189
|
+
.jd-launcher--open {
|
|
190
|
+
opacity: 0;
|
|
191
|
+
visibility: hidden;
|
|
192
|
+
pointer-events: none;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
186
195
|
`;
|
package/src/styles/messages.ts
CHANGED
|
@@ -308,6 +308,211 @@ export const MESSAGE_STYLES = `
|
|
|
308
308
|
height: 20px;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
/* ============================================
|
|
312
|
+
Structured Content (structContent)
|
|
313
|
+
============================================ */
|
|
314
|
+
|
|
315
|
+
/* Container for all struct blocks */
|
|
316
|
+
.jd-struct {
|
|
317
|
+
display: flex;
|
|
318
|
+
flex-direction: column;
|
|
319
|
+
gap: 8px;
|
|
320
|
+
width: 100%;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* Text block */
|
|
324
|
+
.jd-struct-text {
|
|
325
|
+
margin: 0;
|
|
326
|
+
font-family: var(--jd-font);
|
|
327
|
+
font-size: 14px;
|
|
328
|
+
line-height: 1.5;
|
|
329
|
+
color: inherit;
|
|
330
|
+
word-wrap: break-word;
|
|
331
|
+
}
|
|
332
|
+
.jd-struct-text[data-size="xs"] { font-size: 11px; }
|
|
333
|
+
.jd-struct-text[data-size="sm"] { font-size: 13px; }
|
|
334
|
+
.jd-struct-text[data-size="md"] { font-size: 14px; }
|
|
335
|
+
.jd-struct-text[data-size="lg"] { font-size: 16px; }
|
|
336
|
+
.jd-struct-text[data-size="xl"] { font-size: 18px; }
|
|
337
|
+
.jd-struct-text[data-size="xxl"] { font-size: 22px; }
|
|
338
|
+
|
|
339
|
+
/* Image block */
|
|
340
|
+
.jd-struct-image {
|
|
341
|
+
width: 100%;
|
|
342
|
+
border-radius: 8px;
|
|
343
|
+
display: block;
|
|
344
|
+
object-fit: cover;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* Button block */
|
|
348
|
+
.jd-struct-button {
|
|
349
|
+
display: inline-flex;
|
|
350
|
+
align-items: center;
|
|
351
|
+
justify-content: center;
|
|
352
|
+
padding: 8px 16px;
|
|
353
|
+
border: none;
|
|
354
|
+
border-radius: 8px;
|
|
355
|
+
font-family: var(--jd-font);
|
|
356
|
+
font-size: 14px;
|
|
357
|
+
font-weight: 500;
|
|
358
|
+
cursor: pointer;
|
|
359
|
+
transition: opacity var(--jd-transition), background var(--jd-transition);
|
|
360
|
+
width: 100%;
|
|
361
|
+
text-align: center;
|
|
362
|
+
}
|
|
363
|
+
.jd-struct-button:hover { opacity: 0.85; }
|
|
364
|
+
.jd-struct-button[data-style="primary"] {
|
|
365
|
+
background: var(--jd-primary);
|
|
366
|
+
color: #fff;
|
|
367
|
+
}
|
|
368
|
+
.jd-struct-button[data-style="secondary"] {
|
|
369
|
+
background: var(--jd-surface);
|
|
370
|
+
color: var(--jd-text);
|
|
371
|
+
border: 1px solid var(--jd-border);
|
|
372
|
+
}
|
|
373
|
+
.jd-struct-button[data-style="danger"] {
|
|
374
|
+
background: #EF4444;
|
|
375
|
+
color: #fff;
|
|
376
|
+
}
|
|
377
|
+
.jd-struct-button[data-style="link"] {
|
|
378
|
+
background: transparent;
|
|
379
|
+
color: var(--jd-primary);
|
|
380
|
+
padding: 4px 0;
|
|
381
|
+
text-decoration: underline;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Actions block (button group) */
|
|
385
|
+
.jd-struct-actions {
|
|
386
|
+
display: flex;
|
|
387
|
+
gap: 8px;
|
|
388
|
+
}
|
|
389
|
+
.jd-struct-actions[data-layout="vertical"] {
|
|
390
|
+
flex-direction: column;
|
|
391
|
+
}
|
|
392
|
+
.jd-struct-actions[data-layout="horizontal"] {
|
|
393
|
+
flex-direction: row;
|
|
394
|
+
}
|
|
395
|
+
.jd-struct-actions[data-layout="horizontal"] .jd-struct-button {
|
|
396
|
+
flex: 1;
|
|
397
|
+
width: auto;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/* Divider */
|
|
401
|
+
.jd-struct-divider {
|
|
402
|
+
border: none;
|
|
403
|
+
border-top: 1px solid var(--jd-border);
|
|
404
|
+
margin: 4px 0;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* Header block */
|
|
408
|
+
.jd-struct-header {
|
|
409
|
+
display: flex;
|
|
410
|
+
flex-direction: column;
|
|
411
|
+
gap: 2px;
|
|
412
|
+
}
|
|
413
|
+
.jd-struct-header__title {
|
|
414
|
+
font-family: var(--jd-font);
|
|
415
|
+
font-size: 16px;
|
|
416
|
+
font-weight: 600;
|
|
417
|
+
color: inherit;
|
|
418
|
+
}
|
|
419
|
+
.jd-struct-header__subtitle {
|
|
420
|
+
font-family: var(--jd-font);
|
|
421
|
+
font-size: 13px;
|
|
422
|
+
color: var(--jd-text-secondary);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/* Card block */
|
|
426
|
+
.jd-struct-card {
|
|
427
|
+
border: 1px solid var(--jd-border);
|
|
428
|
+
border-radius: 12px;
|
|
429
|
+
overflow: hidden;
|
|
430
|
+
background: var(--jd-background);
|
|
431
|
+
}
|
|
432
|
+
.jd-struct-card__header {
|
|
433
|
+
padding: 12px 16px;
|
|
434
|
+
}
|
|
435
|
+
.jd-struct-card__title {
|
|
436
|
+
font-family: var(--jd-font);
|
|
437
|
+
font-size: 15px;
|
|
438
|
+
font-weight: 600;
|
|
439
|
+
color: var(--jd-text);
|
|
440
|
+
}
|
|
441
|
+
.jd-struct-card__subtitle {
|
|
442
|
+
font-family: var(--jd-font);
|
|
443
|
+
font-size: 13px;
|
|
444
|
+
color: var(--jd-text-secondary);
|
|
445
|
+
margin-top: 2px;
|
|
446
|
+
}
|
|
447
|
+
.jd-struct-card__hero {
|
|
448
|
+
width: 100%;
|
|
449
|
+
border-radius: 0;
|
|
450
|
+
display: block;
|
|
451
|
+
object-fit: cover;
|
|
452
|
+
}
|
|
453
|
+
.jd-struct-card__body {
|
|
454
|
+
padding: 12px 16px;
|
|
455
|
+
display: flex;
|
|
456
|
+
flex-direction: column;
|
|
457
|
+
gap: 8px;
|
|
458
|
+
}
|
|
459
|
+
.jd-struct-card__footer {
|
|
460
|
+
padding: 8px 16px 12px;
|
|
461
|
+
display: flex;
|
|
462
|
+
flex-direction: column;
|
|
463
|
+
gap: 6px;
|
|
464
|
+
border-top: 1px solid var(--jd-border);
|
|
465
|
+
}
|
|
466
|
+
.jd-struct-card__actions {
|
|
467
|
+
padding: 8px 16px 12px;
|
|
468
|
+
display: flex;
|
|
469
|
+
flex-direction: column;
|
|
470
|
+
gap: 6px;
|
|
471
|
+
border-top: 1px solid var(--jd-border);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/* Carousel block */
|
|
475
|
+
.jd-struct-carousel {
|
|
476
|
+
display: flex;
|
|
477
|
+
gap: 12px;
|
|
478
|
+
overflow-x: auto;
|
|
479
|
+
scroll-snap-type: x mandatory;
|
|
480
|
+
-webkit-overflow-scrolling: touch;
|
|
481
|
+
scrollbar-width: none;
|
|
482
|
+
padding-bottom: 4px;
|
|
483
|
+
}
|
|
484
|
+
.jd-struct-carousel::-webkit-scrollbar { display: none; }
|
|
485
|
+
.jd-struct-carousel > .jd-struct-card {
|
|
486
|
+
min-width: 240px;
|
|
487
|
+
max-width: 280px;
|
|
488
|
+
flex-shrink: 0;
|
|
489
|
+
scroll-snap-align: start;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/* Container block (generic layout) */
|
|
493
|
+
.jd-struct-container {
|
|
494
|
+
display: flex;
|
|
495
|
+
gap: 8px;
|
|
496
|
+
}
|
|
497
|
+
.jd-struct-container[data-layout="vertical"] {
|
|
498
|
+
flex-direction: column;
|
|
499
|
+
}
|
|
500
|
+
.jd-struct-container[data-layout="horizontal"] {
|
|
501
|
+
flex-direction: row;
|
|
502
|
+
}
|
|
503
|
+
.jd-struct-container[data-layout="baseline"] {
|
|
504
|
+
flex-direction: row;
|
|
505
|
+
align-items: baseline;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/* Wider bubble for struct content */
|
|
509
|
+
.jd-message--assistant .jd-message__bubble:has(.jd-struct) {
|
|
510
|
+
padding: 12px;
|
|
511
|
+
}
|
|
512
|
+
.jd-message--assistant:has(.jd-struct) {
|
|
513
|
+
max-width: 95%;
|
|
514
|
+
}
|
|
515
|
+
|
|
311
516
|
/* Empty State */
|
|
312
517
|
.jd-empty-state {
|
|
313
518
|
flex: 1;
|
package/src/types/index.ts
CHANGED
|
@@ -33,6 +33,7 @@ export interface Message {
|
|
|
33
33
|
id: string;
|
|
34
34
|
role: 'user' | 'assistant';
|
|
35
35
|
content: string;
|
|
36
|
+
structContent?: any;
|
|
36
37
|
timestamp: number;
|
|
37
38
|
status?: 'sending' | 'sent' | 'error';
|
|
38
39
|
}
|
|
@@ -293,6 +294,7 @@ export interface ConnectionAckPayload {
|
|
|
293
294
|
export interface MessagePayload {
|
|
294
295
|
id: string;
|
|
295
296
|
content: string;
|
|
297
|
+
structContent?: any;
|
|
296
298
|
role?: 'user' | 'assistant';
|
|
297
299
|
timestamp?: number;
|
|
298
300
|
}
|