2 lines
36 KiB
JavaScript
2 lines
36 KiB
JavaScript
import{j as e,r as m}from"./router-zNjPR4CY.js";import{u as R,B as $,g as j,a8 as se,a9 as te,aa as ae,S as J,I as re,E as Ce,D as He,i as qe,j as Ke,k as We,l as Ge,a5 as Je,L as ee,a3 as Qe,x as ye}from"./index-CuOHsLf7.js";import{u as M}from"./unified-ws-CBnrIqHW.js";import{T as Xe}from"./textarea-8PIujbf-.js";import{aR as Ye,d as Q,aS as Ze,L as ne,aT as es,aU as B,ay as ie,R as ss,X as Se,w as _e,b as ts,aV as as,aW as rs,a3 as ns,a1 as is,J as os,S as ls}from"./icons-DTcdLw9j.js";import{ac as Te,ad as Me,ae as Re}from"./radix-C-ZuImoP.js";import{S as cs,a as ds,b as us,c as ms,d as hs}from"./select-DGqIoF9r.js";import"./misc-BwRzHX8c.js";import"./utils-DjBw3JGv.js";function ps(s,t){const r=new Set([...Object.keys(s),...Object.keys(t)]);for(const l of r)if(s[l]!==t[l])return!1;return!0}class fs{initialized=!1;listeners=new Map;sessionPayloads=new Map;openedSessions=new Set;pendingOpens=new Map;initialize(){this.initialized||(M.addEventListener(t=>{if(t.domain!=="chat"||!t.session)return;const r=this.listeners.get(t.session);r&&r.forEach(l=>{try{l(t.data)}catch(o){console.error("聊天会话监听器执行失败:",o)}})}),M.onReconnect(()=>{this.openedSessions.clear(),this.pendingOpens.clear(),this.reopenSessions()}),M.onConnectionChange(t=>{t||(this.openedSessions.clear(),this.pendingOpens.clear())}),this.initialized=!0)}async reopenSessions(){const t=Array.from(this.sessionPayloads.entries());for(const[r,l]of t)try{await M.call({domain:"chat",method:"session.open",session:r,data:{...l,restore:!0}}),this.openedSessions.add(r)}catch(o){console.error(`恢复聊天会话失败 (${r}):`,o)}}async openSession(t,r){this.initialize();const l=this.sessionPayloads.get(t);this.sessionPayloads.set(t,r);const o=this.pendingOpens.get(t);if(o){await o;return}if(this.openedSessions.has(t)&&l!==void 0&&ps(l,r))return;const c=M.call({domain:"chat",method:"session.open",session:t,data:r}).then(()=>{this.openedSessions.add(t)});this.pendingOpens.set(t,c);try{await c}finally{this.pendingOpens.delete(t)}}async closeSession(t){if(this.sessionPayloads.delete(t),this.openedSessions.delete(t),this.pendingOpens.delete(t),M.getStatus()==="connected")try{await M.call({domain:"chat",method:"session.close",session:t,data:{}})}catch(r){console.warn(`关闭聊天会话失败 (${t}):`,r)}}async sendMessage(t,r,l){await M.call({domain:"chat",method:"message.send",session:t,data:{content:r,user_name:l}})}async updateNickname(t,r){const l=this.sessionPayloads.get(t);l&&this.sessionPayloads.set(t,{...l,user_name:r}),await M.call({domain:"chat",method:"session.update_nickname",session:t,data:{user_name:r}})}onSessionMessage(t,r){this.initialize();const l=this.listeners.get(t)??new Set;return l.add(r),this.listeners.set(t,l),()=>{const o=this.listeners.get(t);o&&(o.delete(r),o.size===0&&this.listeners.delete(t))}}onConnectionChange(t){return M.onConnectionChange(t)}onStatusChange(t){return M.onStatusChange(t)}async restart(){await M.restart()}}const I=new fs;function xs({value:s,onChange:t,onSend:r,disabled:l,isConnected:o}){const{t:c}=R(),h=i=>{i.key==="Enter"&&!i.shiftKey&&!i.nativeEvent.isComposing&&(i.preventDefault(),l||r())},b=!l&&s.trim().length>0;return e.jsx("div",{className:"bg-card/85 supports-backdrop-filter:bg-card/65 shrink-0 border-t backdrop-blur",children:e.jsxs("div",{className:"mx-auto max-w-4xl px-3 py-3 sm:px-6 sm:py-4",children:[e.jsxs("div",{className:j("group bg-background/80 focus-within:border-primary/60 focus-within:ring-primary/20 relative flex items-end gap-2 rounded-2xl border px-3 py-2 shadow-sm transition focus-within:ring-2",!o&&"opacity-70"),children:[e.jsx(Xe,{"aria-label":c("chat.input.placeholder"),autoResize:!0,className:"max-h-40 min-h-9 flex-1 resize-none border-0 bg-transparent px-1 py-1.5 text-sm shadow-none focus-visible:ring-0",disabled:!o,maxHeight:160,minHeight:36,placeholder:c(o?"chat.input.placeholder":"chat.input.waiting"),value:s,onChange:i=>t(i.target.value),onKeyDown:h}),e.jsx($,{"aria-label":c("chat.actions.send"),className:j("h-9 w-9 shrink-0 rounded-full transition",b?"shadow-md":"opacity-60"),disabled:!b,size:"icon",title:c("chat.actions.send"),onClick:r,children:e.jsx(Ye,{className:"h-4 w-4"})})]}),e.jsx("p",{className:"text-muted-foreground mt-1.5 hidden px-2 text-[11px] sm:block",children:c("chat.composer.hint")})]})})}const F=m.forwardRef(({className:s,...t},r)=>e.jsx(Te,{ref:r,className:j("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",s),...t}));F.displayName=Te.displayName;const Ue=m.forwardRef(({className:s,...t},r)=>e.jsx(Re,{ref:r,className:j("aspect-square h-full w-full",s),...t}));Ue.displayName=Re.displayName;const L=m.forwardRef(({className:s,...t},r)=>e.jsx(Me,{ref:r,className:j("flex h-full w-full items-center justify-center rounded-full bg-muted",s),...t}));L.displayName=Me.displayName;function gs({activeTab:s,botDisplayName:t,isConnecting:r,isLoadingHistory:l,onReconnect:o}){const{t:c}=R(),h=s?.type==="virtual",b=s?.virtualConfig,i=s?.isConnected??!1;return e.jsx("header",{className:"bg-card/85 supports-backdrop-filter:bg-card/65 relative z-1 shrink-0 border-b backdrop-blur",children:e.jsxs("div",{className:"flex items-center justify-between gap-3 px-4 py-3 sm:px-6 sm:py-4",children:[e.jsxs("div",{className:"flex min-w-0 items-center gap-3",children:[e.jsxs("div",{className:"relative shrink-0",children:[e.jsx(F,{className:"h-10 w-10 ring-1 ring-border/60 sm:h-11 sm:w-11",children:e.jsx(L,{className:"bg-primary-gradient text-primary-foreground",children:e.jsx(Q,{className:"h-5 w-5"})})}),e.jsx("span",{"aria-hidden":"true",className:j("absolute right-0 bottom-0 h-3 w-3 rounded-full border-2 border-card transition-colors",i?"bg-emerald-500":r?"bg-amber-500":"bg-muted-foreground/60")})]}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("h1",{className:"truncate text-sm font-semibold leading-tight sm:text-base",children:t}),e.jsxs("div",{className:"text-muted-foreground mt-0.5 flex items-center gap-1.5 text-xs leading-tight",children:[i?e.jsxs(e.Fragment,{children:[e.jsx(Ze,{className:"h-3 w-3 text-emerald-500"}),e.jsx("span",{children:c("chat.status.connected")})]}):r?e.jsxs(e.Fragment,{children:[e.jsx(ne,{className:"h-3 w-3 animate-spin"}),e.jsx("span",{children:c("chat.status.connecting")})]}):e.jsxs(e.Fragment,{children:[e.jsx(es,{className:"h-3 w-3 text-rose-500"}),e.jsx("span",{children:c("chat.status.disconnected")})]}),h&&b&&e.jsxs(e.Fragment,{children:[e.jsx("span",{"aria-hidden":!0,className:"text-muted-foreground/40",children:"·"}),e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx(B,{className:"h-3 w-3"}),e.jsx("span",{className:"max-w-40 truncate",children:b.userName})]}),e.jsx("span",{className:"bg-muted text-muted-foreground rounded-full px-1.5 py-0.5 text-[10px] font-medium",children:b.platform}),b.groupName&&e.jsxs("span",{className:"hidden items-center gap-1 sm:inline-flex",children:[e.jsx(ie,{className:"h-3 w-3"}),e.jsx("span",{className:"max-w-40 truncate",children:b.groupName})]})]})]})]})]}),e.jsxs("div",{className:"flex shrink-0 items-center gap-1",children:[l&&e.jsx(ne,{className:"text-muted-foreground h-4 w-4 animate-spin","aria-hidden":"true"}),e.jsxs(se,{children:[e.jsx(te,{asChild:!0,children:e.jsx($,{"aria-label":c("chat.actions.reconnect"),className:"h-9 w-9 rounded-full",disabled:r,size:"icon",variant:"ghost",onClick:o,children:e.jsx(ss,{className:j("h-4 w-4",r&&"animate-spin")})})}),e.jsx(ae,{side:"bottom",children:c("chat.actions.reconnect")})]})]})]})})}function bs({tabs:s,activeTabId:t,onSwitch:r,onClose:l,onAddVirtual:o}){const{t:c}=R();return e.jsx("div",{className:"bg-card/85 supports-backdrop-filter:bg-card/65 shrink-0 border-b backdrop-blur",children:e.jsxs("div",{className:"scrollbar-thin flex items-center gap-1 overflow-x-auto px-3 py-2",children:[s.map(h=>{const b=t===h.id,i=h.type==="virtual"?B:Q;return e.jsxs("div",{className:j("group flex shrink-0 items-center rounded-full border text-xs transition",b?"bg-primary text-primary-foreground border-transparent shadow-sm":"bg-background/60 text-muted-foreground hover:text-foreground hover:bg-background border-transparent"),children:[e.jsxs("button",{type:"button",className:"flex items-center gap-1.5 rounded-full px-3 py-1.5",onClick:()=>r(h.id),children:[e.jsx(i,{className:"h-3.5 w-3.5"}),e.jsx("span",{className:"max-w-32 truncate font-medium",children:h.label}),e.jsx("span",{"aria-hidden":!0,className:j("h-1.5 w-1.5 rounded-full transition-colors",b?h.isConnected?"bg-primary-foreground":"bg-primary-foreground/50":h.isConnected?"bg-emerald-500":"bg-muted-foreground/40")})]}),h.id!=="webui-default"&&e.jsx("button",{type:"button","aria-label":c("chat.sidebar.closeConversation",{label:h.label}),className:j("mr-1 rounded-full p-0.5 transition",b?"hover:bg-primary-foreground/20":"hover:bg-muted"),onClick:k=>l(h.id,k),children:e.jsx(Se,{className:"h-3 w-3"})})]},h.id)}),e.jsx("button",{type:"button","aria-label":c("chat.sidebar.newVirtual"),title:c("chat.sidebar.newVirtual"),className:"text-muted-foreground hover:bg-muted hover:text-foreground flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-dashed transition",onClick:o,children:e.jsx(_e,{className:"h-3.5 w-3.5"})})]})})}function vs(s,t){return s?(s.type==="system"||s.type==="error",s.content||t):t}function ws({tab:s,active:t,onSwitch:r,onClose:l}){const{t:o}=R(),c=s.type==="virtual",h=s.messages[s.messages.length-1],b=vs(h,o("chat.sidebar.emptyPreview")),i=c?B:Q;return e.jsxs("div",{className:j("group relative flex w-full min-w-0 items-center gap-1 rounded-xl pr-1 transition-colors",t?"bg-primary/12 text-foreground shadow-inner":"hover:bg-muted/70 text-foreground/90"),children:[t&&e.jsx("span",{"aria-hidden":!0,className:"bg-primary absolute top-2 bottom-2 left-0 w-1 rounded-full"}),e.jsxs("button",{type:"button",className:"flex w-full min-w-0 flex-1 items-center gap-3 overflow-hidden rounded-xl px-2.5 py-2 text-left",onClick:()=>r(s.id),children:[e.jsxs("div",{className:"relative shrink-0",children:[e.jsx(F,{className:"h-11 w-11 ring-1 ring-border/60",children:e.jsx(L,{className:j("text-xs",c?"bg-secondary text-secondary-foreground":"bg-primary-gradient text-primary-foreground"),children:e.jsx(i,{className:"h-5 w-5"})})}),e.jsx("span",{"aria-hidden":!0,className:j("border-card absolute right-0 bottom-0 h-3 w-3 rounded-full border-2 transition-colors",s.isConnected?"bg-emerald-500":"bg-muted-foreground/40")})]}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex min-w-0 items-center justify-between gap-2",children:[e.jsx("span",{className:"min-w-0 flex-1 truncate text-sm font-medium",children:s.label}),e.jsx("span",{className:j("shrink-0 rounded-full px-1.5 py-0.5 text-[10px] font-medium tracking-wide",c?"bg-secondary text-secondary-foreground":"bg-primary/15 text-primary"),children:o(c?"chat.sidebar.virtualBadge":"chat.sidebar.webuiBadge")})]}),e.jsx("p",{className:"text-muted-foreground mt-0.5 truncate text-xs",children:b})]})]}),s.id!=="webui-default"&&e.jsx("button",{type:"button","aria-label":o("chat.sidebar.closeConversation",{label:s.label}),className:"text-muted-foreground hover:bg-background hover:text-foreground rounded-md p-1 opacity-0 transition group-hover:opacity-100 focus-visible:opacity-100",onClick:k=>l(s.id,k),children:e.jsx(Se,{className:"h-3.5 w-3.5"})})]})}function js({className:s,tabs:t,activeTabId:r,userName:l,onSwitch:o,onClose:c,onAddVirtual:h,onUpdateUserName:b}){const{t:i}=R(),[k,_]=m.useState(!1),[P,y]=m.useState(l),d=()=>{y(l),_(!0)},N=()=>{const x=P.trim()||i("chat.userNameFallback");b(x),_(!1)};return e.jsxs("aside",{className:j("bg-card/90 supports-backdrop-filter:bg-card/70 flex h-full w-72 shrink-0 flex-col border-r backdrop-blur xl:w-80",s),children:[e.jsx("div",{className:"border-b px-4 pt-5 pb-4",children:e.jsxs("div",{className:"flex items-end justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("h2",{className:"truncate text-lg font-semibold tracking-tight",children:i("chat.sidebar.title")}),e.jsx("p",{className:"text-muted-foreground mt-0.5 truncate text-xs",children:i("chat.sidebar.subtitle",{count:t.length})})]}),e.jsxs(se,{children:[e.jsx(te,{asChild:!0,children:e.jsx($,{"aria-label":i("chat.sidebar.newVirtual"),className:"h-9 w-9 shrink-0 rounded-full shadow-sm",size:"icon",onClick:h,children:e.jsx(_e,{className:"h-4 w-4"})})}),e.jsx(ae,{side:"bottom",children:i("chat.sidebar.newVirtual")})]})]})}),e.jsx(J,{className:"min-h-0 flex-1",contentClassName:"!block w-full min-w-0",scrollbars:"vertical",viewportClassName:"[&>div]:!block [&>div]:!min-w-0 [&>div]:w-full",children:e.jsx("nav",{"aria-label":i("chat.sidebar.conversations"),className:"space-y-0.5 p-2",children:t.map(x=>e.jsx(ws,{active:r===x.id,tab:x,onSwitch:o,onClose:c},x.id))})}),e.jsx("div",{className:"border-t p-3",children:e.jsxs("div",{className:"bg-background/70 hover:bg-background flex items-center gap-3 rounded-xl border p-2.5 transition-colors",children:[e.jsx(F,{className:"h-10 w-10 shrink-0 ring-1 ring-border/60",children:e.jsx(L,{className:"bg-secondary text-secondary-foreground",children:e.jsx(B,{className:"h-5 w-5"})})}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("p",{className:"text-muted-foreground text-[11px] uppercase tracking-wide",children:i("chat.sidebar.profileTitle")}),k?e.jsxs("div",{className:"mt-0.5 flex items-center gap-1",children:[e.jsx(re,{autoFocus:!0,className:"h-7 text-sm",placeholder:i("chat.identity.namePlaceholder"),value:P,onChange:x=>y(x.target.value),onKeyDown:x=>{x.key==="Enter"&&!x.nativeEvent.isComposing?(x.preventDefault(),N()):x.key==="Escape"&&_(!1)}}),e.jsx($,{"aria-label":i("chat.sidebar.saveName"),className:"h-7 w-7 shrink-0",size:"icon",variant:"ghost",onClick:N,children:e.jsx(ts,{className:"h-3.5 w-3.5"})})]}):e.jsxs("div",{className:"flex min-w-0 items-center gap-1",children:[e.jsx("p",{className:"min-w-0 flex-1 truncate text-sm font-medium",children:l}),e.jsxs(se,{children:[e.jsx(te,{asChild:!0,children:e.jsx($,{"aria-label":i("chat.sidebar.editName"),className:"h-6 w-6 shrink-0 opacity-60 hover:opacity-100",size:"icon",variant:"ghost",onClick:d,children:e.jsx(as,{className:"h-3 w-3"})})}),e.jsx(ae,{side:"top",children:i("chat.sidebar.editName")})]})]})]})]})})]})}const Ie=m.createContext(null);function ys(){return m.useContext(Ie)}function Ns(s){return s.data&&typeof s.data=="object"?s.data:{target_user_id:s.data==null?null:String(s.data)}}function ks(s){return s.data&&typeof s.data=="object"?s.data:{target_message_id:s.data==null?null:String(s.data)}}function Ne({segment:s}){const{t}=R();switch(s.type){case"text":return e.jsx("span",{className:"whitespace-pre-wrap",children:String(s.data)});case"image":case"emoji":{const r=s.type==="emoji"?t("chat.media.emoji"):t("chat.media.image");return e.jsx("img",{src:String(s.data),alt:r,className:j("max-w-full rounded-lg",s.type==="emoji"?"max-h-32":"max-h-64"),loading:"lazy",onError:l=>{const o=l.target;o.style.display="none";const c=document.createElement("span");c.className="text-muted-foreground text-xs",c.textContent=t("chat.media.loadFailed",{type:r}),o.parentElement?.appendChild(c)}})}case"voice":return e.jsx("div",{className:"flex items-center gap-2",children:e.jsxs("audio",{controls:!0,src:String(s.data),className:"h-8 max-w-[200px]",children:[e.jsx("track",{kind:"captions",src:"",label:t("chat.media.noCaptions"),default:!0}),t("chat.media.audioUnsupported")]})});case"video":return e.jsxs("video",{controls:!0,src:String(s.data),className:"max-h-64 max-w-full rounded-lg",children:[e.jsx("track",{kind:"captions",src:"",label:t("chat.media.noCaptions"),default:!0}),t("chat.media.videoUnsupported")]});case"face":return e.jsx("span",{className:"text-muted-foreground",children:t("chat.media.face",{data:String(s.data)})});case"music":return e.jsx("span",{className:"text-muted-foreground",children:t("chat.media.music")});case"file":return e.jsx("span",{className:"text-muted-foreground",children:t("chat.media.file",{data:String(s.data)})});case"reply":{const r=ks(s);return e.jsx(Ss,{data:r})}case"at":{const r=Ns(s),l=r.target_user_cardname||r.target_user_nickname||r.target_user_id||"";return e.jsxs("span",{className:"text-primary bg-primary/10 mx-0.5 inline-flex items-center rounded px-1 text-[0.95em] font-medium",title:r.target_user_id?`@${r.target_user_id}`:"@",children:["@",l||t("chat.media.unknownMessage")]})}case"forward":return e.jsx("span",{className:"text-muted-foreground",children:t("chat.media.forward")});default:return e.jsx("span",{className:"text-muted-foreground",children:t("chat.media.unknown",{type:s.original_type||t("chat.media.unknownMessage")})})}}function Cs({message:s,isBot:t}){if(s.message_type==="rich"&&s.segments&&s.segments.length>0){const r=[],l=[];for(const o of s.segments)o.type==="reply"?l.push(o):r.push(o);return e.jsxs("div",{className:"flex flex-col gap-2",children:[l.map((o,c)=>e.jsx(Ne,{segment:o},`reply-${c}`)),r.length>0&&e.jsx("div",{className:"flex flex-wrap items-baseline whitespace-pre-wrap",children:r.map((o,c)=>e.jsx(Ne,{segment:o},`inline-${c}`))})]})}return e.jsx("span",{className:"whitespace-pre-wrap",children:s.content})}function Ss({data:s}){const{t}=R(),{toast:r}=Ce(),l=ys(),o=s.target_message_sender_cardname||s.target_message_sender_nickname||s.target_message_sender_id||t("chat.message.replyUnknownSender",{defaultValue:"未知发送者"}),c=s.target_message_content?.trim()||t("chat.media.replyMissing",{defaultValue:"原消息内容不可用"}),h=s.target_message_id?String(s.target_message_id):"",b=!!(h&&l),i=()=>{if(!h||!l)return;l.scrollToMessage(h)||r({title:t("chat.toast.replyNotFoundTitle",{defaultValue:"原始消息不在当前视图"}),description:t("chat.toast.replyNotFoundDescription",{defaultValue:"该消息可能已被清除、不在本会话中,或者尚未加载。"}),variant:"destructive"})},k=j("group block w-full rounded-md border-l-2 border-primary/60 bg-background/40 px-2 py-1 text-left text-xs",b&&"cursor-pointer transition hover:bg-background/70"),_=e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(rs,{className:"mt-0.5 h-3 w-3 shrink-0 opacity-70","aria-hidden":!0}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("div",{className:"text-primary/80 truncate text-[11px] font-medium",children:o}),e.jsx("div",{className:"text-muted-foreground truncate",children:c})]})]});return b?e.jsx("button",{type:"button",className:k,onClick:i,children:_}):e.jsx("div",{className:k,children:_})}function _s({type:s,visible:t,imageUrl:r}){return e.jsx("div",{className:"h-8 w-8 shrink-0 sm:h-9 sm:w-9",children:t&&e.jsxs(F,{className:"h-full w-full ring-1 ring-border/60",children:[s==="bot"&&r?e.jsx(Ue,{src:r,alt:"",className:"object-cover"}):null,e.jsx(L,{className:j("text-xs",s==="user"?"bg-secondary text-secondary-foreground":"bg-primary-gradient text-primary-foreground"),children:s==="user"?e.jsx(is,{className:"h-4 w-4"}):e.jsx(Q,{className:"h-4 w-4"})})]})})}function Ts({botName:s}){const{t}=R();return e.jsxs("div",{className:"flex h-full flex-col items-center justify-center gap-3 px-6 py-16 text-center",children:[e.jsxs("div",{className:"bg-primary-gradient text-primary-foreground relative flex h-16 w-16 items-center justify-center rounded-2xl shadow-lg",children:[e.jsx(ns,{className:"h-7 w-7"}),e.jsx("span",{className:"bg-primary/30 absolute inset-0 -z-10 animate-pulse rounded-2xl blur-xl"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("h2",{className:"text-base font-semibold sm:text-lg",children:t("chat.message.empty",{bot:s})}),e.jsx("p",{className:"text-muted-foreground text-xs sm:text-sm",children:t("chat.message.emptyHint")})]})]})}function Ms({messages:s,isLoadingHistory:t,botDisplayName:r,botQq:l,userName:o,language:c}){const{t:h}=R(),b=m.useRef(null),i=m.useRef(new Map);m.useEffect(()=>{b.current?.scrollIntoView({behavior:"smooth"})},[s]);const k=m.useCallback(d=>{const N=i.current.get(d);return N?(N.scrollIntoView({behavior:"smooth",block:"center"}),N.classList.add("chat-message-flash"),window.setTimeout(()=>{N.classList.remove("chat-message-flash")},1600),!0):!1},[]),_=m.useMemo(()=>({scrollToMessage:k}),[k]),P=d=>new Date(d*1e3).toLocaleTimeString(c||"zh-CN",{hour:"2-digit",minute:"2-digit"}),y=l&&/^\d+$/.test(l)?`https://q1.qlogo.cn/g?b=qq&nk=${l}&s=640`:void 0;return s.length===0&&!t?e.jsx("div",{className:"min-w-0 min-h-0 flex-1 overflow-hidden",children:e.jsx(J,{className:"h-full w-full",contentClassName:"!block w-full min-w-0",scrollbars:"vertical",viewportClassName:"[&>div]:!block [&>div]:!min-w-0 [&>div]:w-full",children:e.jsx(Ts,{botName:r})})}):e.jsx("div",{className:"min-w-0 min-h-0 flex-1 overflow-hidden",children:e.jsx(J,{className:"h-full w-full",contentClassName:"!block w-full min-w-0",scrollbars:"vertical",viewportClassName:"[&>div]:!block [&>div]:!min-w-0 [&>div]:w-full",children:e.jsx(Ie.Provider,{value:_,children:e.jsxs("div",{className:"mx-auto flex w-full max-w-4xl min-w-0 flex-col gap-1 px-3 py-5 sm:px-6 sm:py-6",children:[s.map((d,N)=>{if(d.type==="system")return e.jsxs("div",{className:"my-2 flex items-center gap-3",children:[e.jsx("div",{className:"bg-border/60 h-px flex-1"}),e.jsx("span",{className:"text-muted-foreground bg-card/70 rounded-full border px-3 py-0.5 text-[11px]",children:d.content}),e.jsx("div",{className:"bg-border/60 h-px flex-1"})]},d.id);if(d.type==="error")return e.jsx("div",{className:"my-2 flex justify-center",children:e.jsx("div",{className:"bg-destructive/10 text-destructive border-destructive/30 rounded-full border px-3 py-1 text-xs",children:d.content})},d.id);const x=d.type==="user",H=x?"user":"bot",z=s[N-1],D=z&&z.type===d.type&&(z.sender?.user_id??z.sender?.name)===(d.sender?.user_id??d.sender?.name),X=d.sender?.name||(x?o:r);return e.jsxs("div",{ref:q=>{q?i.current.set(d.id,q):i.current.delete(d.id)},"data-message-id":d.id,className:j("chat-message-row flex w-full min-w-0 items-end gap-2 sm:gap-3",x?"flex-row-reverse":"flex-row",D?"mt-0.5":"mt-3 first:mt-0"),children:[e.jsx(_s,{type:H,visible:!D,imageUrl:H==="bot"?y:void 0}),e.jsxs("div",{className:j("flex min-w-0 max-w-[80%] flex-col sm:max-w-[70%]",x?"items-end":"items-start"),children:[!D&&e.jsxs("div",{className:j("text-muted-foreground mb-1 flex items-center gap-2 px-1 text-[11px]",x&&"flex-row-reverse"),children:[e.jsx("span",{className:"hidden font-medium sm:inline",children:X}),e.jsx("span",{children:P(d.timestamp)})]}),e.jsx("div",{className:j("shadow-sm/30 wrap-break-word min-w-0 max-w-full overflow-hidden px-3.5 py-2 text-sm leading-relaxed",x?"bg-primary text-primary-foreground rounded-2xl rounded-br-md":"bg-muted text-foreground rounded-2xl rounded-bl-md"),children:e.jsx(Cs,{message:d,isBot:!x})})]})]},d.id)}),e.jsx("div",{ref:b}),e.jsx("span",{className:"sr-only","aria-live":"polite",children:s.length>0?h("chat.sidebar.subtitle",{count:s.length}):""})]})})})})}const Pe="maibot_webui_virtual_tabs";function Rs(){return"webui_"+Math.random().toString(36).substr(2,9)+"_"+Date.now().toString(36)}function Us(){const s="maibot_webui_user_id";let t=localStorage.getItem(s);return t||(t=Rs(),localStorage.setItem(s,t)),t}function Is(){return localStorage.getItem("maibot_webui_user_name")||"WebUI用户"}function Ps(s){localStorage.setItem("maibot_webui_user_name",s)}function Ds(){try{const s=localStorage.getItem(Pe);if(s)return JSON.parse(s)}catch(s){console.error("[Chat] 加载虚拟标签页失败:",s)}return[]}function ke(s){try{localStorage.setItem(Pe,JSON.stringify(s))}catch(t){console.error("[Chat] 保存虚拟标签页失败:",t)}}function Es({open:s,onOpenChange:t,platforms:r,persons:l,isLoadingPlatforms:o,isLoadingPersons:c,personSearchQuery:h,setPersonSearchQuery:b,tempVirtualConfig:i,setTempVirtualConfig:k,onSelectPerson:_,onCreateVirtualTab:P}){const{t:y}=R();return e.jsx(He,{open:s,onOpenChange:t,children:e.jsxs(qe,{className:"flex max-h-[85vh] flex-col overflow-hidden sm:max-w-125",confirmOnEnter:!0,children:[e.jsxs(Ke,{children:[e.jsxs(We,{className:"flex items-center gap-2",children:[e.jsx(B,{className:"h-5 w-5"}),y("chat.dialog.title")]}),e.jsx(Ge,{children:y("chat.dialog.description")})]}),e.jsxs(Je,{className:"flex-1 space-y-4",viewportClassName:"pr-0",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsxs(ee,{className:"flex items-center gap-2",children:[e.jsx(os,{className:"h-4 w-4"}),y("chat.dialog.platform")]}),e.jsxs(cs,{value:i.platform,onValueChange:d=>{k(N=>({...N,platform:d,personId:"",userId:"",userName:""}))},children:[e.jsx(ds,{disabled:o,children:e.jsx(us,{placeholder:y(o?"chat.dialog.loading":"chat.dialog.platformPlaceholder")})}),e.jsx(ms,{children:r.map(d=>e.jsxs(hs,{value:d.platform,children:[d.platform," ",y("chat.dialog.personCount",{count:d.count})]},d.platform))})]})]}),i.platform&&e.jsxs("div",{className:"flex flex-1 flex-col space-y-2 overflow-hidden",children:[e.jsxs(ee,{className:"flex items-center gap-2",children:[e.jsx(ie,{className:"h-4 w-4"}),y("chat.dialog.user")]}),e.jsxs("div",{className:"relative",children:[e.jsx(ls,{className:"text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2"}),e.jsx(re,{placeholder:y("chat.dialog.searchUser"),value:h,onChange:d=>b(d.target.value),className:"pl-9"})]}),e.jsx(J,{className:"bg-background/40 h-62.5 rounded-lg border",children:e.jsx("div",{className:"p-1.5",children:c?e.jsx("div",{className:"flex items-center justify-center py-10",children:e.jsx(ne,{className:"text-muted-foreground h-6 w-6 animate-spin"})}):l.length===0?e.jsxs("div",{className:"text-muted-foreground flex flex-col items-center justify-center py-10",children:[e.jsx(ie,{className:"mb-2 h-8 w-8 opacity-50"}),e.jsx("p",{className:"text-sm",children:y("chat.dialog.noUsers")})]}):e.jsx("div",{className:"space-y-0.5",children:l.map(d=>{const N=i.personId===d.person_id,x=d.nickname||d.person_name;return e.jsxs("button",{type:"button",onClick:()=>_(d),className:j("flex w-full items-center gap-3 rounded-md p-2 text-left transition-colors",N?"bg-primary/12 text-foreground":"hover:bg-muted/70"),children:[e.jsx(F,{className:"h-9 w-9 shrink-0 ring-1 ring-border/60",children:e.jsx(L,{className:j("text-xs font-semibold",N?"bg-primary-gradient text-primary-foreground":"bg-muted text-foreground"),children:(x||"?").charAt(0).toUpperCase()})}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:"truncate text-sm font-medium",children:x}),d.is_known&&e.jsx("span",{className:"bg-emerald-500/15 text-emerald-600 dark:text-emerald-400 rounded-full px-1.5 py-0.5 text-[10px] font-medium",children:y("chat.dialog.knownUserSuffix").replace(/^\s*·\s*/,"")})]}),e.jsx("div",{className:"text-muted-foreground truncate font-mono text-[11px]",children:d.user_id})]})]},d.person_id)})})})})]}),i.personId&&e.jsxs("div",{className:"space-y-2",children:[e.jsx(ee,{children:y("chat.dialog.groupName")}),e.jsx(re,{placeholder:y("chat.virtualGroupFallback"),value:i.groupName,onChange:d=>k(N=>({...N,groupName:d.target.value}))}),e.jsx("p",{className:"text-muted-foreground text-xs",children:y("chat.dialog.groupNameHint")})]})]}),e.jsxs(Qe,{className:"gap-2 sm:gap-0",children:[e.jsx($,{variant:"outline",onClick:()=>t(!1),children:y("chat.actions.cancel")}),e.jsx($,{"data-dialog-action":"confirm",onClick:P,disabled:!i.platform||!i.personId,children:y("chat.dialog.create")})]})]})})}function qs(){const{t:s,i18n:t}=R(),r={id:"webui-default",type:"webui",label:s("chat.defaultTab"),messages:[],isConnected:!1,isTyping:!1,sessionInfo:{}},l=()=>{const f=Ds().map(u=>{const n=u.virtualConfig;return!n.groupId&&n.platform&&n.userId&&(n.groupId=`webui_virtual_group_${n.platform}_${n.userId}`),{id:u.id,type:"virtual",label:u.label,virtualConfig:n,messages:[],isConnected:!1,isTyping:!1,sessionInfo:{}}});return[r,...f]},[o,c]=m.useState(l),[h,b]=m.useState("webui-default"),i=o.find(a=>a.id===h)||o[0],[k,_]=m.useState(""),[P,y]=m.useState(!1),[d,N]=m.useState(!0),[x,H]=m.useState(Is()),[z,D]=m.useState(!1),[X,q]=m.useState([]),[De,Ee]=m.useState([]),[$e,oe]=m.useState(!1),[ze,le]=m.useState(!1),[Y,ce]=m.useState(""),[S,Z]=m.useState({platform:"",personId:"",userId:"",userName:"",groupName:"",groupId:""}),de=m.useRef(Us()),ue=m.useRef(0),U=m.useRef(new Map),A=m.useRef(new Map),me=m.useRef([]),{toast:T}=Ce();m.useEffect(()=>{me.current=o},[o]);const V=a=>(ue.current+=1,`${a}-${Date.now()}-${ue.current}-${Math.random().toString(36).substr(2,9)}`),E=m.useCallback((a,f)=>{c(u=>u.map(n=>n.id===a?{...n,...f}:n))},[]),O=m.useCallback((a,f)=>{c(u=>u.map(n=>n.id===a?{...n,messages:[...n.messages,f]}:n))},[]),Ve=m.useCallback(async()=>{oe(!0);try{const a=await ye("/api/chat/platforms");if(console.log("[Chat] 平台列表响应:",a.status,a.headers.get("content-type")),a.ok){const f=a.headers.get("content-type");if(f&&f.includes("application/json")){const u=await a.json();console.log("[Chat] 平台列表数据:",u),q(u.platforms||[])}else{const u=await a.text();console.error("[Chat] 获取平台列表失败: 非 JSON 响应:",u.substring(0,200)),T({title:s("chat.toast.connectionFailed"),description:s("chat.toast.backendUnavailable"),variant:"destructive"})}}else console.error("[Chat] 获取平台列表失败: HTTP",a.status),T({title:s("chat.toast.platformFailed"),description:s("chat.toast.serverError",{status:a.status}),variant:"destructive"})}catch(a){console.error("[Chat] 获取平台列表失败:",a),T({title:s("chat.toast.networkError"),description:s("chat.toast.backendUnavailableShort"),variant:"destructive"})}finally{oe(!1)}},[s,T]),he=m.useCallback(async(a,f)=>{le(!0);try{const u=new URLSearchParams;a&&u.append("platform",a),f&&u.append("search",f),u.append("limit","50");const n=await ye(`/api/chat/persons?${u.toString()}`);if(n.ok){const p=n.headers.get("content-type");if(p&&p.includes("application/json")){const w=await n.json();Ee(w.persons||[])}else console.error("[Chat] 获取用户列表失败: 后端返回非 JSON 响应")}}catch(u){console.error("[Chat] 获取用户列表失败:",u)}finally{le(!1)}},[]);m.useEffect(()=>{S.platform&&he(S.platform,Y)},[S.platform,Y,he]);const pe=m.useCallback((a,f,u,n)=>{switch(n.type){case"session_info":E(a,{sessionInfo:{session_id:n.session_id,user_id:n.user_id,user_name:n.user_name,bot_name:n.bot_name,bot_qq:n.bot_qq}});break;case"system":O(a,{id:V("sys"),type:"system",content:n.content||"",timestamp:n.timestamp||Date.now()/1e3});break;case"user_message":{const p=n.sender?.user_id,w=f==="virtual"&&u?u.userId:de.current,v=p?p.replace(/^webui_user_/,""):"",g=w?w.replace(/^webui_user_/,""):"";if(v&&g&&v===g)break;const C=U.current.get(a)||new Set,W=`user-${n.content}-${Math.floor((n.timestamp||0)*1e3)}`;if(C.has(W))break;if(C.add(W),U.current.set(a,C),C.size>100){const G=C.values().next().value;G&&C.delete(G)}O(a,{id:n.message_id||V("user"),type:"user",content:n.content||"",timestamp:n.timestamp||Date.now()/1e3,sender:n.sender});break}case"bot_message":{E(a,{isTyping:!1});const p=U.current.get(a)||new Set,w=`bot-${n.content}-${Math.floor((n.timestamp||0)*1e3)}`;if(p.has(w))break;if(p.add(w),U.current.set(a,p),p.size>100){const v=p.values().next().value;v&&p.delete(v)}c(v=>v.map(g=>{if(g.id!==a)return g;const C={id:V("bot"),type:"bot",content:n.content||"",message_type:n.message_type==="rich"?"rich":"text",segments:n.segments,timestamp:n.timestamp||Date.now()/1e3,sender:n.sender};return{...g,messages:[...g.messages,C]}}));break}case"typing":E(a,{isTyping:n.is_typing||!1});break;case"error":c(p=>p.map(w=>w.id!==a?w:{...w,messages:[...w.messages,{id:V("error"),type:"error",content:n.content||s("chat.message.errorFallback"),timestamp:n.timestamp||Date.now()/1e3}]})),T({title:s("chat.toast.error"),description:n.content,variant:"destructive"});break;case"history":{const p=n.messages||[],w=new Set,v=p.map(g=>{const C=g.is_bot||!1,W=g.id||V(C?"bot":"user"),G=`${C?"bot":"user"}-${g.content}-${Math.floor(g.timestamp*1e3)}`;w.add(G);const je=g.message_type==="rich"&&Array.isArray(g.segments)&&g.segments.length>0;return{id:W,type:C?"bot":"user",content:g.content,timestamp:g.timestamp,message_type:je?"rich":"text",segments:je?g.segments??void 0:void 0,sender:{name:g.sender_name||s(C?"chat.botNameFallback":"chat.userFallback"),user_id:g.sender_id,is_bot:C}}});U.current.set(a,w),E(a,{messages:v}),N(!1);break}}},[O,s,T,E]),fe=m.useCallback((a,f,u)=>{if(A.current.has(a))return;const n=I.onSessionMessage(a,p=>{pe(a,f,u,p)});A.current.set(a,n)},[pe]),xe=m.useCallback(async(a,f,u)=>{fe(a,f,u),N(!0);try{f==="virtual"&&u?await I.openSession(a,{user_id:u.userId,user_name:u.userName,platform:u.platform,person_id:u.personId,group_name:u.groupName||s("chat.virtualGroupFallback"),group_id:u.groupId}):await I.openSession(a,{user_id:de.current,user_name:x}),E(a,{isConnected:!0})}catch(n){console.error(`[Tab ${a}] 打开聊天会话失败:`,n),N(!1),T({title:s("chat.toast.connectionFailed"),description:s("chat.toast.sessionUnavailable"),variant:"destructive"})}},[fe,s,T,E,x]),K=m.useRef(!1);m.useEffect(()=>{K.current=!1;const a=A.current,f=me,u=I.onConnectionChange(p=>{K.current||c(w=>w.map(v=>({...v,isConnected:p})))}),n=I.onStatusChange(p=>{K.current||y(p==="connecting")});return o.forEach(p=>{U.current.set(p.id,new Set),xe(p.id,p.type,p.virtualConfig)}),()=>{K.current=!0,u(),n(),a.forEach(p=>{p()}),a.clear(),f.current.forEach(p=>{I.closeSession(p.id)})}},[]);const Fe=m.useCallback(async()=>{if(!k.trim()||!i?.isConnected)return;const a=i?.type==="virtual"&&i.virtualConfig?.userName||x,f=k.trim(),u=Date.now()/1e3,n=U.current.get(h)||new Set,p=`user-${f}-${Math.floor(u*1e3)}`;if(n.add(p),U.current.set(h,n),n.size>100){const v=n.values().next().value;v&&n.delete(v)}const w={id:V("user"),type:"user",content:f,timestamp:u,sender:{name:a,is_bot:!1}};O(h,w),_("");try{await I.sendMessage(h,f,a)}catch(v){console.error("发送聊天消息失败:",v),c(g=>g.map(C=>C.id!==h?C:{...C,isTyping:!1})),T({title:s("chat.toast.sendFailed"),description:s("chat.toast.currentSessionUnavailable"),variant:"destructive"})}},[i,h,O,k,s,T,x]),Le=m.useCallback(a=>{const f=a.trim()||s("chat.userNameFallback");H(f),Ps(f),i?.isConnected&&I.updateNickname(h,f)},[i?.isConnected,h,s]),Ae=()=>{I.restart()},ge=()=>{Z({platform:"",personId:"",userId:"",userName:"",groupName:"",groupId:""}),ce(""),Ve(),D(!0)},Oe=()=>{if(!S.platform||!S.personId){T({title:s("chat.toast.incompleteConfig"),description:s("chat.toast.selectPlatformAndUser"),variant:"destructive"});return}const a=`webui_virtual_group_${S.platform}_${S.userId}`,f=`virtual-${S.platform}-${S.userId}-${Date.now()}`,u=S.userName||S.userId,n={id:f,type:"virtual",label:u,virtualConfig:{...S,groupId:a},messages:[],isConnected:!1,isTyping:!1,sessionInfo:{}};c(p=>{const w=[...p,n],v=w.filter(g=>g.type==="virtual"&&g.virtualConfig).map(g=>({id:g.id,label:g.label,virtualConfig:g.virtualConfig,createdAt:Date.now()}));return ke(v),w}),b(f),D(!1),U.current.set(f,new Set),xe(f,"virtual",{...S,groupId:a}),T({title:s("chat.toast.virtualTabCreated"),description:s("chat.toast.virtualTabCreatedDesc",{label:u})})},be=(a,f)=>{if(f?.stopPropagation(),a==="webui-default")return;const u=A.current.get(a);u&&(u(),A.current.delete(a)),I.closeSession(a),U.current.delete(a),c(n=>{const p=n.filter(v=>v.id!==a),w=p.filter(v=>v.type==="virtual"&&v.virtualConfig).map(v=>({id:v.id,label:v.label,virtualConfig:v.virtualConfig,createdAt:Date.now()}));return ke(w),p}),h===a&&b("webui-default")},ve=a=>{b(a)},Be=a=>{Z(f=>({...f,personId:a.person_id,userId:a.user_id,userName:a.nickname||a.person_name}))},we=i?.sessionInfo.bot_name||s("chat.botNameFallback");return e.jsxs("div",{className:"bg-background flex h-full min-h-0",children:[e.jsx(Es,{open:z,onOpenChange:D,platforms:X,persons:De,isLoadingPlatforms:$e,isLoadingPersons:ze,personSearchQuery:Y,setPersonSearchQuery:ce,tempVirtualConfig:S,setTempVirtualConfig:Z,onSelectPerson:Be,onCreateVirtualTab:Oe}),e.jsx(js,{className:"hidden md:flex",tabs:o,activeTabId:h,userName:x,onSwitch:ve,onClose:be,onAddVirtual:ge,onUpdateUserName:Le}),e.jsxs("div",{className:"flex min-w-0 flex-1 flex-col",children:[e.jsx("div",{className:"md:hidden",children:e.jsx(bs,{tabs:o,activeTabId:h,onSwitch:ve,onClose:be,onAddVirtual:ge})}),e.jsx(gs,{activeTab:i,botDisplayName:we,isConnecting:P,isLoadingHistory:d,onReconnect:Ae}),e.jsx(Ms,{messages:i?.messages??[],isLoadingHistory:d,botDisplayName:we,botQq:i?.sessionInfo.bot_qq,userName:x,language:t.language}),e.jsx(xs,{value:k,onChange:_,onSend:()=>{Fe()},disabled:!i?.isConnected||!k.trim(),isConnected:!!i?.isConnected})]})]})}export{qs as ChatPage};
|