@saiboniuma/chat-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ (function(o,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("@saiboniuma/realtime-core")):typeof define=="function"&&define.amd?define(["exports","@saiboniuma/realtime-core"],l):(o=typeof globalThis<"u"?globalThis:o||self,l(o.RealtimeChatUI={},o.RealtimeCore))})(this,(function(o,l){"use strict";class x{constructor(e){this.renderers=[],this.inputTools=[],this.plugins=new Map,this.i18n=e}register(e){if(this.plugins.has(e.name))return;const t={registerRenderer:i=>{this.renderers.push(i)},registerInputTool:i=>{this.inputTools.push(i),this.inputTools.sort((n,s)=>(n.order??0)-(s.order??0))},i18n:this.i18n,registerLocale:(i,n)=>{this.i18n.registerLocale(i,n)}};e.install(t),this.plugins.set(e.name,e)}unregister(e){var i;const t=this.plugins.get(e);if(t){(i=t.uninstall)==null||i.call(t),this.plugins.delete(e),this.renderers=[],this.inputTools=[];for(const n of this.plugins.values()){const s={registerRenderer:c=>{this.renderers.push(c)},registerInputTool:c=>{this.inputTools.push(c)},i18n:this.i18n,registerLocale:(c,h)=>{this.i18n.registerLocale(c,h)}};n.install(s)}this.inputTools.sort((n,s)=>(n.order??0)-(s.order??0))}}getRenderer(e){var t;for(const i of this.renderers)if((t=i.canRender)!=null&&t.call(i,e))return i;for(const i of this.renderers)if(i.contentTypes.includes(Number(e.contentType)))return i;return null}getInputTools(){return this.inputTools}destroy(){var e;for(const t of this.plugins.values())(e=t.uninstall)==null||e.call(t);this.plugins.clear(),this.renderers=[],this.inputTools=[]}}const O={"--chat-bg":"#f5f7fb","--chat-bubble-self-start":"#667eea","--chat-bubble-self-end":"#764ba2","--chat-bubble-other":"#ffffff","--chat-text-primary":"#333333","--chat-text-secondary":"#666666","--chat-text-self":"#ffffff","--chat-border":"#e8e8e8","--chat-input-bg":"#ffffff","--chat-avatar-bg":"#e8e8e8","--chat-avatar-self-start":"#667eea","--chat-avatar-self-end":"#764ba2","--chat-error":"#ff4d4f","--chat-accent-start":"#667eea","--chat-accent-end":"#764ba2"},G={"--chat-bg":"#1a1a2e","--chat-bubble-self-start":"#667eea","--chat-bubble-self-end":"#764ba2","--chat-bubble-other":"#2d2d44","--chat-text-primary":"#e0e0e0","--chat-text-secondary":"#999999","--chat-text-self":"#ffffff","--chat-border":"#3d3d5c","--chat-input-bg":"#2d2d44","--chat-avatar-bg":"#3d3d5c","--chat-avatar-self-start":"#667eea","--chat-avatar-self-end":"#764ba2","--chat-error":"#ff4d4f","--chat-accent-start":"#667eea","--chat-accent-end":"#764ba2"};class y{constructor(){this.currentTheme="light",this.container=null}bind(e){this.container=e,this.applyTheme()}setTheme(e){this.currentTheme=e,this.applyTheme()}getTheme(){return this.currentTheme}applyTheme(){if(!this.container)return;const e=this.currentTheme==="dark"?G:O;for(const[t,i]of Object.entries(e))this.container.style.setProperty(t,i)}}function r(a,e,t){const i=document.createElement(a);if(e&&(i.className=e),t)for(const[n,s]of Object.entries(t))i.setAttribute(n,s);return i}function H(a,e){a.textContent=e}function u(a){for(;a.firstChild;)a.removeChild(a.firstChild)}function K(a,e,t){a.classList.toggle(e,t)}function w(a,e,t="chat-ui-avatar"){const i=r("div",t);if(a){const n=r("img","chat-ui-avatar-img");n.src=a,n.alt=e,n.onerror=()=>{n.style.display="none";const s=C(e);i.appendChild(s)},i.appendChild(n)}else{const n=C(e);i.appendChild(n)}return i}function C(a){const e=r("div","chat-ui-avatar-fallback");return e.textContent=I(a),e}function I(a){return a?a.charAt(0).toUpperCase():"?"}function T(a,e){const t=new Date(a),i=new Date,n=i.getTime()-t.getTime(),s=Math.floor(n/6e4);if(s<1)return(e==null?void 0:e.t("time.just_now"))??"刚刚";if(s<60)return(e==null?void 0:e.t("time.minutes_ago",{n:s}))??`${s}分钟前`;if(t.toDateString()===i.toDateString())return t.toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"});const c=new Date(i);return c.setDate(c.getDate()-1),t.toDateString()===c.toDateString()?`${(e==null?void 0:e.t("time.yesterday"))??"昨天"} ${t.toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"})}`:t.toLocaleString("zh-CN",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"})}function g(a){requestAnimationFrame(()=>{a.scrollTop=a.scrollHeight})}function k(a,e=100){const{scrollTop:t,scrollHeight:i,clientHeight:n}=a;return i-t-n<e}class E{constructor(e,t,i,n,s){this.currentUserID="",this.messages=[],this.renderedMsgIds=new Set,this.container=e,this.pluginManager=t,this.i18n=i,this.shadowRoot=n,this.getSenderInfo=s}setCurrentUserID(e){this.currentUserID=e}updateMessages(e){const t=e.filter(s=>!l.isSystemMessage(s)),i=[],n=new Set;for(const s of t)n.has(s.clientMsgID)||(n.add(s.clientMsgID),i.push(s));this.messages=i,this.render()}appendMessage(e){if(l.isSystemMessage(e)){console.log("[MessageListRenderer] appendMessage 跳过: 系统消息, clientMsgID:",e.clientMsgID);return}if(this.renderedMsgIds.has(e.clientMsgID)){console.log("[MessageListRenderer] appendMessage 跳过: 重复消息, clientMsgID:",e.clientMsgID);return}console.log("[MessageListRenderer] appendMessage 渲染消息, clientMsgID:",e.clientMsgID,"sendID:",e.sendID),this.messages.length===0&&u(this.container),this.messages.push(e);const t=k(this.container);this.renderMessageItem(e),t&&g(this.container)}scrollToBottom(){g(this.container)}render(){if(u(this.container),this.renderedMsgIds.clear(),this.messages.length===0){this.renderEmptyState();return}for(const e of this.messages)this.renderMessageItem(e);g(this.container)}renderEmptyState(){const e=r("div","chat-ui-empty"),t=r("p");t.textContent=this.i18n.t("message.empty");const i=r("p","chat-ui-empty-hint");i.textContent=this.i18n.t("message.emptyHint"),e.appendChild(t),e.appendChild(i),this.container.appendChild(e)}renderMessageItem(e){this.renderedMsgIds.add(e.clientMsgID);const t=e.sendID===this.currentUserID,i=this.getSenderInfo(e),n=r("div",`chat-ui-message-item${t?" is-self":""}`),s=r("div","chat-ui-message-header"),c=w(i.avatar,i.nickname,`chat-ui-avatar${t?" avatar-self":""}`),h=r("div","chat-ui-message-nickname");h.textContent=i.nickname,s.appendChild(c),s.appendChild(h),n.appendChild(s);const p=r("div","chat-ui-bubble-row"),v={isSelf:t,currentUserID:this.currentUserID,i18n:this.i18n,shadowRoot:this.shadowRoot},P=this.pluginManager.getRenderer(e);if(P){const f=P.render(e,v);p.appendChild(f)}else{const f=r("div","chat-ui-bubble");f.textContent=this.i18n.t("message.unknownType"),p.appendChild(f)}n.appendChild(p);const N=r("div","chat-ui-message-time");N.textContent=T(e.sendTime,this.i18n),n.appendChild(N),this.container.appendChild(n)}refreshTexts(){this.render()}clear(){this.messages=[],this.renderedMsgIds.clear(),u(this.container)}}class M{constructor(e,t,i,n,s){this.core=null,this.isSending=!1,this.isDisabled=!1,this.recvID="",this.groupId="",this.onAppendMessage=null,this.wrapper=e,this.pluginManager=t,this.i18n=i,this.shadowRoot=n,this.onSend=s,this.build()}setOnAppendMessage(e){this.onAppendMessage=e}bindCore(e){this.core=e}setRecvID(e){this.recvID=e}setGroupId(e){this.groupId=e}setDisabled(e){this.isDisabled=e,this.inputElement.disabled=e,this.updateSendButton(),this.updateToolbarButtons()}getInputElement(){return this.inputElement}refreshToolbar(){this.buildToolbar()}refreshTexts(){this.inputElement.placeholder=this.i18n.t("input.placeholder"),this.updateSendButton(),this.buildToolbar()}build(){this.wrapper.className="chat-ui-input-wrapper",this.toolbarContainer=r("div","chat-ui-toolbar"),this.wrapper.appendChild(this.toolbarContainer),this.inputArea=r("div","chat-ui-input-area"),this.inputElement=r("input","chat-ui-message-input"),this.inputElement.type="text",this.inputElement.placeholder=this.i18n.t("input.placeholder"),this.inputElement.addEventListener("keydown",this.handleKeydown.bind(this)),this.inputElement.addEventListener("input",()=>this.updateSendButton()),this.inputArea.appendChild(this.inputElement),this.sendButton=r("button","chat-ui-send-btn"),this.sendButton.textContent=this.i18n.t("input.send"),this.sendButton.addEventListener("click",this.handleSend.bind(this)),this.inputArea.appendChild(this.sendButton),this.wrapper.appendChild(this.inputArea),this.buildToolbar(),this.updateSendButton()}buildToolbar(){for(;this.toolbarContainer.firstChild;)this.toolbarContainer.removeChild(this.toolbarContainer.firstChild);const e=this.pluginManager.getInputTools();for(const t of e){const i=r("button","chat-ui-tool-btn");i.innerHTML=t.icon,i.title=t.tooltip,i.disabled=this.isDisabled,i.addEventListener("click",()=>{const n={inputElement:this.inputElement,insertText:s=>{this.inputElement.value+=s,this.inputElement.focus(),this.updateSendButton()},getInputText:()=>this.inputElement.value,setInputText:s=>{this.inputElement.value=s,this.updateSendButton()},getRecvID:()=>this.groupId||this.recvID,isGroup:()=>!!this.groupId,core:this.core,appendMessage:s=>{this.onAppendMessage&&this.onAppendMessage(s)},shadowRoot:this.shadowRoot,i18n:this.i18n};t.onActivate(n)}),this.toolbarContainer.appendChild(i)}}handleKeydown(e){e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),this.handleSend())}async handleSend(){const e=this.inputElement.value.trim();if(!(!e||this.isSending||this.isDisabled)){this.isSending=!0,this.updateSendButton();try{await this.onSend(e),this.inputElement.value=""}finally{this.isSending=!1,this.updateSendButton()}}}updateToolbarButtons(){this.toolbarContainer.querySelectorAll("button").forEach(t=>{t.disabled=this.isDisabled})}updateSendButton(){const e=this.inputElement.value.trim().length>0;this.sendButton.disabled=!e||this.isDisabled||this.isSending,this.sendButton.textContent=this.isSending?this.i18n.t("input.sending"):this.i18n.t("input.send")}}const S={"input.placeholder":"请输入消息...","input.send":"发送","input.sending":"发送中","message.image":"[图片]","message.loading":"加载中...","message.noMore":"没有更多消息了","message.failed":"发送失败","message.retry":"重试","message.empty":"暂无消息","message.emptyHint":"发送消息开始对话","message.unknownType":"[未知消息类型]","message.imageError":"[图片加载失败]","emoji.title":"表情","emoji.categories.recent":"常用","emoji.categories.emotion":"情绪","emoji.categories.gesture":"手势","emoji.categories.heart":"心形","emoji.categories.other":"其他","emoji.close":"关闭","image.upload":"发送图片","image.preview":"查看大图","image.uploading":"上传中...","status.connecting":"正在连接服务器...","status.connected":"已连接","status.disconnected":"连接失败,请检查网络后重试","status.reconnect":"重新连接","time.just_now":"刚刚","time.minutes_ago":"{n}分钟前","time.today":"今天","time.yesterday":"昨天","common.cancel":"取消","common.confirm":"确认","common.close":"关闭","widget.title":"在线客服"},L={"input.placeholder":"Type a message...","input.send":"Send","input.sending":"Sending","message.image":"[Image]","message.loading":"Loading...","message.noMore":"No more messages","message.failed":"Failed to send","message.retry":"Retry","message.empty":"No messages yet","message.emptyHint":"Send a message to start","message.unknownType":"[Unknown message type]","message.imageError":"[Image load failed]","emoji.title":"Emoji","emoji.categories.recent":"Frequent","emoji.categories.emotion":"Emotion","emoji.categories.gesture":"Gesture","emoji.categories.heart":"Heart","emoji.categories.other":"Other","emoji.close":"Close","image.upload":"Send Image","image.preview":"Preview","image.uploading":"Uploading...","status.connecting":"Connecting...","status.connected":"Connected","status.disconnected":"Connection failed, please check network","status.reconnect":"Reconnect","time.just_now":"Just now","time.minutes_ago":"{n} min ago","time.today":"Today","time.yesterday":"Yesterday","common.cancel":"Cancel","common.confirm":"Confirm","common.close":"Close","widget.title":"Customer Service"};class j{constructor(e){this.currentLocale="zh-CN",this.messages=new Map,this.changeListeners=new Set,this.messages.set("zh-CN",{...S}),this.messages.set("en-US",{...L}),e&&(this.currentLocale=e)}t(e,t){var s;const i=this.messages.get(this.currentLocale);let n=(i==null?void 0:i[e])??((s=this.messages.get("zh-CN"))==null?void 0:s[e])??e;if(t)for(const[c,h]of Object.entries(t))n=n.replace(`{${c}}`,String(h));return n}getLocale(){return this.currentLocale}setLocale(e){this.currentLocale!==e&&(this.currentLocale=e,this.changeListeners.forEach(t=>t()))}registerLocale(e,t){const i=this.messages.get(e)||{};this.messages.set(e,{...i,...t})}onChange(e){this.changeListeners.add(e)}offChange(e){this.changeListeners.delete(e)}destroy(){this.changeListeners.clear()}}const $=":host{--chat-bg: #f5f7fb;--chat-bubble-self-start: #667eea;--chat-bubble-self-end: #764ba2;--chat-bubble-other: #ffffff;--chat-text-primary: #333333;--chat-text-secondary: #666666;--chat-text-self: #ffffff;--chat-border: #e8e8e8;--chat-input-bg: #ffffff;--chat-avatar-bg: #e8e8e8;--chat-avatar-self-start: #667eea;--chat-avatar-self-end: #764ba2;--chat-error: #ff4d4f;--chat-accent-start: #667eea;--chat-accent-end: #764ba2;display:block;height:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:14px;line-height:1.5;color:var(--chat-text-primary);box-sizing:border-box}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}.chat-ui-root{position:relative;display:flex;flex-direction:column;height:100%;background:var(--chat-bg);overflow:hidden}.chat-ui-overlay{position:absolute;top:0;left:0;right:0;bottom:0;background:#f5f7fbfa;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;z-index:10}.chat-ui-spinner{width:36px;height:36px;border:3px solid var(--chat-border);border-top-color:var(--chat-accent-start);border-radius:50%;animation:chat-ui-spin .8s linear infinite}.chat-ui-overlay-text{font-size:14px;color:var(--chat-text-secondary)}.chat-ui-error-icon{width:48px;height:48px;border-radius:50%;background:var(--chat-error);color:#fff;font-size:24px;font-weight:700;display:flex;align-items:center;justify-content:center}.chat-ui-reconnect-btn{margin-top:4px;padding:8px 24px;border:none;border-radius:20px;background:linear-gradient(135deg,var(--chat-accent-start) 0%,var(--chat-accent-end) 100%);color:#fff;font-size:14px;cursor:pointer;transition:opacity .2s}.chat-ui-reconnect-btn:hover{opacity:.9}@keyframes chat-ui-spin{to{transform:rotate(360deg)}}",F=".chat-ui-message-list{flex:1;overflow-y:auto;padding:16px}.chat-ui-empty{text-align:center;color:var(--chat-text-secondary);padding-top:60px}.chat-ui-empty p{margin:8px 0}.chat-ui-empty-hint{font-size:12px}.chat-ui-message-item{display:flex;flex-direction:column;margin-bottom:16px;align-items:flex-start}.chat-ui-message-item.is-self{align-items:flex-end}.chat-ui-message-header{display:flex;align-items:center;gap:8px;margin-bottom:6px}.chat-ui-message-item.is-self .chat-ui-message-header{flex-direction:row-reverse}.chat-ui-avatar{width:28px;height:28px;border-radius:50%;flex-shrink:0;overflow:hidden;background:var(--chat-avatar-bg);display:flex;align-items:center;justify-content:center}.chat-ui-avatar.avatar-self{background:linear-gradient(135deg,var(--chat-avatar-self-start) 0%,var(--chat-avatar-self-end) 100%)}.chat-ui-avatar-img{width:100%;height:100%;object-fit:cover}.chat-ui-avatar-fallback{width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;color:#fff;background:#999}.chat-ui-avatar.avatar-self .chat-ui-avatar-fallback{background:transparent}.chat-ui-message-nickname{font-size:12px;color:var(--chat-text-secondary)}.chat-ui-bubble-row{width:100%;display:flex}.chat-ui-message-item.is-self .chat-ui-bubble-row{justify-content:flex-end}.chat-ui-bubble{padding:10px 14px;border-radius:12px;background:var(--chat-bubble-other);color:var(--chat-text-primary);word-wrap:break-word;box-shadow:0 1px 2px #0000000d;max-width:85%}.chat-ui-message-item.is-self .chat-ui-bubble{background:linear-gradient(135deg,var(--chat-bubble-self-start) 0%,var(--chat-bubble-self-end) 100%);color:var(--chat-text-self)}.chat-ui-message-time{font-size:11px;color:var(--chat-text-secondary);margin-top:4px;padding:0 4px}.chat-ui-message-item.is-self .chat-ui-message-time{text-align:right;align-self:flex-end}",q=".chat-ui-input-wrapper{position:relative;background:var(--chat-input-bg);border-top:1px solid var(--chat-border)}.chat-ui-toolbar{display:flex;align-items:center;gap:2px;padding:6px 10px 0}.chat-ui-tool-btn{width:32px;height:32px;display:flex;align-items:center;justify-content:center;border:none;background:transparent;color:var(--chat-text-secondary);cursor:pointer;border-radius:6px;transition:all .2s;flex-shrink:0;font-size:18px}.chat-ui-tool-btn:hover:not(:disabled){background:#0000000d;color:var(--chat-accent-start)}.chat-ui-tool-btn.active{background:#0000000d;color:var(--chat-accent-start)}.chat-ui-tool-btn:disabled{opacity:.4;cursor:not-allowed}.chat-ui-hidden-input{display:none}.chat-ui-input-area{display:flex;align-items:center;gap:8px;padding:6px 10px 10px;box-sizing:border-box;width:100%}.chat-ui-message-input{flex:1;padding:10px 16px;border:1px solid var(--chat-border);border-radius:20px;font-size:14px;outline:none;transition:border-color .2s;background-color:var(--chat-input-bg);color:var(--chat-text-primary);font-family:inherit}.chat-ui-message-input:focus{border-color:var(--chat-accent-start)}.chat-ui-message-input:disabled{background:#0000000d;cursor:not-allowed}.chat-ui-send-btn{padding:10px 20px;background:linear-gradient(135deg,var(--chat-accent-start) 0%,var(--chat-accent-end) 100%);color:#fff;border:none;border-radius:20px;font-size:14px;cursor:pointer;transition:all .2s;white-space:nowrap;flex-shrink:0;font-family:inherit;font-weight:500}.chat-ui-send-btn:hover:not(:disabled){opacity:.9;box-shadow:0 2px 8px #667eea66}.chat-ui-send-btn:disabled{background:#d1d5db;color:#9ca3af;cursor:not-allowed;box-shadow:none}@media(max-width:480px){.chat-ui-toolbar{padding:4px 8px 0}.chat-ui-tool-btn{width:28px;height:28px;font-size:16px}.chat-ui-input-area{gap:6px;padding:4px 8px 8px}.chat-ui-message-input{padding:8px 12px;font-size:16px}.chat-ui-send-btn{padding:8px 14px;font-size:14px}}",W=".chat-ui-message-image{padding:4px!important;background:transparent!important;box-shadow:none!important}.chat-ui-message-item.is-self .chat-ui-message-image{background:transparent!important}.chat-ui-chat-image{max-width:200px;max-height:200px;border-radius:8px;cursor:pointer;transition:transform .2s;object-fit:contain}.chat-ui-chat-image:hover{transform:scale(1.02)}.chat-ui-image-error{color:var(--chat-text-secondary);font-size:12px;padding:20px;display:block;background:#0000000d;border-radius:8px}.chat-ui-image-preview-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:#000000d9;display:flex;align-items:center;justify-content:center;z-index:1000}.chat-ui-image-preview-container{position:relative;max-width:90vw;max-height:90vh}.chat-ui-preview-image{max-width:90vw;max-height:90vh;object-fit:contain;border-radius:4px}.chat-ui-close-preview-btn{position:absolute;top:-40px;right:-40px;width:36px;height:36px;border:none;background:#fff3;color:#fff;font-size:24px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .2s}.chat-ui-close-preview-btn:hover{background:#ffffff4d}",J=".chat-ui-emoji-picker{position:absolute;bottom:100%;left:0;right:0;background:var(--chat-input-bg);border:1px solid var(--chat-border);border-radius:12px 12px 0 0;box-shadow:0 -4px 12px #0000001a;padding:12px;z-index:100}.chat-ui-emoji-categories{display:flex;gap:8px;margin-bottom:12px;border-bottom:1px solid var(--chat-border);padding-bottom:8px}.chat-ui-category-btn{padding:4px 12px;border:none;background:#0000000d;border-radius:16px;font-size:12px;color:var(--chat-text-secondary);cursor:pointer;transition:all .2s;font-family:inherit}.chat-ui-category-btn:hover{background:#0000001a}.chat-ui-category-btn.active{background:linear-gradient(135deg,var(--chat-accent-start) 0%,var(--chat-accent-end) 100%);color:#fff}.chat-ui-emoji-grid{display:grid;grid-template-columns:repeat(8,1fr);gap:4px;max-height:200px;overflow-y:auto}.chat-ui-emoji-btn{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border:none;background:transparent;font-size:22px;cursor:pointer;border-radius:8px;transition:background .2s}.chat-ui-emoji-btn:hover{background:#0000000d}@media(max-width:480px){.chat-ui-emoji-grid{grid-template-columns:repeat(6,1fr)}.chat-ui-emoji-btn{width:44px;height:44px;font-size:24px}}";class V{constructor(e,t={}){this.core=null,this.currentUserID="",this.userInfo={nickname:"访客",avatar:""},this.agentInfo={nickname:"客服",avatar:""},this.agentUserID="",this.groupId="",this.host=r("div","chat-ui-host"),this.shadowRoot=this.host.attachShadow({mode:"open"}),e.appendChild(this.host);const i=document.createElement("style");if(i.textContent=[$,F,q,W,J].join(`
2
+ `),this.shadowRoot.appendChild(i),this.root=r("div",`chat-ui-root${t.fullPage?" full-page":""}`),this.shadowRoot.appendChild(this.root),this.i18n=new j(t.locale),this.themeManager=new y,this.themeManager.bind(this.root),t.theme&&this.themeManager.setTheme(t.theme),this.pluginManager=new x(this.i18n),this.messageListEl=r("div","chat-ui-message-list"),this.root.appendChild(this.messageListEl),this.messageListRenderer=new E(this.messageListEl,this.pluginManager,this.i18n,this.shadowRoot,this.getSenderInfo.bind(this)),this.inputWrapperEl=r("div"),this.root.appendChild(this.inputWrapperEl),this.inputArea=new M(this.inputWrapperEl,this.pluginManager,this.i18n,this.shadowRoot,this.handleSend.bind(this)),this.inputArea.setOnAppendMessage(n=>{this.messageListRenderer.appendMessage(n),this.messageListRenderer.scrollToBottom()}),this.inputArea.setDisabled(!0),t.plugins){for(const n of t.plugins)this.pluginManager.register(n);this.inputArea.refreshToolbar()}this.i18n.onChange(()=>{this.messageListRenderer.refreshTexts(),this.inputArea.refreshTexts()})}bindCore(e){this.core=e,this.inputArea.bindCore(e),e.on("message:received",t=>{console.log("[ChatUI] message:received 回调触发, clientMsgID:",t.clientMsgID,"sendID:",t.sendID),this.messageListRenderer.appendMessage(t)}),e.on("connection:success",()=>{this.inputArea.setDisabled(!1),this.hideOverlay()}),e.on("connection:failed",()=>{this.inputArea.setDisabled(!0),this.showErrorOverlay()}),e.on("connection:connecting",()=>{this.showLoadingOverlay()})}setUserInfo(e,t,i){this.currentUserID=e,this.userInfo={nickname:t,avatar:i},this.messageListRenderer.setCurrentUserID(e)}setAgentInfo(e,t,i){this.agentUserID=e,this.agentInfo={nickname:t,avatar:i},this.inputArea.setRecvID(e)}setGroupId(e){this.groupId=e,this.inputArea.setGroupId(e)}setMessages(e){this.messageListRenderer.updateMessages(e)}appendMessage(e){this.messageListRenderer.appendMessage(e)}setInputEnabled(e){this.inputArea.setDisabled(!e)}scrollToBottom(){this.messageListRenderer.scrollToBottom()}registerPlugin(e){this.pluginManager.register(e),this.inputArea.refreshToolbar()}unregisterPlugin(e){this.pluginManager.unregister(e),this.inputArea.refreshToolbar()}setLocale(e){this.i18n.setLocale(e)}setTheme(e){this.themeManager.setTheme(e)}getI18n(){return this.i18n}getShadowRoot(){return this.shadowRoot}destroy(){this.pluginManager.destroy(),this.i18n.destroy(),this.messageListRenderer.clear(),this.host.remove()}getSenderInfo(e){return e.sendID===this.currentUserID?this.userInfo:this.agentInfo}async handleSend(e){if(!this.core||!this.agentUserID)return;const t=!!this.groupId,i=t?this.groupId:this.agentUserID;try{const n=await this.core.message.sendText(i,e,t);n&&(this.messageListRenderer.appendMessage(n),this.messageListRenderer.scrollToBottom())}catch(n){console.error("[ChatUI] 发送消息失败:",n),this.inputArea.setDisabled(!1)}}showLoadingOverlay(){this.hideOverlay();const e=r("div","chat-ui-overlay");e.setAttribute("data-type","loading");const t=r("div","chat-ui-spinner"),i=r("span","chat-ui-overlay-text");i.textContent=this.i18n.t("status.connecting"),e.appendChild(t),e.appendChild(i),this.root.appendChild(e)}showErrorOverlay(){this.hideOverlay();const e=r("div","chat-ui-overlay");e.setAttribute("data-type","error");const t=r("div","chat-ui-error-icon");t.textContent="!";const i=r("span","chat-ui-overlay-text");i.textContent=this.i18n.t("status.disconnected");const n=r("button","chat-ui-reconnect-btn");n.textContent=this.i18n.t("status.reconnect"),n.addEventListener("click",()=>{this.host.dispatchEvent(new CustomEvent("reconnect"))}),e.appendChild(t),e.appendChild(i),e.appendChild(n),this.root.appendChild(e)}hideOverlay(){const e=this.root.querySelector(".chat-ui-overlay");e&&e.remove()}}class D{constructor(){this.contentTypes=[l.MessageType.TextMessage]}render(e,t){var n;const i=r("div","chat-ui-bubble");return i.textContent=((n=e.textElem)==null?void 0:n.content)||e.content||t.i18n.t("message.unknownType"),i}}class Y{constructor(){this.name="text",this.version="0.1.0"}install(e){e.registerRenderer(new D)}}const d=class d{static show(e,t){d.close(t);const i=r("div","chat-ui-image-preview-overlay");i.addEventListener("click",()=>d.close(t));const n=r("div","chat-ui-image-preview-container");n.addEventListener("click",h=>h.stopPropagation());const s=r("img","chat-ui-preview-image");s.src=e;const c=r("button","chat-ui-close-preview-btn");c.innerHTML="&times;",c.addEventListener("click",()=>d.close(t)),n.appendChild(s),n.appendChild(c),i.appendChild(n),t.appendChild(i),d.overlay=i}static close(e){d.overlay&&d.overlay.parentNode===e&&(e.removeChild(d.overlay),d.overlay=null)}};d.overlay=null;let m=d;function R(a){var e;if(Number(a.contentType)!==l.MessageType.CustomMessage||!((e=a.customElem)!=null&&e.data))return null;try{const t=JSON.parse(a.customElem.data);if(t.type==="image"&&t.url)return t}catch{}return null}function A(a,e=!0){var i,n,s;if(a.pictureElem){if(e&&((i=a.pictureElem.snapshotPicture)!=null&&i.url))return a.pictureElem.snapshotPicture.url;if((n=a.pictureElem.bigPicture)!=null&&n.url)return a.pictureElem.bigPicture.url;if((s=a.pictureElem.sourcePicture)!=null&&s.url)return a.pictureElem.sourcePicture.url}const t=R(a);return t?t.url:""}class B{constructor(){this.contentTypes=[l.MessageType.PictureMessage]}canRender(e){return Number(e.contentType)===l.MessageType.PictureMessage?!0:R(e)!==null}render(e,t){const i=r("div","chat-ui-bubble chat-ui-message-image"),n=A(e);if(n){const s=r("img","chat-ui-chat-image");s.src=n,s.setAttribute("referrerpolicy","no-referrer"),s.addEventListener("click",()=>{const c=A(e,!1);c&&m.show(c,t.shadowRoot)}),s.onerror=()=>{s.style.display="none";const c=r("span","chat-ui-image-error");c.textContent=t.i18n.t("message.imageError"),i.appendChild(c)},i.appendChild(s)}else{const s=r("span","chat-ui-image-error");s.textContent=t.i18n.t("message.imageError"),i.appendChild(s)}return i}}class Q{constructor(){this.name="image",this.version="0.1.0"}install(e){e.registerRenderer(new B),e.registerInputTool({name:"image-upload",icon:"🖼",tooltip:e.i18n.t("image.upload"),order:10,onActivate(t){const i=document.createElement("input");i.type="file",i.accept="image/*",i.style.display="none",i.addEventListener("change",async()=>{var c;const n=(c=i.files)==null?void 0:c[0];if(!n||!n.type.startsWith("image/")||n.size>10*1024*1024)return;const s=t.getRecvID();if(!(!s||!t.core)){try{const h=await X(n),p=await t.core.upload.uploadFile(n),v=await t.core.message.sendImage(s,n,p.absfileName,{width:h.width,height:h.height,size:n.size,type:n.type},t.isGroup());t.appendMessage(v)}catch(h){console.error("[ImagePlugin] 发送图片失败:",h)}i.remove()}}),document.body.appendChild(i),i.click()}})}}function X(a){return new Promise((e,t)=>{const i=new Image;i.onload=()=>{e({width:i.width,height:i.height}),URL.revokeObjectURL(i.src)},i.onerror=()=>{t(new Error("无法读取图片尺寸")),URL.revokeObjectURL(i.src)},i.src=URL.createObjectURL(a)})}const z=["😀","😃","😄","😁","😆","😅","🤣","😂","🙂","😊","😇","🥰","😍","🤩","😘","😗","😚","😋","😛","😜","🤪","😝","🤑","🤗","🤭","🤫","🤔","🤐","🤨","😐","😑","😶","😏","😒","🙄","😬","😮‍💨","🤥","😌","😔","😪","🤤","😴","😷","🤒","🤕","🤢","🤮","🤧","🥵","🥶","🥴","😵","🤯","🤠","🥳","😎","🤓","🧐","😕","😟","🙁","☹️","😮","😯","😲","😳","🥺","😦","😧","😨","😰","😥","😢","😭","😱","😖","😣","😞","😓","👍","👎","👌","🤌","🤏","✌️","🤞","🤟","🤘","🤙","👈","👉","👆","👇","☝️","👋","🤚","🖐️","✋","🖖","👏","🙌","🤝","🙏","❤️","🧡","💛","💚","💙","💜","🖤","🤍","💔","❣️","💕","💞","💓","💗","💖","💘","⭐","✨","🌟","💫","🔥","💯","✅","❌","❓","❗","💤","💬","👀","🎉","🎊","🎁"],b=[{nameKey:"emoji.categories.recent",start:0,end:32},{nameKey:"emoji.categories.emotion",start:32,end:56},{nameKey:"emoji.categories.gesture",start:56,end:80},{nameKey:"emoji.categories.heart",start:80,end:96},{nameKey:"emoji.categories.other",start:96,end:112}];class U{constructor(e,t,i,n){this.activeCategory=0,this.visible=!1,this.pickerEl=null,this.gridEl=null,this.categoryBtns=[],this.container=e,this.i18n=t,this.onSelect=i,this.onClose=n}toggle(){this.visible?this.hide():this.show()}show(){this.visible||(this.visible=!0,this.build())}hide(){this.visible&&(this.visible=!1,this.pickerEl&&(this.pickerEl.remove(),this.pickerEl=null),this.onClose())}isVisible(){return this.visible}refreshTexts(){this.visible&&this.hide()}build(){this.pickerEl=r("div","chat-ui-emoji-picker");const e=r("div","chat-ui-emoji-categories");this.categoryBtns=[],b.forEach((t,i)=>{const n=r("button",`chat-ui-category-btn${i===this.activeCategory?" active":""}`);n.textContent=this.i18n.t(t.nameKey),n.addEventListener("click",()=>{this.activeCategory=i,this.updateCategoryButtons(),this.renderGrid()}),this.categoryBtns.push(n),e.appendChild(n)}),this.pickerEl.appendChild(e),this.gridEl=r("div","chat-ui-emoji-grid"),this.renderGrid(),this.pickerEl.appendChild(this.gridEl),this.container.insertBefore(this.pickerEl,this.container.firstChild)}renderGrid(){if(!this.gridEl)return;u(this.gridEl);const e=b[this.activeCategory],t=z.slice(e.start,e.end);for(const i of t){const n=r("button","chat-ui-emoji-btn");n.textContent=i,n.addEventListener("click",()=>{this.onSelect(i),this.hide()}),this.gridEl.appendChild(n)}}updateCategoryButtons(){this.categoryBtns.forEach((e,t)=>{e.className=`chat-ui-category-btn${t===this.activeCategory?" active":""}`})}destroy(){this.hide()}}class Z{constructor(){this.name="emoji",this.version="0.1.0",this.picker=null}install(e){e.registerInputTool({name:"emoji",icon:"😀",tooltip:e.i18n.t("emoji.title"),order:0,onActivate:t=>{if(!this.picker){const i=t.inputElement.closest(".chat-ui-input-wrapper");if(!i)return;this.picker=new U(i,t.i18n,n=>{t.insertText(n)},()=>{})}this.picker.toggle()}})}uninstall(){var e;(e=this.picker)==null||e.destroy(),this.picker=null}}o.ChatUI=V,o.EmojiPicker=U,o.EmojiPlugin=Z,o.I18n=j,o.ImagePlugin=Q,o.ImagePreview=m,o.ImageRenderer=B,o.InputArea=M,o.MessageListRenderer=E,o.PluginManager=x,o.TextPlugin=Y,o.TextRenderer=D,o.ThemeManager=y,o.clearChildren=u,o.createAvatarElement=w,o.createElement=r,o.emojiCategories=b,o.emojiList=z,o.enUS=L,o.formatMessageTime=T,o.getAvatarText=I,o.isNearBottom=k,o.scrollToBottom=g,o.setTextContent=H,o.toggleClass=K,o.zhCN=S,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})}));
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@saiboniuma/chat-ui",
3
+ "version": "0.1.0",
4
+ "description": "Pluggable chat UI component library with Shadow DOM isolation",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "saibotianlong",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/make33136671/sb-framework-chat-js.git",
11
+ "directory": "packages/chat-ui"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public",
15
+ "registry": "https://registry.npmjs.org/"
16
+ },
17
+ "keywords": ["chat-ui", "realtime", "messaging", "customer-service", "shadow-dom"],
18
+ "sideEffects": false,
19
+ "main": "./dist/realtime-chat-ui.umd.cjs",
20
+ "module": "./dist/realtime-chat-ui.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/realtime-chat-ui.js",
26
+ "require": "./dist/realtime-chat-ui.umd.cjs"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "build": "vite build",
34
+ "type-check": "tsc --noEmit"
35
+ },
36
+ "dependencies": {
37
+ "@saiboniuma/realtime-core": "workspace:^"
38
+ },
39
+ "devDependencies": {
40
+ "typescript": "~5.6.3",
41
+ "vite": "^6.2.0",
42
+ "vite-plugin-dts": "^4.5.0"
43
+ }
44
+ }