collabdocchat 2.5.9 → 2.5.10
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/assets/{index-Dkrhl719.js → index-9XAJPSRa.js} +35 -35
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/scripts/cleanup-scripts.js +2 -0
- package/scripts/fix-startup-issues.js +2 -0
- package/scripts/start-simple.js +2 -0
- package/server/public/index.html +2 -0
- package/server/routes/polls.js +23 -4
- package/src/pages/admin-dashboard.js +13 -8
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
</div>
|
|
49
49
|
</div>
|
|
50
50
|
</div>
|
|
51
|
-
`,document.getElementById("showRegister").addEventListener("click",()=>{document.getElementById("loginSection").classList.add("hidden"),document.getElementById("registerSection").classList.remove("hidden")}),document.getElementById("showLogin").addEventListener("click",()=>{document.getElementById("registerSection").classList.add("hidden"),document.getElementById("loginSection").classList.remove("hidden")}),document.getElementById("loginForm").addEventListener("submit",async n=>{n.preventDefault();const c=new FormData(n.target),s=c.get("username"),d=c.get("password");try{const S=await o.login(s,d);a(S.user,S.token)}catch(S){document.getElementById("loginError").textContent=S.message}}),document.getElementById("registerForm").addEventListener("submit",async n=>{n.preventDefault();const c=new FormData(n.target),s=c.get("username"),d=c.get("password");try{const S=await o.register(s,d);a(S.user,S.token)}catch(S){document.getElementById("registerError").textContent=S.message}})}const je="http://localhost:3000/api";class gt{constructor(){this.token=localStorage.getItem("token")}async request(e,o={}){const n=await fetch(`${je}${e}`,{...o,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.token}`,...o.headers}});if(!n.ok){const c=await n.json().catch(()=>({message:"请求失败"}));throw console.error("API 错误:",{endpoint:e,status:n.status,error:c}),new Error(c.message||`请求失败: ${n.status}`)}return await n.json()}async getGroups(){return await this.request("/groups")}async getAllGroups(){return await this.request("/groups/all")}async getGroup(e){return await this.request(`/groups/${e}`)}async createGroup(e,o,n){return await this.request("/groups",{method:"POST",body:JSON.stringify({name:e,description:o,members:n})})}async joinGroup(e){return await this.request(`/groups/${e}/join`,{method:"POST"})}async leaveGroup(e){return await this.request(`/groups/${e}/leave`,{method:"POST"})}async addMember(e,o){return await this.request(`/groups/${e}/members`,{method:"POST",body:JSON.stringify({userId:o})})}async removeMember(e,o){return await this.request(`/groups/${e}/members/${o}`,{method:"DELETE"})}async setMuteAll(e,o){return await this.request(`/groups/${e}/mute/all`,{method:"POST",body:JSON.stringify({enabled:o})})}async setUserMute(e,o,n){return await this.request(`/groups/${e}/mute/users/${o}`,{method:"POST",body:JSON.stringify({muted:n})})}async getAllUsers(){return await this.request("/auth/users")}async getGroupMessages(e){return await this.request(`/groups/${e}/messages`)}async randomCall(e,o=1){return await this.request(`/groups/${e}/call`,{method:"POST",body:JSON.stringify({count:o})})}async clearGroupMessages(e){return await this.request(`/groups/${e}/messages`,{method:"DELETE",body:JSON.stringify({deleteAll:!0})})}async getTasks(e){return await this.request(`/tasks/group/${e}`)}async getMyTasks(){return await this.request("/tasks/my")}async createTask(e){return await this.request("/tasks",{method:"POST",body:JSON.stringify(e)})}async updateTaskStatus(e,o){return await this.request(`/tasks/${e}/status`,{method:"PATCH",body:JSON.stringify({status:o})})}async deleteTask(e){return await this.request(`/tasks/${e}`,{method:"DELETE"})}async getDocuments(e){return await this.request(`/documents/group/${e}`)}async getDocument(e){return await this.request(`/documents/${e}`)}async createDocument(e,o,n,c=[]){return await this.request("/documents",{method:"POST",body:JSON.stringify({title:e,content:o,groupId:n,editableMembers:c})})}async updateDocument(e,o){return await this.request(`/documents/${e}`,{method:"PATCH",body:JSON.stringify({content:o})})}async updateDocumentPermissions(e,o){return await this.request(`/documents/${e}/permissions`,{method:"PATCH",body:JSON.stringify({editableMembers:o})})}async getDocumentVersions(e){return await this.request(`/documents/${e}/versions`)}async deleteDocument(e){return await this.request(`/documents/${e}`,{method:"DELETE"})}async getAuditLogs(e={},o={}){const n=new URLSearchParams;Object.keys(e).forEach(s=>{e[s]&&n.append(s,e[s])}),Object.keys(o).forEach(s=>{o[s]&&n.append(s,o[s])});const c=n.toString();return await this.request(`/audit${c?"?"+c:""}`)}async getUserActivityStats(e,o={}){const c=new URLSearchParams(o).toString();return await this.request(`/audit/user-stats/${e}${c?"?"+c:""}`)}async getDocumentEditHistory(e,o=20){return await this.request(`/audit/document-history/${e}?limit=${o}`)}async getGroupAuditLogs(e,o={},n={}){const c=new URLSearchParams;Object.keys(o).forEach(d=>{o[d]&&c.append(d,o[d])}),Object.keys(n).forEach(d=>{n[d]&&c.append(d,n[d])});const s=c.toString();return await this.request(`/audit/group/${e}${s?"?"+s:""}`)}async getAuditSummary(e={}){const n=new URLSearchParams(e).toString();return await this.request(`/audit/stats/summary${n?"?"+n:""}`)}async getAuditLogDetail(e){return await this.request(`/audit/${e}`)}async clearAuditLogs(e={}){return await this.request("/audit",{method:"DELETE",body:JSON.stringify(e)})}async uploadFile(e,o,n=""){const c=new FormData;c.append("file",o),c.append("groupId",e),n&&c.append("description",n);const s=await fetch(`${je}/files/upload`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:c});if(!s.ok){const d=await s.json().catch(()=>({message:"上传失败"}));throw new Error(d.message||"上传失败")}return await s.json()}async getGroupFiles(e){return await this.request(`/files/group/${e}`)}async deleteFile(e){return await this.request(`/files/${e}`,{method:"DELETE"})}getFileDownloadUrl(e){return`${je}/files/${e}/download?token=${this.token}`}async createPoll(e){return await this.request("/polls",{method:"POST",body:JSON.stringify(e)})}async getGroupPolls(e){return await this.request(`/polls/group/${e}`)}async getPoll(e){return await this.request(`/polls/${e}`)}async vote(e,o){return await this.request(`/polls/${e}/vote`,{method:"POST",body:JSON.stringify({optionIndexes:o})})}async endPoll(e){return await this.request(`/polls/${e}/end`,{method:"PUT"})}async deletePoll(e){return await this.request(`/polls/${e}`,{method:"DELETE"})}}function Le(a){if(typeof a!="string"||!a)throw new Error("expected a non-empty string, got: "+a)}function _e(a){if(typeof a!="number")throw new Error("expected a number, got: "+a)}const At=1,Pt=1,ve="emoji",fe="keyvalue",Ye="favorites",Ft="tokens",mt="tokens",Ot="unicode",yt="count",Ht="group",Nt="order",bt="group-order",Ue="eTag",Be="url",at="skinTone",we="readonly",Ke="readwrite",ht="skinUnicodes",qt="skinUnicodes",Ut="https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/en/emojibase/data.json",Rt="en";function Gt(a,e){const o=new Set,n=[];for(const c of a){const s=e(c);o.has(s)||(o.add(s),n.push(c))}return n}function nt(a){return Gt(a,e=>e.unicode)}function Wt(a){function e(o,n,c){const s=n?a.createObjectStore(o,{keyPath:n}):a.createObjectStore(o);if(c)for(const[d,[S,H]]of Object.entries(c))s.createIndex(d,S,{multiEntry:H});return s}e(fe),e(ve,Ot,{[mt]:[Ft,!0],[bt]:[[Ht,Nt]],[ht]:[qt,!0]}),e(Ye,void 0,{[yt]:[""]})}const Re={},Te={},Me={};function vt(a,e,o){o.onerror=()=>e(o.error),o.onblocked=()=>e(new Error("IDB blocked")),o.onsuccess=()=>a(o.result)}async function Vt(a){const e=await new Promise((o,n)=>{const c=indexedDB.open(a,At);Re[a]=c,c.onupgradeneeded=s=>{s.oldVersion<Pt&&Wt(c.result)},vt(o,n,c)});return e.onclose=()=>Je(a),e}function Yt(a){return Te[a]||(Te[a]=Vt(a)),Te[a]}function be(a,e,o,n){return new Promise((c,s)=>{const d=a.transaction(e,o,{durability:"relaxed"}),S=typeof e=="string"?d.objectStore(e):e.map($=>d.objectStore($));let H;n(S,d,$=>{H=$}),d.oncomplete=()=>c(H),d.onerror=()=>s(d.error)})}function Je(a){const e=Re[a],o=e&&e.result;if(o){o.close();const n=Me[a];if(n)for(const c of n)c()}delete Re[a],delete Te[a],delete Me[a]}function Kt(a){return new Promise((e,o)=>{Je(a);const n=indexedDB.deleteDatabase(a);vt(e,o,n)})}function Jt(a,e){let o=Me[a];o||(o=Me[a]=[]),o.push(e)}const Xt=new Set([":D","XD",":'D","O:)",":X",":P",";P","XP",":L",":Z",":j","8D","XO","8)",":B",":O",":S",":'o","Dx","X(","D:",":C",">0)",":3","</3","<3","\\M/",":E","8#"]);function xe(a){return a.split(/[\s_]+/).map(e=>!e.match(/\w/)||Xt.has(e)?e.toLowerCase():e.replace(/[)(:,]/g,"").replace(/’/g,"'").toLowerCase()).filter(Boolean)}const Zt=2;function xt(a){return a.filter(Boolean).map(e=>e.toLowerCase()).filter(e=>e.length>=Zt)}function Qt(a){return a.map(({annotation:o,emoticon:n,group:c,order:s,shortcodes:d,skins:S,tags:H,emoji:$,version:j})=>{const O=[...new Set(xt([...(d||[]).map(xe).flat(),...(H||[]).map(xe).flat(),...xe(o),n]))].sort(),_={annotation:o,group:c,order:s,tags:H,tokens:O,unicode:$,version:j};if(n&&(_.emoticon=n),d&&(_.shortcodes=d),S){_.skinTones=[],_.skinUnicodes=[],_.skinVersions=[];for(const{tone:U,emoji:se,version:ae}of S)_.skinTones.push(U),_.skinUnicodes.push(se),_.skinVersions.push(ae)}return _})}function ft(a,e,o,n){a[e](o).onsuccess=c=>n&&n(c.target.result)}function he(a,e,o){ft(a,"get",e,o)}function wt(a,e,o){ft(a,"getAll",e,o)}function Xe(a){a.commit&&a.commit()}function ea(a,e){let o=a[0];for(let n=1;n<a.length;n++){const c=a[n];e(o)>e(c)&&(o=c)}return o}function kt(a,e){const o=ea(a,c=>c.length),n=[];for(const c of o)a.some(s=>s.findIndex(d=>e(d)===e(c))===-1)||n.push(c);return n}async function ta(a){return!await Ze(a,fe,Be)}async function aa(a,e,o){const[n,c]=await Promise.all([Ue,Be].map(s=>Ze(a,fe,s)));return n===o&&c===e}async function na(a,e){return be(a,ve,we,(n,c,s)=>{let d;const S=()=>{n.getAll(d&&IDBKeyRange.lowerBound(d,!0),50).onsuccess=H=>{const $=H.target.result;for(const j of $)if(d=j.unicode,e(j))return s(j);if($.length<50)return s();S()}};S()})}async function Et(a,e,o,n){try{const c=Qt(e);await be(a,[ve,fe],Ke,([s,d],S)=>{let H,$,j=0;function O(){++j===2&&_()}function _(){if(!(H===n&&$===o)){s.clear();for(const U of c)s.put(U);d.put(n,Ue),d.put(o,Be),Xe(S)}}he(d,Ue,U=>{H=U,O()}),he(d,Be,U=>{$=U,O()})})}finally{}}async function oa(a,e){return be(a,ve,we,(o,n,c)=>{const s=IDBKeyRange.bound([e,0],[e+1,0],!1,!0);wt(o.index(bt),s,c)})}async function Lt(a,e){const o=xt(xe(e));return o.length?be(a,ve,we,(n,c,s)=>{const d=[],S=()=>{d.length===o.length&&H()},H=()=>{const $=kt(d,j=>j.unicode);s($.sort((j,O)=>j.order<O.order?-1:1))};for(let $=0;$<o.length;$++){const j=o[$],O=$===o.length-1?IDBKeyRange.bound(j,j+"",!1,!0):IDBKeyRange.only(j);wt(n.index(mt),O,_=>{d.push(_),S()})}}):[]}async function sa(a,e){const o=await Lt(a,e);return o.length?o.filter(n=>(n.shortcodes||[]).map(s=>s.toLowerCase()).includes(e.toLowerCase()))[0]||null:await na(a,c=>(c.shortcodes||[]).includes(e.toLowerCase()))||null}async function ia(a,e){return be(a,ve,we,(o,n,c)=>he(o,e,s=>{if(s)return c(s);he(o.index(ht),e,d=>c(d||null))}))}function Ze(a,e,o){return be(a,e,we,(n,c,s)=>he(n,o,s))}function ra(a,e,o,n){return be(a,e,Ke,(c,s)=>{c.put(n,o),Xe(s)})}function da(a,e){return be(a,Ye,Ke,(o,n)=>he(o,e,c=>{o.put((c||0)+1,e),Xe(n)}))}function la(a,e,o){return o===0?[]:be(a,[Ye,ve],we,([n,c],s,d)=>{const S=[];n.index(yt).openCursor(void 0,"prev").onsuccess=H=>{const $=H.target.result;if(!$)return d(S);function j(U){if(S.push(U),S.length===o)return d(S);$.continue()}const O=$.primaryKey,_=e.byName(O);if(_)return j(_);he(c,O,U=>{if(U)return j(U);$.continue()})}})}const $e="";function ca(a,e){const o=new Map;for(const c of a){const s=e(c);for(const d of s){let S=o;for(let $=0;$<d.length;$++){const j=d.charAt($);let O=S.get(j);O||(O=new Map,S.set(j,O)),S=O}let H=S.get($e);H||(H=[],S.set($e,H)),H.push(c)}}return(c,s)=>{let d=o;for(let $=0;$<c.length;$++){const j=c.charAt($),O=d.get(j);if(O)d=O;else return[]}if(s)return d.get($e)||[];const S=[],H=[d];for(;H.length;){const j=[...H.shift().entries()].sort((O,_)=>O[0]<_[0]?-1:1);for(const[O,_]of j)O===$e?S.push(..._):H.push(_)}return S}}const pa=["name","url"];function ua(a){const e=a&&Array.isArray(a),o=e&&a.length&&(!a[0]||pa.some(n=>!(n in a[0])));if(!e||o)throw new Error("Custom emojis are in the wrong format")}function ot(a){ua(a);const e=(_,U)=>_.name.toLowerCase()<U.name.toLowerCase()?-1:1,o=a.sort(e),c=ca(a,_=>{const U=new Set;if(_.shortcodes)for(const se of _.shortcodes)for(const ae of xe(se))U.add(ae);return U}),s=_=>c(_,!0),d=_=>c(_,!1),S=_=>{const U=xe(_),se=U.map((ae,ie)=>(ie<U.length-1?s:d)(ae));return kt(se,ae=>ae.name).sort(e)},H=new Map,$=new Map;for(const _ of a){$.set(_.name.toLowerCase(),_);for(const U of _.shortcodes||[])H.set(U.toLowerCase(),_)}return{all:o,search:S,byShortcode:_=>H.get(_.toLowerCase()),byName:_=>$.get(_.toLowerCase())}}const ga=typeof wrappedJSObject<"u";function ke(a){if(!a)return a;if(ga&&(a=structuredClone(a)),delete a.tokens,a.skinTones){const e=a.skinTones.length;a.skins=Array(e);for(let o=0;o<e;o++)a.skins[o]={tone:a.skinTones[o],unicode:a.skinUnicodes[o],version:a.skinVersions[o]};delete a.skinTones,delete a.skinUnicodes,delete a.skinVersions}return a}function $t(a){a||console.warn("emoji-picker-element is more efficient if the dataSource server exposes an ETag header.")}const ma=["annotation","emoji","group","order","version"];function ya(a){if(!a||!Array.isArray(a)||!a[0]||typeof a[0]!="object"||ma.some(e=>!(e in a[0])))throw new Error("Emoji data is in the wrong format")}function It(a,e){if(Math.floor(a.status/100)!==2)throw new Error("Failed to fetch: "+e+": "+a.status)}async function ba(a){const e=await fetch(a,{method:"HEAD"});It(e,a);const o=e.headers.get("etag");return $t(o),o}async function Ge(a){const e=await fetch(a);It(e,a);const o=e.headers.get("etag");$t(o);const n=await e.json();return ya(n),[o,n]}function ha(a){for(var e="",o=new Uint8Array(a),n=o.byteLength,c=-1;++c<n;)e+=String.fromCharCode(o[c]);return e}function va(a){for(var e=a.length,o=new ArrayBuffer(e),n=new Uint8Array(o),c=-1;++c<e;)n[c]=a.charCodeAt(c);return o}async function Tt(a){const e=JSON.stringify(a);let o=va(e);const n=await crypto.subtle.digest("SHA-1",o),c=ha(n);return btoa(c)}async function xa(a,e){let o,n=await ba(e);if(!n){const c=await Ge(e);n=c[0],o=c[1],n||(n=await Tt(o))}await aa(a,e,n)||(o||(o=(await Ge(e))[1]),await Et(a,o,e,n))}async function fa(a,e){let[o,n]=await Ge(e);o||(o=await Tt(n)),await Et(a,n,e,o)}async function wa(a,e){try{await xa(a,e)}catch(o){if(o.name!=="InvalidStateError")throw o}}class ka{constructor({dataSource:e=Ut,locale:o=Rt,customEmoji:n=[]}={}){this.dataSource=e,this.locale=o,this._dbName=`emoji-picker-element-${this.locale}`,this._db=void 0,this._lazyUpdate=void 0,this._custom=ot(n),this._clear=this._clear.bind(this),this._ready=this._init()}async _init(){const e=this._db=await Yt(this._dbName);Jt(this._dbName,this._clear);const o=this.dataSource;await ta(e)?await fa(e,o):this._lazyUpdate=wa(e,o)}async ready(){const e=async()=>(this._ready||(this._ready=this._init()),this._ready);await e(),this._db||await e()}async getEmojiByGroup(e){return _e(e),await this.ready(),nt(await oa(this._db,e)).map(ke)}async getEmojiBySearchQuery(e){Le(e),await this.ready();const o=this._custom.search(e),n=nt(await Lt(this._db,e)).map(ke);return[...o,...n]}async getEmojiByShortcode(e){Le(e),await this.ready();const o=this._custom.byShortcode(e);return o||ke(await sa(this._db,e))}async getEmojiByUnicodeOrName(e){Le(e),await this.ready();const o=this._custom.byName(e);return o||ke(await ia(this._db,e))}async getPreferredSkinTone(){return await this.ready(),await Ze(this._db,fe,at)||0}async setPreferredSkinTone(e){return _e(e),await this.ready(),ra(this._db,fe,at,e)}async incrementFavoriteEmojiCount(e){return Le(e),await this.ready(),da(this._db,e)}async getTopFavoriteEmoji(e){return _e(e),await this.ready(),(await la(this._db,this._custom,e)).map(ke)}set customEmoji(e){this._custom=ot(e)}get customEmoji(){return this._custom.all}async _shutdown(){await this.ready();try{await this._lazyUpdate}catch{}}_clear(){this._db=this._ready=this._lazyUpdate=void 0}async close(){await this._shutdown(),await Je(this._dbName)}async delete(){await this._shutdown(),await Kt(this._dbName)}}const We=[[-1,"✨","custom"],[0,"😀","smileys-emotion"],[1,"👋","people-body"],[3,"🐱","animals-nature"],[4,"🍎","food-drink"],[5,"🏠️","travel-places"],[6,"⚽","activities"],[7,"📝","objects"],[8,"⛔️","symbols"],[9,"🏁","flags"]].map(([a,e,o])=>({id:a,emoji:e,name:o})),Ae=We.slice(1),Ea=2,st=6,St=typeof requestIdleCallback=="function"?requestIdleCallback:setTimeout;function it(a){return a.unicode.includes("")}const La={"":17,"":16,"🫨":15.1,"🫠":14,"🥲":13.1,"🥻":12.1,"🥰":11,"🤩":5,"👱♀️":4,"🤣":3,"👁️🗨️":2,"😀":1,"😐️":.7,"😃":.6},$a=1e3,Ia="🖐️",Ta=8,Sa=["😊","😒","❤️","👍️","😍","😂","😭","☺️","😔","😩","😏","💕","🙌","😘"],Bt='"Twemoji Mozilla","Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji","EmojiOne Color","Android Emoji",sans-serif',Ba=(a,e)=>a<e?-1:a>e?1:0,rt=(a,e)=>{const o=document.createElement("canvas");o.width=o.height=1;const n=o.getContext("2d",{willReadFrequently:!0});return n.textBaseline="top",n.font=`100px ${Bt}`,n.fillStyle=e,n.scale(.01,.01),n.fillText(a,0,0),n.getImageData(0,0,1,1).data},Ma=(a,e)=>{const o=[...a].join(","),n=[...e].join(",");return o===n&&!o.startsWith("0,0,0,")};function Ca(a){const e=rt(a,"#000"),o=rt(a,"#fff");return e&&o&&Ma(e,o)}function za(){const a=Object.entries(La);try{for(const[e,o]of a)if(Ca(e))return o}catch{}finally{}return a[0][1]}let Pe;const Fe=()=>(Pe||(Pe=new Promise(a=>St(()=>a(za())))),Pe),Ve=new Map,Da="️",ja="\uD83C",_a="",Aa=127995,Pa=57339;function Fa(a,e){if(e===0)return a;const o=a.indexOf(_a);return o!==-1?a.substring(0,o)+String.fromCodePoint(Aa+e-1)+a.substring(o):(a.endsWith(Da)&&(a=a.substring(0,a.length-1)),a+ja+String.fromCodePoint(Pa+e-1))}function ye(a){a.preventDefault(),a.stopPropagation()}function Oe(a,e,o){return e+=a?-1:1,e<0?e=o.length-1:e>=o.length&&(e=0),e}function Mt(a,e){const o=new Set,n=[];for(const c of a){const s=e(c);o.has(s)||(o.add(s),n.push(c))}return n}function Oa(a,e){const o=n=>{const c={};for(const s of n)typeof s.tone=="number"&&s.version<=e&&(c[s.tone]=s.unicode);return c};return a.map(({unicode:n,skins:c,shortcodes:s,url:d,name:S,category:H,annotation:$})=>({unicode:n,name:S,shortcodes:s,url:d,category:H,annotation:$,id:n||S,skins:c&&o(c)}))}const Se=requestAnimationFrame;let Ha=typeof ResizeObserver=="function";function Na(a,e,o){let n;Ha?(n=new ResizeObserver(o),n.observe(a)):Se(o),e.addEventListener("abort",()=>{n&&n.disconnect()})}function dt(a){{const e=document.createRange();return e.selectNode(a.firstChild),e.getBoundingClientRect().width}}let He;function qa(a,e,o){let n=!0;for(const c of a){const s=o(c);if(!s)continue;const d=dt(s);typeof He>"u"&&(He=dt(e));const S=d/1.8<He;Ve.set(c.unicode,S),S||(n=!1)}return n}function Ua(a){return Mt(a,e=>e)}function Ra(a){a&&(a.scrollTop=0)}function Ee(a,e,o){let n=a.get(e);return n||(n=o(),a.set(e,n)),n}function lt(a){return""+a}function Ga(a){const e=document.createElement("template");return e.innerHTML=a,e}const Wa=new WeakMap,Va=new WeakMap,Ya=Symbol("un-keyed"),Ka="replaceChildren"in Element.prototype;function Ja(a,e){Ka?a.replaceChildren(...e):(a.innerHTML="",a.append(...e))}function Xa(a,e){let o=a.firstChild,n=0;for(;o;){if(e[n]!==o)return!0;o=o.nextSibling,n++}return n!==e.length}function Za(a,e){const{targetNode:o}=e;let{targetParentNode:n}=e,c=!1;n?c=Xa(n,a):(c=!0,e.targetNode=void 0,e.targetParentNode=n=o.parentNode),c&&Ja(n,a)}function Qa(a,e){for(const o of e){const{targetNode:n,currentExpression:c,binding:{expressionIndex:s,attributeName:d,attributeValuePre:S,attributeValuePost:H}}=o,$=a[s];if(c!==$)if(o.currentExpression=$,d)if($===null)n.removeAttribute(d);else{const j=S+lt($)+H;n.setAttribute(d,j)}else{let j;Array.isArray($)?Za($,o):$ instanceof Element?(j=$,n.replaceWith(j)):n.nodeValue=lt($),j&&(o.targetNode=j)}}}function en(a){let e="",o=!1,n=!1,c=-1;const s=new Map,d=[];let S=0;for(let $=0,j=a.length;$<j;$++){const O=a[$];if(e+=O.slice(S),$===j-1)break;for(let q=0;q<O.length;q++)switch(O.charAt(q)){case"<":{O.charAt(q+1)==="/"?d.pop():(o=!0,d.push(++c));break}case">":{o=!1,n=!1;break}case"=":{n=!0;break}}const _=d[d.length-1],U=Ee(s,_,()=>[]);let se,ae,ie;if(n){const q=/(\S+)="?([^"=]*)$/.exec(O);se=q[1],ae=q[2];const V=/^([^">]*)("?)/.exec(a[$+1]);ie=V[1],e=e.slice(0,-1*q[0].length),S=V[0].length}else S=0;const pe={attributeName:se,attributeValuePre:ae,attributeValuePost:ie,expressionIndex:$};U.push(pe),!o&&!n&&(e+=" ")}return{template:Ga(e),elementsToBindings:s}}function ct(a,e,o){for(let n=0;n<a.length;n++){const c=a[n],s=c.attributeName?e:e.firstChild,d={binding:c,targetNode:s,targetParentNode:void 0,currentExpression:void 0};o.push(d)}}function tn(a,e){const o=[];let n;if(e.size===1&&(n=e.get(0)))ct(n,a,o);else{const c=document.createTreeWalker(a,NodeFilter.SHOW_ELEMENT);let s=a,d=-1;do{const S=e.get(++d);S&&ct(S,s,o)}while(s=c.nextNode())}return o}function an(a){const{template:e,elementsToBindings:o}=Ee(Wa,a,()=>en(a)),n=e.cloneNode(!0).content.firstElementChild,c=tn(n,o);return function(d){return Qa(d,c),n}}function nn(a){const e=Ee(Va,a,()=>new Map);let o=Ya;function n(s,...d){const S=Ee(e,s,()=>new Map);return Ee(S,o,()=>an(s))(d)}function c(s,d,S){return s.map((H,$)=>{const j=o;o=S(H);try{return d(H,$)}finally{o=j}})}return{map:c,html:n}}function on(a,e,o,n,c,s,d,S,H){const{labelWithSkin:$,titleForEmoji:j,unicodeWithSkin:O}=o,{html:_,map:U}=nn(e);function se(q,V,ne){return U(q,(oe,le)=>_`<button role="${V?"option":"menuitem"}" aria-selected="${V?le===e.activeSearchItem:null}" aria-label="${$(oe,e.currentSkinTone)}" title="${j(oe)}" class="${"emoji"+(V&&le===e.activeSearchItem?" active":"")+(oe.unicode?"":" custom-emoji")}" id="${`${ne}-${oe.id}`}" style="${oe.unicode?null:`--custom-emoji-background: url(${JSON.stringify(oe.url)})`}">${oe.unicode?O(oe,e.currentSkinTone):""}</button>`,oe=>`${ne}-${oe.id}`)}const ie=_`<section data-ref="rootElement" class="picker" aria-label="${e.i18n.regionLabel}" style="${e.pickerStyle||""}"><div class="pad-top"></div><div class="search-row"><div class="search-wrapper"><input id="search" class="search" type="search" role="combobox" enterkeyhint="search" placeholder="${e.i18n.searchLabel}" autocapitalize="none" autocomplete="off" spellcheck="true" aria-expanded="${!!(e.searchMode&&e.currentEmojis.length)}" aria-controls="search-results" aria-describedby="search-description" aria-autocomplete="list" aria-activedescendant="${e.activeSearchItemId?`emo-${e.activeSearchItemId}`:null}" data-ref="searchElement" data-on-input="onSearchInput" data-on-keydown="onSearchKeydown"><label class="sr-only" for="search">${e.i18n.searchLabel}</label> <span id="search-description" class="sr-only">${e.i18n.searchDescription}</span></div><div class="skintone-button-wrapper ${e.skinTonePickerExpandedAfterAnimation?"expanded":""}"><button id="skintone-button" class="emoji ${e.skinTonePickerExpanded?"hide-focus":""}" aria-label="${e.skinToneButtonLabel}" title="${e.skinToneButtonLabel}" aria-describedby="skintone-description" aria-haspopup="listbox" aria-expanded="${e.skinTonePickerExpanded}" aria-controls="skintone-list" data-on-click="onClickSkinToneButton">${e.skinToneButtonText||""}</button></div><span id="skintone-description" class="sr-only">${e.i18n.skinToneDescription}</span><div data-ref="skinToneDropdown" id="skintone-list" class="skintone-list hide-focus ${e.skinTonePickerExpanded?"":"hidden no-animate"}" style="transform:translateY(${e.skinTonePickerExpanded?0:"calc(-1 * var(--num-skintones) * var(--total-emoji-size))"})" role="listbox" aria-label="${e.i18n.skinTonesLabel}" aria-activedescendant="skintone-${e.activeSkinTone}" aria-hidden="${!e.skinTonePickerExpanded}" tabIndex="-1" data-on-focusout="onSkinToneOptionsFocusOut" data-on-click="onSkinToneOptionsClick" data-on-keydown="onSkinToneOptionsKeydown" data-on-keyup="onSkinToneOptionsKeyup">${U(e.skinTones,(q,V)=>_`<div id="skintone-${V}" class="emoji ${V===e.activeSkinTone?"active":""}" aria-selected="${V===e.activeSkinTone}" role="option" title="${e.i18n.skinTones[V]}" aria-label="${e.i18n.skinTones[V]}">${q}</div>`,q=>q)}</div></div><div class="nav" role="tablist" style="grid-template-columns:repeat(${e.groups.length},1fr)" aria-label="${e.i18n.categoriesLabel}" data-on-keydown="onNavKeydown" data-on-click="onNavClick">${U(e.groups,q=>_`<button role="tab" class="nav-button" aria-controls="tab-${q.id}" aria-label="${e.i18n.categories[q.name]}" aria-selected="${!e.searchMode&&e.currentGroup.id===q.id}" title="${e.i18n.categories[q.name]}" data-group-id="${q.id}"><div class="nav-emoji emoji">${q.emoji}</div></button>`,q=>q.id)}</div><div class="indicator-wrapper"><div class="indicator" style="transform:translateX(${(e.isRtl?-1:1)*e.currentGroupIndex*100}%)"></div></div><div class="message ${e.message?"":"gone"}" role="alert" aria-live="polite">${e.message||""}</div><div data-ref="tabpanelElement" class="tabpanel ${!e.databaseLoaded||e.message?"gone":""}" role="${e.searchMode?"region":"tabpanel"}" aria-label="${e.searchMode?e.i18n.searchResultsLabel:e.i18n.categories[e.currentGroup.name]}" id="${e.searchMode?null:`tab-${e.currentGroup.id}`}" tabIndex="0" data-on-click="onEmojiClick"><div data-action="calculateEmojiGridStyle">${U(e.currentEmojisWithCategories,(q,V)=>_`<div><div id="menu-label-${V}" class="category ${e.currentEmojisWithCategories.length===1&&e.currentEmojisWithCategories[0].category===""?"gone":""}" aria-hidden="true">${e.searchMode?e.i18n.searchResultsLabel:q.category?q.category:e.currentEmojisWithCategories.length>1?e.i18n.categories.custom:e.i18n.categories[e.currentGroup.name]}</div><div class="emoji-menu ${V!==0&&!e.searchMode&&e.currentGroup.id===-1?"visibility-auto":""}" style="${`--num-rows: ${Math.ceil(q.emojis.length/e.numColumns)}`}" data-action="updateOnIntersection" role="${e.searchMode?"listbox":"menu"}" aria-labelledby="menu-label-${V}" id="${e.searchMode?"search-results":null}">${se(q.emojis,e.searchMode,"emo")}</div></div>`,q=>q.category)}</div></div><div class="favorites onscreen emoji-menu ${e.message?"gone":""}" role="menu" aria-label="${e.i18n.favoritesLabel}" data-on-click="onEmojiClick">${se(e.currentFavorites,!1,"fav")}</div><button data-ref="baselineEmoji" aria-hidden="true" tabindex="-1" class="abs-pos hidden emoji baseline-emoji">😀</button></section>`,pe=(q,V)=>{for(const ne of a.querySelectorAll(`[${q}]`))V(ne,ne.getAttribute(q))};if(H){a.appendChild(ie);for(const q of["click","focusout","input","keydown","keyup"])pe(`data-on-${q}`,(V,ne)=>{V.addEventListener(q,n[ne])});pe("data-ref",(q,V)=>{s[V]=q}),d.addEventListener("abort",()=>{a.removeChild(ie)})}pe("data-action",(q,V)=>{let ne=S.get(V);ne||S.set(V,ne=new WeakSet),ne.has(q)||(ne.add(q),c[V](q))})}const Ce=typeof queueMicrotask=="function"?queueMicrotask:a=>Promise.resolve().then(a);function sn(a){let e=!1,o;const n=new Map,c=new Set;let s;const d=()=>{if(e)return;const $=[...c];c.clear();try{for(const j of $)j()}finally{s=!1,c.size&&(s=!0,Ce(d))}},S=new Proxy({},{get($,j){if(o){let O=n.get(j);O||(O=new Set,n.set(j,O)),O.add(o)}return $[j]},set($,j,O){if($[j]!==O){$[j]=O;const _=n.get(j);if(_){for(const U of _)c.add(U);s||(s=!0,Ce(d))}}return!0}}),H=$=>{const j=()=>{const O=o;o=j;try{return $()}finally{o=O}};return j()};return a.addEventListener("abort",()=>{e=!0}),{state:S,createEffect:H}}function Ne(a,e,o){if(a.length!==e.length)return!1;for(let n=0;n<a.length;n++)if(!o(a[n],e[n]))return!1;return!0}const pt=new WeakMap;function rn(a,e,o){{const n=a.closest(".tabpanel");let c=pt.get(n);c||(c=new IntersectionObserver(o,{root:n,rootMargin:"50% 0px 50% 0px",threshold:0}),pt.set(n,c),e.addEventListener("abort",()=>{c.disconnect()})),c.observe(a)}}const qe=[],{assign:Ie}=Object;function dn(a,e){const o={},n=new AbortController,c=n.signal,{state:s,createEffect:d}=sn(c),S=new Map;Ie(s,{skinToneEmoji:void 0,i18n:void 0,database:void 0,customEmoji:void 0,customCategorySorting:void 0,emojiVersion:void 0}),Ie(s,e),Ie(s,{initialLoad:!0,currentEmojis:[],currentEmojisWithCategories:[],rawSearchText:"",searchText:"",searchMode:!1,activeSearchItem:-1,message:void 0,skinTonePickerExpanded:!1,skinTonePickerExpandedAfterAnimation:!1,currentSkinTone:0,activeSkinTone:0,skinToneButtonText:void 0,pickerStyle:void 0,skinToneButtonLabel:"",skinTones:[],currentFavorites:[],defaultFavoriteEmojis:void 0,numColumns:Ta,isRtl:!1,currentGroupIndex:0,groups:Ae,databaseLoaded:!1,activeSearchItemId:void 0}),d(()=>{s.currentGroup!==s.groups[s.currentGroupIndex]&&(s.currentGroup=s.groups[s.currentGroupIndex])});const H=t=>{a.getElementById(t).focus()},$=t=>a.getElementById(`emo-${t.id}`),j=(t,r)=>{o.rootElement.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0}))},O=(t,r)=>t.id===r.id,_=(t,r)=>{const{category:l,emojis:b}=t,{category:E,emojis:w}=r;return l!==E?!1:Ne(b,w,O)},U=t=>{Ne(s.currentEmojis,t,O)||(s.currentEmojis=t)},se=t=>{s.searchMode!==t&&(s.searchMode=t)},ae=t=>{Ne(s.currentEmojisWithCategories,t,_)||(s.currentEmojisWithCategories=t)},ie=(t,r)=>r&&t.skins&&t.skins[r]||t.unicode,V={labelWithSkin:(t,r)=>Ua([t.name||ie(t,r),t.annotation,...t.shortcodes||qe].filter(Boolean)).join(", "),titleForEmoji:t=>t.annotation||(t.shortcodes||qe).join(", "),unicodeWithSkin:ie},ne={onClickSkinToneButton:y,onEmojiClick:m,onNavClick:re,onNavKeydown:X,onSearchKeydown:F,onSkinToneOptionsClick:g,onSkinToneOptionsFocusOut:p,onSkinToneOptionsKeydown:h,onSkinToneOptionsKeyup:M,onSearchInput:i},oe={calculateEmojiGridStyle:I,updateOnIntersection:A};let le=!0;d(()=>{on(a,s,V,ne,oe,o,c,S,le),le=!1}),s.emojiVersion||Fe().then(t=>{t||(s.message=s.i18n.emojiUnsupportedMessage)}),d(()=>{async function t(){let r=!1;const l=setTimeout(()=>{r=!0,s.message=s.i18n.loadingMessage},$a);try{await s.database.ready(),s.databaseLoaded=!0}catch(b){console.error(b),s.message=s.i18n.networkErrorMessage}finally{clearTimeout(l),r&&(r=!1,s.message="")}}s.database&&t()}),d(()=>{s.pickerStyle=`
|
|
51
|
+
`,document.getElementById("showRegister").addEventListener("click",()=>{document.getElementById("loginSection").classList.add("hidden"),document.getElementById("registerSection").classList.remove("hidden")}),document.getElementById("showLogin").addEventListener("click",()=>{document.getElementById("registerSection").classList.add("hidden"),document.getElementById("loginSection").classList.remove("hidden")}),document.getElementById("loginForm").addEventListener("submit",async n=>{n.preventDefault();const c=new FormData(n.target),s=c.get("username"),d=c.get("password");try{const S=await o.login(s,d);a(S.user,S.token)}catch(S){document.getElementById("loginError").textContent=S.message}}),document.getElementById("registerForm").addEventListener("submit",async n=>{n.preventDefault();const c=new FormData(n.target),s=c.get("username"),d=c.get("password");try{const S=await o.register(s,d);a(S.user,S.token)}catch(S){document.getElementById("registerError").textContent=S.message}})}const je="http://localhost:3000/api";class gt{constructor(){this.token=localStorage.getItem("token")}async request(e,o={}){const n=await fetch(`${je}${e}`,{...o,headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.token}`,...o.headers}});if(!n.ok){const c=await n.json().catch(()=>({message:"请求失败"}));throw console.error("API 错误:",{endpoint:e,status:n.status,error:c}),new Error(c.message||`请求失败: ${n.status}`)}return await n.json()}async getGroups(){return await this.request("/groups")}async getAllGroups(){return await this.request("/groups/all")}async getGroup(e){return await this.request(`/groups/${e}`)}async createGroup(e,o,n){return await this.request("/groups",{method:"POST",body:JSON.stringify({name:e,description:o,members:n})})}async joinGroup(e){return await this.request(`/groups/${e}/join`,{method:"POST"})}async leaveGroup(e){return await this.request(`/groups/${e}/leave`,{method:"POST"})}async addMember(e,o){return await this.request(`/groups/${e}/members`,{method:"POST",body:JSON.stringify({userId:o})})}async removeMember(e,o){return await this.request(`/groups/${e}/members/${o}`,{method:"DELETE"})}async setMuteAll(e,o){return await this.request(`/groups/${e}/mute/all`,{method:"POST",body:JSON.stringify({enabled:o})})}async setUserMute(e,o,n){return await this.request(`/groups/${e}/mute/users/${o}`,{method:"POST",body:JSON.stringify({muted:n})})}async getAllUsers(){return await this.request("/auth/users")}async getGroupMessages(e){return await this.request(`/groups/${e}/messages`)}async randomCall(e,o=1){return await this.request(`/groups/${e}/call`,{method:"POST",body:JSON.stringify({count:o})})}async clearGroupMessages(e){return await this.request(`/groups/${e}/messages`,{method:"DELETE",body:JSON.stringify({deleteAll:!0})})}async getTasks(e){return await this.request(`/tasks/group/${e}`)}async getMyTasks(){return await this.request("/tasks/my")}async createTask(e){return await this.request("/tasks",{method:"POST",body:JSON.stringify(e)})}async updateTaskStatus(e,o){return await this.request(`/tasks/${e}/status`,{method:"PATCH",body:JSON.stringify({status:o})})}async deleteTask(e){return await this.request(`/tasks/${e}`,{method:"DELETE"})}async getDocuments(e){return await this.request(`/documents/group/${e}`)}async getDocument(e){return await this.request(`/documents/${e}`)}async createDocument(e,o,n,c=[]){return await this.request("/documents",{method:"POST",body:JSON.stringify({title:e,content:o,groupId:n,editableMembers:c})})}async updateDocument(e,o){return await this.request(`/documents/${e}`,{method:"PATCH",body:JSON.stringify({content:o})})}async updateDocumentPermissions(e,o){return await this.request(`/documents/${e}/permissions`,{method:"PATCH",body:JSON.stringify({editableMembers:o})})}async getDocumentVersions(e){return await this.request(`/documents/${e}/versions`)}async deleteDocument(e){return await this.request(`/documents/${e}`,{method:"DELETE"})}async getAuditLogs(e={},o={}){const n=new URLSearchParams;Object.keys(e).forEach(s=>{e[s]&&n.append(s,e[s])}),Object.keys(o).forEach(s=>{o[s]&&n.append(s,o[s])});const c=n.toString();return await this.request(`/audit${c?"?"+c:""}`)}async getUserActivityStats(e,o={}){const c=new URLSearchParams(o).toString();return await this.request(`/audit/user-stats/${e}${c?"?"+c:""}`)}async getDocumentEditHistory(e,o=20){return await this.request(`/audit/document-history/${e}?limit=${o}`)}async getGroupAuditLogs(e,o={},n={}){const c=new URLSearchParams;Object.keys(o).forEach(d=>{o[d]&&c.append(d,o[d])}),Object.keys(n).forEach(d=>{n[d]&&c.append(d,n[d])});const s=c.toString();return await this.request(`/audit/group/${e}${s?"?"+s:""}`)}async getAuditSummary(e={}){const n=new URLSearchParams(e).toString();return await this.request(`/audit/stats/summary${n?"?"+n:""}`)}async getAuditLogDetail(e){return await this.request(`/audit/${e}`)}async clearAuditLogs(e={}){return await this.request("/audit",{method:"DELETE",body:JSON.stringify(e)})}async uploadFile(e,o,n=""){const c=new FormData;c.append("file",o),c.append("groupId",e),n&&c.append("description",n);const s=await fetch(`${je}/files/upload`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:c});if(!s.ok){const d=await s.json().catch(()=>({message:"上传失败"}));throw new Error(d.message||"上传失败")}return await s.json()}async getGroupFiles(e){return await this.request(`/files/group/${e}`)}async deleteFile(e){return await this.request(`/files/${e}`,{method:"DELETE"})}getFileDownloadUrl(e){return`${je}/files/${e}/download?token=${this.token}`}async createPoll(e){return await this.request("/polls",{method:"POST",body:JSON.stringify(e)})}async getGroupPolls(e){return await this.request(`/polls/group/${e}`)}async getPoll(e){return await this.request(`/polls/${e}`)}async vote(e,o){return await this.request(`/polls/${e}/vote`,{method:"POST",body:JSON.stringify({optionIndexes:o})})}async endPoll(e){return await this.request(`/polls/${e}/end`,{method:"PUT"})}async deletePoll(e){return await this.request(`/polls/${e}`,{method:"DELETE"})}}function Le(a){if(typeof a!="string"||!a)throw new Error("expected a non-empty string, got: "+a)}function _e(a){if(typeof a!="number")throw new Error("expected a number, got: "+a)}const At=1,Pt=1,ve="emoji",fe="keyvalue",Ye="favorites",Ft="tokens",mt="tokens",Ot="unicode",yt="count",Ht="group",Nt="order",bt="group-order",Ue="eTag",Be="url",at="skinTone",we="readonly",Ke="readwrite",ht="skinUnicodes",qt="skinUnicodes",Ut="https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/en/emojibase/data.json",Rt="en";function Gt(a,e){const o=new Set,n=[];for(const c of a){const s=e(c);o.has(s)||(o.add(s),n.push(c))}return n}function nt(a){return Gt(a,e=>e.unicode)}function Wt(a){function e(o,n,c){const s=n?a.createObjectStore(o,{keyPath:n}):a.createObjectStore(o);if(c)for(const[d,[S,N]]of Object.entries(c))s.createIndex(d,S,{multiEntry:N});return s}e(fe),e(ve,Ot,{[mt]:[Ft,!0],[bt]:[[Ht,Nt]],[ht]:[qt,!0]}),e(Ye,void 0,{[yt]:[""]})}const Re={},Te={},Me={};function vt(a,e,o){o.onerror=()=>e(o.error),o.onblocked=()=>e(new Error("IDB blocked")),o.onsuccess=()=>a(o.result)}async function Vt(a){const e=await new Promise((o,n)=>{const c=indexedDB.open(a,At);Re[a]=c,c.onupgradeneeded=s=>{s.oldVersion<Pt&&Wt(c.result)},vt(o,n,c)});return e.onclose=()=>Je(a),e}function Yt(a){return Te[a]||(Te[a]=Vt(a)),Te[a]}function be(a,e,o,n){return new Promise((c,s)=>{const d=a.transaction(e,o,{durability:"relaxed"}),S=typeof e=="string"?d.objectStore(e):e.map($=>d.objectStore($));let N;n(S,d,$=>{N=$}),d.oncomplete=()=>c(N),d.onerror=()=>s(d.error)})}function Je(a){const e=Re[a],o=e&&e.result;if(o){o.close();const n=Me[a];if(n)for(const c of n)c()}delete Re[a],delete Te[a],delete Me[a]}function Kt(a){return new Promise((e,o)=>{Je(a);const n=indexedDB.deleteDatabase(a);vt(e,o,n)})}function Jt(a,e){let o=Me[a];o||(o=Me[a]=[]),o.push(e)}const Xt=new Set([":D","XD",":'D","O:)",":X",":P",";P","XP",":L",":Z",":j","8D","XO","8)",":B",":O",":S",":'o","Dx","X(","D:",":C",">0)",":3","</3","<3","\\M/",":E","8#"]);function xe(a){return a.split(/[\s_]+/).map(e=>!e.match(/\w/)||Xt.has(e)?e.toLowerCase():e.replace(/[)(:,]/g,"").replace(/’/g,"'").toLowerCase()).filter(Boolean)}const Zt=2;function xt(a){return a.filter(Boolean).map(e=>e.toLowerCase()).filter(e=>e.length>=Zt)}function Qt(a){return a.map(({annotation:o,emoticon:n,group:c,order:s,shortcodes:d,skins:S,tags:N,emoji:$,version:j})=>{const O=[...new Set(xt([...(d||[]).map(xe).flat(),...(N||[]).map(xe).flat(),...xe(o),n]))].sort(),_={annotation:o,group:c,order:s,tags:N,tokens:O,unicode:$,version:j};if(n&&(_.emoticon=n),d&&(_.shortcodes=d),S){_.skinTones=[],_.skinUnicodes=[],_.skinVersions=[];for(const{tone:U,emoji:se,version:ae}of S)_.skinTones.push(U),_.skinUnicodes.push(se),_.skinVersions.push(ae)}return _})}function ft(a,e,o,n){a[e](o).onsuccess=c=>n&&n(c.target.result)}function he(a,e,o){ft(a,"get",e,o)}function wt(a,e,o){ft(a,"getAll",e,o)}function Xe(a){a.commit&&a.commit()}function ea(a,e){let o=a[0];for(let n=1;n<a.length;n++){const c=a[n];e(o)>e(c)&&(o=c)}return o}function kt(a,e){const o=ea(a,c=>c.length),n=[];for(const c of o)a.some(s=>s.findIndex(d=>e(d)===e(c))===-1)||n.push(c);return n}async function ta(a){return!await Ze(a,fe,Be)}async function aa(a,e,o){const[n,c]=await Promise.all([Ue,Be].map(s=>Ze(a,fe,s)));return n===o&&c===e}async function na(a,e){return be(a,ve,we,(n,c,s)=>{let d;const S=()=>{n.getAll(d&&IDBKeyRange.lowerBound(d,!0),50).onsuccess=N=>{const $=N.target.result;for(const j of $)if(d=j.unicode,e(j))return s(j);if($.length<50)return s();S()}};S()})}async function Et(a,e,o,n){try{const c=Qt(e);await be(a,[ve,fe],Ke,([s,d],S)=>{let N,$,j=0;function O(){++j===2&&_()}function _(){if(!(N===n&&$===o)){s.clear();for(const U of c)s.put(U);d.put(n,Ue),d.put(o,Be),Xe(S)}}he(d,Ue,U=>{N=U,O()}),he(d,Be,U=>{$=U,O()})})}finally{}}async function oa(a,e){return be(a,ve,we,(o,n,c)=>{const s=IDBKeyRange.bound([e,0],[e+1,0],!1,!0);wt(o.index(bt),s,c)})}async function Lt(a,e){const o=xt(xe(e));return o.length?be(a,ve,we,(n,c,s)=>{const d=[],S=()=>{d.length===o.length&&N()},N=()=>{const $=kt(d,j=>j.unicode);s($.sort((j,O)=>j.order<O.order?-1:1))};for(let $=0;$<o.length;$++){const j=o[$],O=$===o.length-1?IDBKeyRange.bound(j,j+"",!1,!0):IDBKeyRange.only(j);wt(n.index(mt),O,_=>{d.push(_),S()})}}):[]}async function sa(a,e){const o=await Lt(a,e);return o.length?o.filter(n=>(n.shortcodes||[]).map(s=>s.toLowerCase()).includes(e.toLowerCase()))[0]||null:await na(a,c=>(c.shortcodes||[]).includes(e.toLowerCase()))||null}async function ia(a,e){return be(a,ve,we,(o,n,c)=>he(o,e,s=>{if(s)return c(s);he(o.index(ht),e,d=>c(d||null))}))}function Ze(a,e,o){return be(a,e,we,(n,c,s)=>he(n,o,s))}function ra(a,e,o,n){return be(a,e,Ke,(c,s)=>{c.put(n,o),Xe(s)})}function da(a,e){return be(a,Ye,Ke,(o,n)=>he(o,e,c=>{o.put((c||0)+1,e),Xe(n)}))}function la(a,e,o){return o===0?[]:be(a,[Ye,ve],we,([n,c],s,d)=>{const S=[];n.index(yt).openCursor(void 0,"prev").onsuccess=N=>{const $=N.target.result;if(!$)return d(S);function j(U){if(S.push(U),S.length===o)return d(S);$.continue()}const O=$.primaryKey,_=e.byName(O);if(_)return j(_);he(c,O,U=>{if(U)return j(U);$.continue()})}})}const $e="";function ca(a,e){const o=new Map;for(const c of a){const s=e(c);for(const d of s){let S=o;for(let $=0;$<d.length;$++){const j=d.charAt($);let O=S.get(j);O||(O=new Map,S.set(j,O)),S=O}let N=S.get($e);N||(N=[],S.set($e,N)),N.push(c)}}return(c,s)=>{let d=o;for(let $=0;$<c.length;$++){const j=c.charAt($),O=d.get(j);if(O)d=O;else return[]}if(s)return d.get($e)||[];const S=[],N=[d];for(;N.length;){const j=[...N.shift().entries()].sort((O,_)=>O[0]<_[0]?-1:1);for(const[O,_]of j)O===$e?S.push(..._):N.push(_)}return S}}const pa=["name","url"];function ua(a){const e=a&&Array.isArray(a),o=e&&a.length&&(!a[0]||pa.some(n=>!(n in a[0])));if(!e||o)throw new Error("Custom emojis are in the wrong format")}function ot(a){ua(a);const e=(_,U)=>_.name.toLowerCase()<U.name.toLowerCase()?-1:1,o=a.sort(e),c=ca(a,_=>{const U=new Set;if(_.shortcodes)for(const se of _.shortcodes)for(const ae of xe(se))U.add(ae);return U}),s=_=>c(_,!0),d=_=>c(_,!1),S=_=>{const U=xe(_),se=U.map((ae,re)=>(re<U.length-1?s:d)(ae));return kt(se,ae=>ae.name).sort(e)},N=new Map,$=new Map;for(const _ of a){$.set(_.name.toLowerCase(),_);for(const U of _.shortcodes||[])N.set(U.toLowerCase(),_)}return{all:o,search:S,byShortcode:_=>N.get(_.toLowerCase()),byName:_=>$.get(_.toLowerCase())}}const ga=typeof wrappedJSObject<"u";function ke(a){if(!a)return a;if(ga&&(a=structuredClone(a)),delete a.tokens,a.skinTones){const e=a.skinTones.length;a.skins=Array(e);for(let o=0;o<e;o++)a.skins[o]={tone:a.skinTones[o],unicode:a.skinUnicodes[o],version:a.skinVersions[o]};delete a.skinTones,delete a.skinUnicodes,delete a.skinVersions}return a}function $t(a){a||console.warn("emoji-picker-element is more efficient if the dataSource server exposes an ETag header.")}const ma=["annotation","emoji","group","order","version"];function ya(a){if(!a||!Array.isArray(a)||!a[0]||typeof a[0]!="object"||ma.some(e=>!(e in a[0])))throw new Error("Emoji data is in the wrong format")}function It(a,e){if(Math.floor(a.status/100)!==2)throw new Error("Failed to fetch: "+e+": "+a.status)}async function ba(a){const e=await fetch(a,{method:"HEAD"});It(e,a);const o=e.headers.get("etag");return $t(o),o}async function Ge(a){const e=await fetch(a);It(e,a);const o=e.headers.get("etag");$t(o);const n=await e.json();return ya(n),[o,n]}function ha(a){for(var e="",o=new Uint8Array(a),n=o.byteLength,c=-1;++c<n;)e+=String.fromCharCode(o[c]);return e}function va(a){for(var e=a.length,o=new ArrayBuffer(e),n=new Uint8Array(o),c=-1;++c<e;)n[c]=a.charCodeAt(c);return o}async function Tt(a){const e=JSON.stringify(a);let o=va(e);const n=await crypto.subtle.digest("SHA-1",o),c=ha(n);return btoa(c)}async function xa(a,e){let o,n=await ba(e);if(!n){const c=await Ge(e);n=c[0],o=c[1],n||(n=await Tt(o))}await aa(a,e,n)||(o||(o=(await Ge(e))[1]),await Et(a,o,e,n))}async function fa(a,e){let[o,n]=await Ge(e);o||(o=await Tt(n)),await Et(a,n,e,o)}async function wa(a,e){try{await xa(a,e)}catch(o){if(o.name!=="InvalidStateError")throw o}}class ka{constructor({dataSource:e=Ut,locale:o=Rt,customEmoji:n=[]}={}){this.dataSource=e,this.locale=o,this._dbName=`emoji-picker-element-${this.locale}`,this._db=void 0,this._lazyUpdate=void 0,this._custom=ot(n),this._clear=this._clear.bind(this),this._ready=this._init()}async _init(){const e=this._db=await Yt(this._dbName);Jt(this._dbName,this._clear);const o=this.dataSource;await ta(e)?await fa(e,o):this._lazyUpdate=wa(e,o)}async ready(){const e=async()=>(this._ready||(this._ready=this._init()),this._ready);await e(),this._db||await e()}async getEmojiByGroup(e){return _e(e),await this.ready(),nt(await oa(this._db,e)).map(ke)}async getEmojiBySearchQuery(e){Le(e),await this.ready();const o=this._custom.search(e),n=nt(await Lt(this._db,e)).map(ke);return[...o,...n]}async getEmojiByShortcode(e){Le(e),await this.ready();const o=this._custom.byShortcode(e);return o||ke(await sa(this._db,e))}async getEmojiByUnicodeOrName(e){Le(e),await this.ready();const o=this._custom.byName(e);return o||ke(await ia(this._db,e))}async getPreferredSkinTone(){return await this.ready(),await Ze(this._db,fe,at)||0}async setPreferredSkinTone(e){return _e(e),await this.ready(),ra(this._db,fe,at,e)}async incrementFavoriteEmojiCount(e){return Le(e),await this.ready(),da(this._db,e)}async getTopFavoriteEmoji(e){return _e(e),await this.ready(),(await la(this._db,this._custom,e)).map(ke)}set customEmoji(e){this._custom=ot(e)}get customEmoji(){return this._custom.all}async _shutdown(){await this.ready();try{await this._lazyUpdate}catch{}}_clear(){this._db=this._ready=this._lazyUpdate=void 0}async close(){await this._shutdown(),await Je(this._dbName)}async delete(){await this._shutdown(),await Kt(this._dbName)}}const We=[[-1,"✨","custom"],[0,"😀","smileys-emotion"],[1,"👋","people-body"],[3,"🐱","animals-nature"],[4,"🍎","food-drink"],[5,"🏠️","travel-places"],[6,"⚽","activities"],[7,"📝","objects"],[8,"⛔️","symbols"],[9,"🏁","flags"]].map(([a,e,o])=>({id:a,emoji:e,name:o})),Ae=We.slice(1),Ea=2,st=6,St=typeof requestIdleCallback=="function"?requestIdleCallback:setTimeout;function it(a){return a.unicode.includes("")}const La={"":17,"":16,"🫨":15.1,"🫠":14,"🥲":13.1,"🥻":12.1,"🥰":11,"🤩":5,"👱♀️":4,"🤣":3,"👁️🗨️":2,"😀":1,"😐️":.7,"😃":.6},$a=1e3,Ia="🖐️",Ta=8,Sa=["😊","😒","❤️","👍️","😍","😂","😭","☺️","😔","😩","😏","💕","🙌","😘"],Bt='"Twemoji Mozilla","Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji","EmojiOne Color","Android Emoji",sans-serif',Ba=(a,e)=>a<e?-1:a>e?1:0,rt=(a,e)=>{const o=document.createElement("canvas");o.width=o.height=1;const n=o.getContext("2d",{willReadFrequently:!0});return n.textBaseline="top",n.font=`100px ${Bt}`,n.fillStyle=e,n.scale(.01,.01),n.fillText(a,0,0),n.getImageData(0,0,1,1).data},Ma=(a,e)=>{const o=[...a].join(","),n=[...e].join(",");return o===n&&!o.startsWith("0,0,0,")};function Ca(a){const e=rt(a,"#000"),o=rt(a,"#fff");return e&&o&&Ma(e,o)}function za(){const a=Object.entries(La);try{for(const[e,o]of a)if(Ca(e))return o}catch{}finally{}return a[0][1]}let Pe;const Fe=()=>(Pe||(Pe=new Promise(a=>St(()=>a(za())))),Pe),Ve=new Map,Da="️",ja="\uD83C",_a="",Aa=127995,Pa=57339;function Fa(a,e){if(e===0)return a;const o=a.indexOf(_a);return o!==-1?a.substring(0,o)+String.fromCodePoint(Aa+e-1)+a.substring(o):(a.endsWith(Da)&&(a=a.substring(0,a.length-1)),a+ja+String.fromCodePoint(Pa+e-1))}function ye(a){a.preventDefault(),a.stopPropagation()}function Oe(a,e,o){return e+=a?-1:1,e<0?e=o.length-1:e>=o.length&&(e=0),e}function Mt(a,e){const o=new Set,n=[];for(const c of a){const s=e(c);o.has(s)||(o.add(s),n.push(c))}return n}function Oa(a,e){const o=n=>{const c={};for(const s of n)typeof s.tone=="number"&&s.version<=e&&(c[s.tone]=s.unicode);return c};return a.map(({unicode:n,skins:c,shortcodes:s,url:d,name:S,category:N,annotation:$})=>({unicode:n,name:S,shortcodes:s,url:d,category:N,annotation:$,id:n||S,skins:c&&o(c)}))}const Se=requestAnimationFrame;let Ha=typeof ResizeObserver=="function";function Na(a,e,o){let n;Ha?(n=new ResizeObserver(o),n.observe(a)):Se(o),e.addEventListener("abort",()=>{n&&n.disconnect()})}function dt(a){{const e=document.createRange();return e.selectNode(a.firstChild),e.getBoundingClientRect().width}}let He;function qa(a,e,o){let n=!0;for(const c of a){const s=o(c);if(!s)continue;const d=dt(s);typeof He>"u"&&(He=dt(e));const S=d/1.8<He;Ve.set(c.unicode,S),S||(n=!1)}return n}function Ua(a){return Mt(a,e=>e)}function Ra(a){a&&(a.scrollTop=0)}function Ee(a,e,o){let n=a.get(e);return n||(n=o(),a.set(e,n)),n}function lt(a){return""+a}function Ga(a){const e=document.createElement("template");return e.innerHTML=a,e}const Wa=new WeakMap,Va=new WeakMap,Ya=Symbol("un-keyed"),Ka="replaceChildren"in Element.prototype;function Ja(a,e){Ka?a.replaceChildren(...e):(a.innerHTML="",a.append(...e))}function Xa(a,e){let o=a.firstChild,n=0;for(;o;){if(e[n]!==o)return!0;o=o.nextSibling,n++}return n!==e.length}function Za(a,e){const{targetNode:o}=e;let{targetParentNode:n}=e,c=!1;n?c=Xa(n,a):(c=!0,e.targetNode=void 0,e.targetParentNode=n=o.parentNode),c&&Ja(n,a)}function Qa(a,e){for(const o of e){const{targetNode:n,currentExpression:c,binding:{expressionIndex:s,attributeName:d,attributeValuePre:S,attributeValuePost:N}}=o,$=a[s];if(c!==$)if(o.currentExpression=$,d)if($===null)n.removeAttribute(d);else{const j=S+lt($)+N;n.setAttribute(d,j)}else{let j;Array.isArray($)?Za($,o):$ instanceof Element?(j=$,n.replaceWith(j)):n.nodeValue=lt($),j&&(o.targetNode=j)}}}function en(a){let e="",o=!1,n=!1,c=-1;const s=new Map,d=[];let S=0;for(let $=0,j=a.length;$<j;$++){const O=a[$];if(e+=O.slice(S),$===j-1)break;for(let q=0;q<O.length;q++)switch(O.charAt(q)){case"<":{O.charAt(q+1)==="/"?d.pop():(o=!0,d.push(++c));break}case">":{o=!1,n=!1;break}case"=":{n=!0;break}}const _=d[d.length-1],U=Ee(s,_,()=>[]);let se,ae,re;if(n){const q=/(\S+)="?([^"=]*)$/.exec(O);se=q[1],ae=q[2];const V=/^([^">]*)("?)/.exec(a[$+1]);re=V[1],e=e.slice(0,-1*q[0].length),S=V[0].length}else S=0;const pe={attributeName:se,attributeValuePre:ae,attributeValuePost:re,expressionIndex:$};U.push(pe),!o&&!n&&(e+=" ")}return{template:Ga(e),elementsToBindings:s}}function ct(a,e,o){for(let n=0;n<a.length;n++){const c=a[n],s=c.attributeName?e:e.firstChild,d={binding:c,targetNode:s,targetParentNode:void 0,currentExpression:void 0};o.push(d)}}function tn(a,e){const o=[];let n;if(e.size===1&&(n=e.get(0)))ct(n,a,o);else{const c=document.createTreeWalker(a,NodeFilter.SHOW_ELEMENT);let s=a,d=-1;do{const S=e.get(++d);S&&ct(S,s,o)}while(s=c.nextNode())}return o}function an(a){const{template:e,elementsToBindings:o}=Ee(Wa,a,()=>en(a)),n=e.cloneNode(!0).content.firstElementChild,c=tn(n,o);return function(d){return Qa(d,c),n}}function nn(a){const e=Ee(Va,a,()=>new Map);let o=Ya;function n(s,...d){const S=Ee(e,s,()=>new Map);return Ee(S,o,()=>an(s))(d)}function c(s,d,S){return s.map((N,$)=>{const j=o;o=S(N);try{return d(N,$)}finally{o=j}})}return{map:c,html:n}}function on(a,e,o,n,c,s,d,S,N){const{labelWithSkin:$,titleForEmoji:j,unicodeWithSkin:O}=o,{html:_,map:U}=nn(e);function se(q,V,ne){return U(q,(oe,le)=>_`<button role="${V?"option":"menuitem"}" aria-selected="${V?le===e.activeSearchItem:null}" aria-label="${$(oe,e.currentSkinTone)}" title="${j(oe)}" class="${"emoji"+(V&&le===e.activeSearchItem?" active":"")+(oe.unicode?"":" custom-emoji")}" id="${`${ne}-${oe.id}`}" style="${oe.unicode?null:`--custom-emoji-background: url(${JSON.stringify(oe.url)})`}">${oe.unicode?O(oe,e.currentSkinTone):""}</button>`,oe=>`${ne}-${oe.id}`)}const re=_`<section data-ref="rootElement" class="picker" aria-label="${e.i18n.regionLabel}" style="${e.pickerStyle||""}"><div class="pad-top"></div><div class="search-row"><div class="search-wrapper"><input id="search" class="search" type="search" role="combobox" enterkeyhint="search" placeholder="${e.i18n.searchLabel}" autocapitalize="none" autocomplete="off" spellcheck="true" aria-expanded="${!!(e.searchMode&&e.currentEmojis.length)}" aria-controls="search-results" aria-describedby="search-description" aria-autocomplete="list" aria-activedescendant="${e.activeSearchItemId?`emo-${e.activeSearchItemId}`:null}" data-ref="searchElement" data-on-input="onSearchInput" data-on-keydown="onSearchKeydown"><label class="sr-only" for="search">${e.i18n.searchLabel}</label> <span id="search-description" class="sr-only">${e.i18n.searchDescription}</span></div><div class="skintone-button-wrapper ${e.skinTonePickerExpandedAfterAnimation?"expanded":""}"><button id="skintone-button" class="emoji ${e.skinTonePickerExpanded?"hide-focus":""}" aria-label="${e.skinToneButtonLabel}" title="${e.skinToneButtonLabel}" aria-describedby="skintone-description" aria-haspopup="listbox" aria-expanded="${e.skinTonePickerExpanded}" aria-controls="skintone-list" data-on-click="onClickSkinToneButton">${e.skinToneButtonText||""}</button></div><span id="skintone-description" class="sr-only">${e.i18n.skinToneDescription}</span><div data-ref="skinToneDropdown" id="skintone-list" class="skintone-list hide-focus ${e.skinTonePickerExpanded?"":"hidden no-animate"}" style="transform:translateY(${e.skinTonePickerExpanded?0:"calc(-1 * var(--num-skintones) * var(--total-emoji-size))"})" role="listbox" aria-label="${e.i18n.skinTonesLabel}" aria-activedescendant="skintone-${e.activeSkinTone}" aria-hidden="${!e.skinTonePickerExpanded}" tabIndex="-1" data-on-focusout="onSkinToneOptionsFocusOut" data-on-click="onSkinToneOptionsClick" data-on-keydown="onSkinToneOptionsKeydown" data-on-keyup="onSkinToneOptionsKeyup">${U(e.skinTones,(q,V)=>_`<div id="skintone-${V}" class="emoji ${V===e.activeSkinTone?"active":""}" aria-selected="${V===e.activeSkinTone}" role="option" title="${e.i18n.skinTones[V]}" aria-label="${e.i18n.skinTones[V]}">${q}</div>`,q=>q)}</div></div><div class="nav" role="tablist" style="grid-template-columns:repeat(${e.groups.length},1fr)" aria-label="${e.i18n.categoriesLabel}" data-on-keydown="onNavKeydown" data-on-click="onNavClick">${U(e.groups,q=>_`<button role="tab" class="nav-button" aria-controls="tab-${q.id}" aria-label="${e.i18n.categories[q.name]}" aria-selected="${!e.searchMode&&e.currentGroup.id===q.id}" title="${e.i18n.categories[q.name]}" data-group-id="${q.id}"><div class="nav-emoji emoji">${q.emoji}</div></button>`,q=>q.id)}</div><div class="indicator-wrapper"><div class="indicator" style="transform:translateX(${(e.isRtl?-1:1)*e.currentGroupIndex*100}%)"></div></div><div class="message ${e.message?"":"gone"}" role="alert" aria-live="polite">${e.message||""}</div><div data-ref="tabpanelElement" class="tabpanel ${!e.databaseLoaded||e.message?"gone":""}" role="${e.searchMode?"region":"tabpanel"}" aria-label="${e.searchMode?e.i18n.searchResultsLabel:e.i18n.categories[e.currentGroup.name]}" id="${e.searchMode?null:`tab-${e.currentGroup.id}`}" tabIndex="0" data-on-click="onEmojiClick"><div data-action="calculateEmojiGridStyle">${U(e.currentEmojisWithCategories,(q,V)=>_`<div><div id="menu-label-${V}" class="category ${e.currentEmojisWithCategories.length===1&&e.currentEmojisWithCategories[0].category===""?"gone":""}" aria-hidden="true">${e.searchMode?e.i18n.searchResultsLabel:q.category?q.category:e.currentEmojisWithCategories.length>1?e.i18n.categories.custom:e.i18n.categories[e.currentGroup.name]}</div><div class="emoji-menu ${V!==0&&!e.searchMode&&e.currentGroup.id===-1?"visibility-auto":""}" style="${`--num-rows: ${Math.ceil(q.emojis.length/e.numColumns)}`}" data-action="updateOnIntersection" role="${e.searchMode?"listbox":"menu"}" aria-labelledby="menu-label-${V}" id="${e.searchMode?"search-results":null}">${se(q.emojis,e.searchMode,"emo")}</div></div>`,q=>q.category)}</div></div><div class="favorites onscreen emoji-menu ${e.message?"gone":""}" role="menu" aria-label="${e.i18n.favoritesLabel}" data-on-click="onEmojiClick">${se(e.currentFavorites,!1,"fav")}</div><button data-ref="baselineEmoji" aria-hidden="true" tabindex="-1" class="abs-pos hidden emoji baseline-emoji">😀</button></section>`,pe=(q,V)=>{for(const ne of a.querySelectorAll(`[${q}]`))V(ne,ne.getAttribute(q))};if(N){a.appendChild(re);for(const q of["click","focusout","input","keydown","keyup"])pe(`data-on-${q}`,(V,ne)=>{V.addEventListener(q,n[ne])});pe("data-ref",(q,V)=>{s[V]=q}),d.addEventListener("abort",()=>{a.removeChild(re)})}pe("data-action",(q,V)=>{let ne=S.get(V);ne||S.set(V,ne=new WeakSet),ne.has(q)||(ne.add(q),c[V](q))})}const Ce=typeof queueMicrotask=="function"?queueMicrotask:a=>Promise.resolve().then(a);function sn(a){let e=!1,o;const n=new Map,c=new Set;let s;const d=()=>{if(e)return;const $=[...c];c.clear();try{for(const j of $)j()}finally{s=!1,c.size&&(s=!0,Ce(d))}},S=new Proxy({},{get($,j){if(o){let O=n.get(j);O||(O=new Set,n.set(j,O)),O.add(o)}return $[j]},set($,j,O){if($[j]!==O){$[j]=O;const _=n.get(j);if(_){for(const U of _)c.add(U);s||(s=!0,Ce(d))}}return!0}}),N=$=>{const j=()=>{const O=o;o=j;try{return $()}finally{o=O}};return j()};return a.addEventListener("abort",()=>{e=!0}),{state:S,createEffect:N}}function Ne(a,e,o){if(a.length!==e.length)return!1;for(let n=0;n<a.length;n++)if(!o(a[n],e[n]))return!1;return!0}const pt=new WeakMap;function rn(a,e,o){{const n=a.closest(".tabpanel");let c=pt.get(n);c||(c=new IntersectionObserver(o,{root:n,rootMargin:"50% 0px 50% 0px",threshold:0}),pt.set(n,c),e.addEventListener("abort",()=>{c.disconnect()})),c.observe(a)}}const qe=[],{assign:Ie}=Object;function dn(a,e){const o={},n=new AbortController,c=n.signal,{state:s,createEffect:d}=sn(c),S=new Map;Ie(s,{skinToneEmoji:void 0,i18n:void 0,database:void 0,customEmoji:void 0,customCategorySorting:void 0,emojiVersion:void 0}),Ie(s,e),Ie(s,{initialLoad:!0,currentEmojis:[],currentEmojisWithCategories:[],rawSearchText:"",searchText:"",searchMode:!1,activeSearchItem:-1,message:void 0,skinTonePickerExpanded:!1,skinTonePickerExpandedAfterAnimation:!1,currentSkinTone:0,activeSkinTone:0,skinToneButtonText:void 0,pickerStyle:void 0,skinToneButtonLabel:"",skinTones:[],currentFavorites:[],defaultFavoriteEmojis:void 0,numColumns:Ta,isRtl:!1,currentGroupIndex:0,groups:Ae,databaseLoaded:!1,activeSearchItemId:void 0}),d(()=>{s.currentGroup!==s.groups[s.currentGroupIndex]&&(s.currentGroup=s.groups[s.currentGroupIndex])});const N=t=>{a.getElementById(t).focus()},$=t=>a.getElementById(`emo-${t.id}`),j=(t,r)=>{o.rootElement.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0}))},O=(t,r)=>t.id===r.id,_=(t,r)=>{const{category:l,emojis:b}=t,{category:E,emojis:w}=r;return l!==E?!1:Ne(b,w,O)},U=t=>{Ne(s.currentEmojis,t,O)||(s.currentEmojis=t)},se=t=>{s.searchMode!==t&&(s.searchMode=t)},ae=t=>{Ne(s.currentEmojisWithCategories,t,_)||(s.currentEmojisWithCategories=t)},re=(t,r)=>r&&t.skins&&t.skins[r]||t.unicode,V={labelWithSkin:(t,r)=>Ua([t.name||re(t,r),t.annotation,...t.shortcodes||qe].filter(Boolean)).join(", "),titleForEmoji:t=>t.annotation||(t.shortcodes||qe).join(", "),unicodeWithSkin:re},ne={onClickSkinToneButton:y,onEmojiClick:m,onNavClick:de,onNavKeydown:X,onSearchKeydown:F,onSkinToneOptionsClick:g,onSkinToneOptionsFocusOut:p,onSkinToneOptionsKeydown:h,onSkinToneOptionsKeyup:M,onSearchInput:i},oe={calculateEmojiGridStyle:I,updateOnIntersection:A};let le=!0;d(()=>{on(a,s,V,ne,oe,o,c,S,le),le=!1}),s.emojiVersion||Fe().then(t=>{t||(s.message=s.i18n.emojiUnsupportedMessage)}),d(()=>{async function t(){let r=!1;const l=setTimeout(()=>{r=!0,s.message=s.i18n.loadingMessage},$a);try{await s.database.ready(),s.databaseLoaded=!0}catch(b){console.error(b),s.message=s.i18n.networkErrorMessage}finally{clearTimeout(l),r&&(r=!1,s.message="")}}s.database&&t()}),d(()=>{s.pickerStyle=`
|
|
52
52
|
--num-groups: ${s.groups.length};
|
|
53
53
|
--indicator-opacity: ${s.searchMode?0:1};
|
|
54
|
-
--num-skintones: ${st};`}),d(()=>{s.customEmoji&&s.database&&B()}),d(()=>{s.customEmoji&&s.customEmoji.length?s.groups!==We&&(s.groups=We):s.groups!==Ae&&(s.currentGroupIndex&&s.currentGroupIndex--,s.groups=Ae)}),d(()=>{async function t(){s.databaseLoaded&&(s.currentSkinTone=await s.database.getPreferredSkinTone())}t()}),d(()=>{s.skinTones=Array(st).fill().map((t,r)=>Fa(s.skinToneEmoji,r))}),d(()=>{s.skinToneButtonText=s.skinTones[s.currentSkinTone]}),d(()=>{s.skinToneButtonLabel=s.i18n.skinToneLabel.replace("{skinTone}",s.i18n.skinTones[s.currentSkinTone])}),d(()=>{async function t(){const{database:r}=s,l=(await Promise.all(Sa.map(b=>r.getEmojiByUnicodeOrName(b)))).filter(Boolean);s.defaultFavoriteEmojis=l}s.databaseLoaded&&t()});function B(){const{customEmoji:t,database:r}=s,l=t||qe;r.customEmoji!==l&&(r.customEmoji=l)}d(()=>{async function t(){B();const{database:r,defaultFavoriteEmojis:l,numColumns:b}=s,E=await r.getTopFavoriteEmoji(b),w=await L(Mt([...E,...l],P=>P.unicode||P.name).slice(0,b));s.currentFavorites=w}s.databaseLoaded&&s.defaultFavoriteEmojis&&t()});function I(t){Na(t,c,()=>{{const r=getComputedStyle(o.rootElement),l=parseInt(r.getPropertyValue("--num-columns"),10),b=r.getPropertyValue("direction")==="rtl";s.numColumns=l,s.isRtl=b}})}function A(t){rn(t,c,r=>{for(const{target:l,isIntersecting:b}of r)l.classList.toggle("onscreen",b)})}d(()=>{async function t(){const{searchText:r,currentGroup:l,databaseLoaded:b,customEmoji:E}=s;if(!b)s.currentEmojis=[],s.searchMode=!1;else if(r.length>=Ea){const w=await W(r);s.searchText===r&&(U(w),se(!0))}else{const{id:w}=l;if(w!==-1||E&&E.length){const P=await z(w);s.currentGroup.id===w&&(U(P),se(!1))}}}t()});const v=()=>{Se(()=>Ra(o.tabpanelElement))};d(()=>{const{currentEmojis:t,emojiVersion:r}=s,l=t.filter(b=>b.unicode).filter(b=>it(b)&&!Ve.has(b.unicode));if(!r&&l.length)U(t),Se(()=>k(l));else{const b=r?t:t.filter(C);U(b),v()}});function k(t){qa(t,o.baselineEmoji,$)?v():s.currentEmojis=[...s.currentEmojis]}function C(t){return!t.unicode||!it(t)||Ve.get(t.unicode)}async function D(t){const r=s.emojiVersion||await Fe();return t.filter(({version:l})=>!l||l<=r)}async function L(t){return Oa(t,s.emojiVersion||await Fe())}async function z(t){const r=t===-1?s.customEmoji:await s.database.getEmojiByGroup(t);return L(await D(r))}async function W(t){return L(await D(await s.database.getEmojiBySearchQuery(t)))}d(()=>{}),d(()=>{function t(){const{searchMode:l,currentEmojis:b}=s;if(l)return[{category:"",emojis:b}];const E=new Map;for(const w of b){const P=w.category||"";let G=E.get(P);G||(G=[],E.set(P,G)),G.push(w)}return[...E.entries()].map(([w,P])=>({category:w,emojis:P})).sort((w,P)=>s.customCategorySorting(w.category,P.category))}const r=t();ae(r)}),d(()=>{s.activeSearchItemId=s.activeSearchItem!==-1&&s.currentEmojis[s.activeSearchItem].id}),d(()=>{const{rawSearchText:t}=s;St(()=>{s.searchText=(t||"").trim(),s.activeSearchItem=-1})});function F(t){if(!s.searchMode||!s.currentEmojis.length)return;const r=l=>{ye(t),s.activeSearchItem=Oe(l,s.activeSearchItem,s.currentEmojis)};switch(t.key){case"ArrowDown":return r(!1);case"ArrowUp":return r(!0);case"Enter":if(s.activeSearchItem===-1)s.activeSearchItem=0;else return ye(t),ue(s.currentEmojis[s.activeSearchItem].id)}}function re(t){const{target:r}=t,l=r.closest(".nav-button");if(!l)return;const b=parseInt(l.dataset.groupId,10);o.searchElement.value="",s.rawSearchText="",s.searchText="",s.activeSearchItem=-1,s.currentGroupIndex=s.groups.findIndex(E=>E.id===b)}function X(t){const{target:r,key:l}=t,b=E=>{E&&(ye(t),E.focus())};switch(l){case"ArrowLeft":return b(r.previousElementSibling);case"ArrowRight":return b(r.nextElementSibling);case"Home":return b(r.parentElement.firstElementChild);case"End":return b(r.parentElement.lastElementChild)}}async function ce(t){const r=await s.database.getEmojiByUnicodeOrName(t),l=[...s.currentEmojis,...s.currentFavorites].find(E=>E.id===t),b=l.unicode&&ie(l,s.currentSkinTone);return await s.database.incrementFavoriteEmojiCount(t),{emoji:r,skinTone:s.currentSkinTone,...b&&{unicode:b},...l.name&&{name:l.name}}}async function ue(t){const r=ce(t);j("emoji-click-sync",r),j("emoji-click",await r)}function m(t){const{target:r}=t;if(!r.classList.contains("emoji"))return;ye(t);const l=r.id.substring(4);ue(l)}function u(t){s.currentSkinTone=t,s.skinTonePickerExpanded=!1,H("skintone-button"),j("skin-tone-change",{skinTone:t}),s.database.setPreferredSkinTone(t)}function g(t){const{target:{id:r}}=t,l=r&&r.match(/^skintone-(\d)/);if(!l)return;ye(t);const b=parseInt(l[1],10);u(b)}function y(t){s.skinTonePickerExpanded=!s.skinTonePickerExpanded,s.activeSkinTone=s.currentSkinTone,s.skinTonePickerExpanded&&(ye(t),Se(()=>H("skintone-list")))}d(()=>{s.skinTonePickerExpanded?o.skinToneDropdown.addEventListener("transitionend",()=>{s.skinTonePickerExpandedAfterAnimation=!0},{once:!0}):s.skinTonePickerExpandedAfterAnimation=!1});function h(t){if(!s.skinTonePickerExpanded)return;const r=async l=>{ye(t),s.activeSkinTone=l};switch(t.key){case"ArrowUp":return r(Oe(!0,s.activeSkinTone,s.skinTones));case"ArrowDown":return r(Oe(!1,s.activeSkinTone,s.skinTones));case"Home":return r(0);case"End":return r(s.skinTones.length-1);case"Enter":return ye(t),u(s.activeSkinTone);case"Escape":return ye(t),s.skinTonePickerExpanded=!1,H("skintone-button")}}function M(t){if(s.skinTonePickerExpanded)switch(t.key){case" ":return ye(t),u(s.activeSkinTone)}}async function p(t){const{relatedTarget:r}=t;(!r||r.id!=="skintone-list")&&(s.skinTonePickerExpanded=!1)}function i(t){s.rawSearchText=t.target.value}return{$set(t){Ie(s,t)},$destroy(){n.abort()}}}const ln="https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/en/emojibase/data.json",cn="en";var pn={categoriesLabel:"Categories",emojiUnsupportedMessage:"Your browser does not support color emoji.",favoritesLabel:"Favorites",loadingMessage:"Loading…",networkErrorMessage:"Could not load emoji.",regionLabel:"Emoji picker",searchDescription:"When search results are available, press up or down to select and enter to choose.",searchLabel:"Search",searchResultsLabel:"Search results",skinToneDescription:"When expanded, press up or down to select and enter to choose.",skinToneLabel:"Choose a skin tone (currently {skinTone})",skinTonesLabel:"Skin tones",skinTones:["Default","Light","Medium-Light","Medium","Medium-Dark","Dark"],categories:{custom:"Custom","smileys-emotion":"Smileys and emoticons","people-body":"People and body","animals-nature":"Animals and nature","food-drink":"Food and drink","travel-places":"Travel and places",activities:"Activities",objects:"Objects",symbols:"Symbols",flags:"Flags"}},un=':host{--emoji-size:1.375rem;--emoji-padding:0.5rem;--category-emoji-size:var(--emoji-size);--category-emoji-padding:var(--emoji-padding);--indicator-height:3px;--input-border-radius:0.5rem;--input-border-size:1px;--input-font-size:1rem;--input-line-height:1.5;--input-padding:0.25rem;--num-columns:8;--outline-size:2px;--border-size:1px;--border-radius:0;--skintone-border-radius:1rem;--category-font-size:1rem;display:flex;width:min-content;height:400px}:host,:host(.light){color-scheme:light;--background:#fff;--border-color:#e0e0e0;--indicator-color:#385ac1;--input-border-color:#999;--input-font-color:#111;--input-placeholder-color:#999;--outline-color:#999;--category-font-color:#111;--button-active-background:#e6e6e6;--button-hover-background:#d9d9d9}:host(.dark){color-scheme:dark;--background:#222;--border-color:#444;--indicator-color:#5373ec;--input-border-color:#ccc;--input-font-color:#efefef;--input-placeholder-color:#ccc;--outline-color:#fff;--category-font-color:#efefef;--button-active-background:#555555;--button-hover-background:#484848}@media (prefers-color-scheme:dark){:host{color-scheme:dark;--background:#222;--border-color:#444;--indicator-color:#5373ec;--input-border-color:#ccc;--input-font-color:#efefef;--input-placeholder-color:#ccc;--outline-color:#fff;--category-font-color:#efefef;--button-active-background:#555555;--button-hover-background:#484848}}:host([hidden]){display:none}button{margin:0;padding:0;border:0;background:0 0;box-shadow:none;-webkit-tap-highlight-color:transparent}button::-moz-focus-inner{border:0}input{padding:0;margin:0;line-height:1.15;font-family:inherit}input[type=search]{-webkit-appearance:none}:focus{outline:var(--outline-color) solid var(--outline-size);outline-offset:calc(-1*var(--outline-size))}:host([data-js-focus-visible]) :focus:not([data-focus-visible-added]){outline:0}:focus:not(:focus-visible){outline:0}.hide-focus{outline:0}*{box-sizing:border-box}.picker{contain:content;display:flex;flex-direction:column;background:var(--background);border:var(--border-size) solid var(--border-color);border-radius:var(--border-radius);width:100%;height:100%;overflow:hidden;--total-emoji-size:calc(var(--emoji-size) + (2 * var(--emoji-padding)));--total-category-emoji-size:calc(var(--category-emoji-size) + (2 * var(--category-emoji-padding)))}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.hidden{opacity:0;pointer-events:none}.abs-pos{position:absolute;left:0;top:0}.gone{display:none!important}.skintone-button-wrapper,.skintone-list{background:var(--background);z-index:3}.skintone-button-wrapper.expanded{z-index:1}.skintone-list{position:absolute;inset-inline-end:0;top:0;z-index:2;overflow:visible;border-bottom:var(--border-size) solid var(--border-color);border-radius:0 0 var(--skintone-border-radius) var(--skintone-border-radius);will-change:transform;transition:transform .2s ease-in-out;transform-origin:center 0}@media (prefers-reduced-motion:reduce){.skintone-list{transition-duration:.001s}}@supports not (inset-inline-end:0){.skintone-list{right:0}}.skintone-list.no-animate{transition:none}.tabpanel{overflow-y:auto;scrollbar-gutter:stable;-webkit-overflow-scrolling:touch;will-change:transform;min-height:0;flex:1;contain:content}.emoji-menu{display:grid;grid-template-columns:repeat(var(--num-columns),var(--total-emoji-size));justify-content:space-around;align-items:flex-start;width:100%}.emoji-menu.visibility-auto{content-visibility:auto;contain-intrinsic-size:calc(var(--num-columns)*var(--total-emoji-size)) calc(var(--num-rows)*var(--total-emoji-size))}.category{padding:var(--emoji-padding);font-size:var(--category-font-size);color:var(--category-font-color)}.emoji,button.emoji{font-size:var(--emoji-size);display:flex;align-items:center;justify-content:center;border-radius:100%;height:var(--total-emoji-size);width:var(--total-emoji-size);line-height:1;overflow:hidden;font-family:var(--emoji-font-family);cursor:pointer}@media (hover:hover) and (pointer:fine){.emoji:hover,button.emoji:hover{background:var(--button-hover-background)}}.emoji.active,.emoji:active,button.emoji.active,button.emoji:active{background:var(--button-active-background)}.onscreen .custom-emoji::after{content:"";width:var(--emoji-size);height:var(--emoji-size);background-repeat:no-repeat;background-position:center center;background-size:contain;background-image:var(--custom-emoji-background)}.nav,.nav-button{align-items:center}.nav{display:grid;justify-content:space-between;contain:content}.nav-button{display:flex;justify-content:center}.nav-emoji{font-size:var(--category-emoji-size);width:var(--total-category-emoji-size);height:var(--total-category-emoji-size)}.indicator-wrapper{display:flex;border-bottom:1px solid var(--border-color)}.indicator{width:calc(100%/var(--num-groups));height:var(--indicator-height);opacity:var(--indicator-opacity);background-color:var(--indicator-color);will-change:transform,opacity;transition:opacity .1s linear,transform .25s ease-in-out}@media (prefers-reduced-motion:reduce){.indicator{will-change:opacity;transition:opacity .1s linear}}.pad-top,input.search{background:var(--background);width:100%}.pad-top{height:var(--emoji-padding);z-index:3}.search-row{display:flex;align-items:center;position:relative;padding-inline-start:var(--emoji-padding);padding-bottom:var(--emoji-padding)}.search-wrapper{flex:1;min-width:0}input.search{padding:var(--input-padding);border-radius:var(--input-border-radius);border:var(--input-border-size) solid var(--input-border-color);color:var(--input-font-color);font-size:var(--input-font-size);line-height:var(--input-line-height)}input.search::placeholder{color:var(--input-placeholder-color)}.favorites{overflow-y:auto;scrollbar-gutter:stable;display:flex;flex-direction:row;border-top:var(--border-size) solid var(--border-color);contain:content}.message{padding:var(--emoji-padding)}';const Ct=["customEmoji","customCategorySorting","database","dataSource","i18n","locale","skinToneEmoji","emojiVersion"],gn=`:host{--emoji-font-family:${Bt}}`;class Qe extends HTMLElement{constructor(e){super(),this.attachShadow({mode:"open"});const o=document.createElement("style");o.textContent=un+gn,this.shadowRoot.appendChild(o),this._ctx={locale:cn,dataSource:ln,skinToneEmoji:Ia,customCategorySorting:Ba,customEmoji:null,i18n:pn,emojiVersion:null,...e};for(const n of Ct)n!=="database"&&Object.prototype.hasOwnProperty.call(this,n)&&(this._ctx[n]=this[n],delete this[n]);this._dbFlush()}connectedCallback(){ut(this),this._cmp||(this._cmp=dn(this.shadowRoot,this._ctx))}disconnectedCallback(){ut(this),Ce(()=>{if(!this.isConnected&&this._cmp){this._cmp.$destroy(),this._cmp=void 0;const{database:e}=this._ctx;e.close().catch(o=>console.error(o))}})}static get observedAttributes(){return["locale","data-source","skin-tone-emoji","emoji-version"]}attributeChangedCallback(e,o,n){this._set(e.replace(/-([a-z])/g,(c,s)=>s.toUpperCase()),e==="emoji-version"?parseFloat(n):n)}_set(e,o){this._ctx[e]=o,this._cmp&&this._cmp.$set({[e]:o}),["locale","dataSource"].includes(e)&&this._dbFlush()}_dbCreate(){const{locale:e,dataSource:o,database:n}=this._ctx;(!n||n.locale!==e||n.dataSource!==o)&&this._set("database",new ka({locale:e,dataSource:o}))}_dbFlush(){Ce(()=>this._dbCreate())}}const zt={};for(const a of Ct)zt[a]={get(){return a==="database"&&this._dbCreate(),this._ctx[a]},set(e){if(a==="database")throw new Error("database is read-only");this._set(a,e)}};Object.defineProperties(Qe.prototype,zt);function ut(a){a instanceof Qe||Object.setPrototypeOf(a,customElements.get(a.tagName.toLowerCase()).prototype)}customElements.get("emoji-picker")||customElements.define("emoji-picker",Qe);function mn(a,e){const o=document.getElementById("app"),n=new gt,c=new ze,s=a.id||a._id;let d=null,S=[];const H=localStorage.getItem("currentTheme")||"dark";yn(H),o.innerHTML=`
|
|
54
|
+
--num-skintones: ${st};`}),d(()=>{s.customEmoji&&s.database&&B()}),d(()=>{s.customEmoji&&s.customEmoji.length?s.groups!==We&&(s.groups=We):s.groups!==Ae&&(s.currentGroupIndex&&s.currentGroupIndex--,s.groups=Ae)}),d(()=>{async function t(){s.databaseLoaded&&(s.currentSkinTone=await s.database.getPreferredSkinTone())}t()}),d(()=>{s.skinTones=Array(st).fill().map((t,r)=>Fa(s.skinToneEmoji,r))}),d(()=>{s.skinToneButtonText=s.skinTones[s.currentSkinTone]}),d(()=>{s.skinToneButtonLabel=s.i18n.skinToneLabel.replace("{skinTone}",s.i18n.skinTones[s.currentSkinTone])}),d(()=>{async function t(){const{database:r}=s,l=(await Promise.all(Sa.map(b=>r.getEmojiByUnicodeOrName(b)))).filter(Boolean);s.defaultFavoriteEmojis=l}s.databaseLoaded&&t()});function B(){const{customEmoji:t,database:r}=s,l=t||qe;r.customEmoji!==l&&(r.customEmoji=l)}d(()=>{async function t(){B();const{database:r,defaultFavoriteEmojis:l,numColumns:b}=s,E=await r.getTopFavoriteEmoji(b),w=await L(Mt([...E,...l],P=>P.unicode||P.name).slice(0,b));s.currentFavorites=w}s.databaseLoaded&&s.defaultFavoriteEmojis&&t()});function I(t){Na(t,c,()=>{{const r=getComputedStyle(o.rootElement),l=parseInt(r.getPropertyValue("--num-columns"),10),b=r.getPropertyValue("direction")==="rtl";s.numColumns=l,s.isRtl=b}})}function A(t){rn(t,c,r=>{for(const{target:l,isIntersecting:b}of r)l.classList.toggle("onscreen",b)})}d(()=>{async function t(){const{searchText:r,currentGroup:l,databaseLoaded:b,customEmoji:E}=s;if(!b)s.currentEmojis=[],s.searchMode=!1;else if(r.length>=Ea){const w=await W(r);s.searchText===r&&(U(w),se(!0))}else{const{id:w}=l;if(w!==-1||E&&E.length){const P=await z(w);s.currentGroup.id===w&&(U(P),se(!1))}}}t()});const v=()=>{Se(()=>Ra(o.tabpanelElement))};d(()=>{const{currentEmojis:t,emojiVersion:r}=s,l=t.filter(b=>b.unicode).filter(b=>it(b)&&!Ve.has(b.unicode));if(!r&&l.length)U(t),Se(()=>k(l));else{const b=r?t:t.filter(C);U(b),v()}});function k(t){qa(t,o.baselineEmoji,$)?v():s.currentEmojis=[...s.currentEmojis]}function C(t){return!t.unicode||!it(t)||Ve.get(t.unicode)}async function D(t){const r=s.emojiVersion||await Fe();return t.filter(({version:l})=>!l||l<=r)}async function L(t){return Oa(t,s.emojiVersion||await Fe())}async function z(t){const r=t===-1?s.customEmoji:await s.database.getEmojiByGroup(t);return L(await D(r))}async function W(t){return L(await D(await s.database.getEmojiBySearchQuery(t)))}d(()=>{}),d(()=>{function t(){const{searchMode:l,currentEmojis:b}=s;if(l)return[{category:"",emojis:b}];const E=new Map;for(const w of b){const P=w.category||"";let G=E.get(P);G||(G=[],E.set(P,G)),G.push(w)}return[...E.entries()].map(([w,P])=>({category:w,emojis:P})).sort((w,P)=>s.customCategorySorting(w.category,P.category))}const r=t();ae(r)}),d(()=>{s.activeSearchItemId=s.activeSearchItem!==-1&&s.currentEmojis[s.activeSearchItem].id}),d(()=>{const{rawSearchText:t}=s;St(()=>{s.searchText=(t||"").trim(),s.activeSearchItem=-1})});function F(t){if(!s.searchMode||!s.currentEmojis.length)return;const r=l=>{ye(t),s.activeSearchItem=Oe(l,s.activeSearchItem,s.currentEmojis)};switch(t.key){case"ArrowDown":return r(!1);case"ArrowUp":return r(!0);case"Enter":if(s.activeSearchItem===-1)s.activeSearchItem=0;else return ye(t),ue(s.currentEmojis[s.activeSearchItem].id)}}function de(t){const{target:r}=t,l=r.closest(".nav-button");if(!l)return;const b=parseInt(l.dataset.groupId,10);o.searchElement.value="",s.rawSearchText="",s.searchText="",s.activeSearchItem=-1,s.currentGroupIndex=s.groups.findIndex(E=>E.id===b)}function X(t){const{target:r,key:l}=t,b=E=>{E&&(ye(t),E.focus())};switch(l){case"ArrowLeft":return b(r.previousElementSibling);case"ArrowRight":return b(r.nextElementSibling);case"Home":return b(r.parentElement.firstElementChild);case"End":return b(r.parentElement.lastElementChild)}}async function ce(t){const r=await s.database.getEmojiByUnicodeOrName(t),l=[...s.currentEmojis,...s.currentFavorites].find(E=>E.id===t),b=l.unicode&&re(l,s.currentSkinTone);return await s.database.incrementFavoriteEmojiCount(t),{emoji:r,skinTone:s.currentSkinTone,...b&&{unicode:b},...l.name&&{name:l.name}}}async function ue(t){const r=ce(t);j("emoji-click-sync",r),j("emoji-click",await r)}function m(t){const{target:r}=t;if(!r.classList.contains("emoji"))return;ye(t);const l=r.id.substring(4);ue(l)}function u(t){s.currentSkinTone=t,s.skinTonePickerExpanded=!1,N("skintone-button"),j("skin-tone-change",{skinTone:t}),s.database.setPreferredSkinTone(t)}function g(t){const{target:{id:r}}=t,l=r&&r.match(/^skintone-(\d)/);if(!l)return;ye(t);const b=parseInt(l[1],10);u(b)}function y(t){s.skinTonePickerExpanded=!s.skinTonePickerExpanded,s.activeSkinTone=s.currentSkinTone,s.skinTonePickerExpanded&&(ye(t),Se(()=>N("skintone-list")))}d(()=>{s.skinTonePickerExpanded?o.skinToneDropdown.addEventListener("transitionend",()=>{s.skinTonePickerExpandedAfterAnimation=!0},{once:!0}):s.skinTonePickerExpandedAfterAnimation=!1});function h(t){if(!s.skinTonePickerExpanded)return;const r=async l=>{ye(t),s.activeSkinTone=l};switch(t.key){case"ArrowUp":return r(Oe(!0,s.activeSkinTone,s.skinTones));case"ArrowDown":return r(Oe(!1,s.activeSkinTone,s.skinTones));case"Home":return r(0);case"End":return r(s.skinTones.length-1);case"Enter":return ye(t),u(s.activeSkinTone);case"Escape":return ye(t),s.skinTonePickerExpanded=!1,N("skintone-button")}}function M(t){if(s.skinTonePickerExpanded)switch(t.key){case" ":return ye(t),u(s.activeSkinTone)}}async function p(t){const{relatedTarget:r}=t;(!r||r.id!=="skintone-list")&&(s.skinTonePickerExpanded=!1)}function i(t){s.rawSearchText=t.target.value}return{$set(t){Ie(s,t)},$destroy(){n.abort()}}}const ln="https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/en/emojibase/data.json",cn="en";var pn={categoriesLabel:"Categories",emojiUnsupportedMessage:"Your browser does not support color emoji.",favoritesLabel:"Favorites",loadingMessage:"Loading…",networkErrorMessage:"Could not load emoji.",regionLabel:"Emoji picker",searchDescription:"When search results are available, press up or down to select and enter to choose.",searchLabel:"Search",searchResultsLabel:"Search results",skinToneDescription:"When expanded, press up or down to select and enter to choose.",skinToneLabel:"Choose a skin tone (currently {skinTone})",skinTonesLabel:"Skin tones",skinTones:["Default","Light","Medium-Light","Medium","Medium-Dark","Dark"],categories:{custom:"Custom","smileys-emotion":"Smileys and emoticons","people-body":"People and body","animals-nature":"Animals and nature","food-drink":"Food and drink","travel-places":"Travel and places",activities:"Activities",objects:"Objects",symbols:"Symbols",flags:"Flags"}},un=':host{--emoji-size:1.375rem;--emoji-padding:0.5rem;--category-emoji-size:var(--emoji-size);--category-emoji-padding:var(--emoji-padding);--indicator-height:3px;--input-border-radius:0.5rem;--input-border-size:1px;--input-font-size:1rem;--input-line-height:1.5;--input-padding:0.25rem;--num-columns:8;--outline-size:2px;--border-size:1px;--border-radius:0;--skintone-border-radius:1rem;--category-font-size:1rem;display:flex;width:min-content;height:400px}:host,:host(.light){color-scheme:light;--background:#fff;--border-color:#e0e0e0;--indicator-color:#385ac1;--input-border-color:#999;--input-font-color:#111;--input-placeholder-color:#999;--outline-color:#999;--category-font-color:#111;--button-active-background:#e6e6e6;--button-hover-background:#d9d9d9}:host(.dark){color-scheme:dark;--background:#222;--border-color:#444;--indicator-color:#5373ec;--input-border-color:#ccc;--input-font-color:#efefef;--input-placeholder-color:#ccc;--outline-color:#fff;--category-font-color:#efefef;--button-active-background:#555555;--button-hover-background:#484848}@media (prefers-color-scheme:dark){:host{color-scheme:dark;--background:#222;--border-color:#444;--indicator-color:#5373ec;--input-border-color:#ccc;--input-font-color:#efefef;--input-placeholder-color:#ccc;--outline-color:#fff;--category-font-color:#efefef;--button-active-background:#555555;--button-hover-background:#484848}}:host([hidden]){display:none}button{margin:0;padding:0;border:0;background:0 0;box-shadow:none;-webkit-tap-highlight-color:transparent}button::-moz-focus-inner{border:0}input{padding:0;margin:0;line-height:1.15;font-family:inherit}input[type=search]{-webkit-appearance:none}:focus{outline:var(--outline-color) solid var(--outline-size);outline-offset:calc(-1*var(--outline-size))}:host([data-js-focus-visible]) :focus:not([data-focus-visible-added]){outline:0}:focus:not(:focus-visible){outline:0}.hide-focus{outline:0}*{box-sizing:border-box}.picker{contain:content;display:flex;flex-direction:column;background:var(--background);border:var(--border-size) solid var(--border-color);border-radius:var(--border-radius);width:100%;height:100%;overflow:hidden;--total-emoji-size:calc(var(--emoji-size) + (2 * var(--emoji-padding)));--total-category-emoji-size:calc(var(--category-emoji-size) + (2 * var(--category-emoji-padding)))}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.hidden{opacity:0;pointer-events:none}.abs-pos{position:absolute;left:0;top:0}.gone{display:none!important}.skintone-button-wrapper,.skintone-list{background:var(--background);z-index:3}.skintone-button-wrapper.expanded{z-index:1}.skintone-list{position:absolute;inset-inline-end:0;top:0;z-index:2;overflow:visible;border-bottom:var(--border-size) solid var(--border-color);border-radius:0 0 var(--skintone-border-radius) var(--skintone-border-radius);will-change:transform;transition:transform .2s ease-in-out;transform-origin:center 0}@media (prefers-reduced-motion:reduce){.skintone-list{transition-duration:.001s}}@supports not (inset-inline-end:0){.skintone-list{right:0}}.skintone-list.no-animate{transition:none}.tabpanel{overflow-y:auto;scrollbar-gutter:stable;-webkit-overflow-scrolling:touch;will-change:transform;min-height:0;flex:1;contain:content}.emoji-menu{display:grid;grid-template-columns:repeat(var(--num-columns),var(--total-emoji-size));justify-content:space-around;align-items:flex-start;width:100%}.emoji-menu.visibility-auto{content-visibility:auto;contain-intrinsic-size:calc(var(--num-columns)*var(--total-emoji-size)) calc(var(--num-rows)*var(--total-emoji-size))}.category{padding:var(--emoji-padding);font-size:var(--category-font-size);color:var(--category-font-color)}.emoji,button.emoji{font-size:var(--emoji-size);display:flex;align-items:center;justify-content:center;border-radius:100%;height:var(--total-emoji-size);width:var(--total-emoji-size);line-height:1;overflow:hidden;font-family:var(--emoji-font-family);cursor:pointer}@media (hover:hover) and (pointer:fine){.emoji:hover,button.emoji:hover{background:var(--button-hover-background)}}.emoji.active,.emoji:active,button.emoji.active,button.emoji:active{background:var(--button-active-background)}.onscreen .custom-emoji::after{content:"";width:var(--emoji-size);height:var(--emoji-size);background-repeat:no-repeat;background-position:center center;background-size:contain;background-image:var(--custom-emoji-background)}.nav,.nav-button{align-items:center}.nav{display:grid;justify-content:space-between;contain:content}.nav-button{display:flex;justify-content:center}.nav-emoji{font-size:var(--category-emoji-size);width:var(--total-category-emoji-size);height:var(--total-category-emoji-size)}.indicator-wrapper{display:flex;border-bottom:1px solid var(--border-color)}.indicator{width:calc(100%/var(--num-groups));height:var(--indicator-height);opacity:var(--indicator-opacity);background-color:var(--indicator-color);will-change:transform,opacity;transition:opacity .1s linear,transform .25s ease-in-out}@media (prefers-reduced-motion:reduce){.indicator{will-change:opacity;transition:opacity .1s linear}}.pad-top,input.search{background:var(--background);width:100%}.pad-top{height:var(--emoji-padding);z-index:3}.search-row{display:flex;align-items:center;position:relative;padding-inline-start:var(--emoji-padding);padding-bottom:var(--emoji-padding)}.search-wrapper{flex:1;min-width:0}input.search{padding:var(--input-padding);border-radius:var(--input-border-radius);border:var(--input-border-size) solid var(--input-border-color);color:var(--input-font-color);font-size:var(--input-font-size);line-height:var(--input-line-height)}input.search::placeholder{color:var(--input-placeholder-color)}.favorites{overflow-y:auto;scrollbar-gutter:stable;display:flex;flex-direction:row;border-top:var(--border-size) solid var(--border-color);contain:content}.message{padding:var(--emoji-padding)}';const Ct=["customEmoji","customCategorySorting","database","dataSource","i18n","locale","skinToneEmoji","emojiVersion"],gn=`:host{--emoji-font-family:${Bt}}`;class Qe extends HTMLElement{constructor(e){super(),this.attachShadow({mode:"open"});const o=document.createElement("style");o.textContent=un+gn,this.shadowRoot.appendChild(o),this._ctx={locale:cn,dataSource:ln,skinToneEmoji:Ia,customCategorySorting:Ba,customEmoji:null,i18n:pn,emojiVersion:null,...e};for(const n of Ct)n!=="database"&&Object.prototype.hasOwnProperty.call(this,n)&&(this._ctx[n]=this[n],delete this[n]);this._dbFlush()}connectedCallback(){ut(this),this._cmp||(this._cmp=dn(this.shadowRoot,this._ctx))}disconnectedCallback(){ut(this),Ce(()=>{if(!this.isConnected&&this._cmp){this._cmp.$destroy(),this._cmp=void 0;const{database:e}=this._ctx;e.close().catch(o=>console.error(o))}})}static get observedAttributes(){return["locale","data-source","skin-tone-emoji","emoji-version"]}attributeChangedCallback(e,o,n){this._set(e.replace(/-([a-z])/g,(c,s)=>s.toUpperCase()),e==="emoji-version"?parseFloat(n):n)}_set(e,o){this._ctx[e]=o,this._cmp&&this._cmp.$set({[e]:o}),["locale","dataSource"].includes(e)&&this._dbFlush()}_dbCreate(){const{locale:e,dataSource:o,database:n}=this._ctx;(!n||n.locale!==e||n.dataSource!==o)&&this._set("database",new ka({locale:e,dataSource:o}))}_dbFlush(){Ce(()=>this._dbCreate())}}const zt={};for(const a of Ct)zt[a]={get(){return a==="database"&&this._dbCreate(),this._ctx[a]},set(e){if(a==="database")throw new Error("database is read-only");this._set(a,e)}};Object.defineProperties(Qe.prototype,zt);function ut(a){a instanceof Qe||Object.setPrototypeOf(a,customElements.get(a.tagName.toLowerCase()).prototype)}customElements.get("emoji-picker")||customElements.define("emoji-picker",Qe);function mn(a,e){const o=document.getElementById("app"),n=new gt,c=new ze,s=a.id||a._id;let d=null,S=[];const N=localStorage.getItem("currentTheme")||"dark";yn(N),o.innerHTML=`
|
|
55
55
|
<div class="dashboard">
|
|
56
56
|
<aside class="sidebar">
|
|
57
57
|
<div class="sidebar-header">
|
|
@@ -619,7 +619,7 @@
|
|
|
619
619
|
</form>
|
|
620
620
|
</div>
|
|
621
621
|
</div>
|
|
622
|
-
`;const g=document.getElementById("filesList");!u.files||u.files.length===0?g.innerHTML='<div class="empty-state">暂无文件</div>':(u.files.forEach(y=>{const h=document.createElement("div");h.className="file-card";const M=
|
|
622
|
+
`;const g=document.getElementById("filesList");!u.files||u.files.length===0?g.innerHTML='<div class="empty-state">暂无文件</div>':(u.files.forEach(y=>{const h=document.createElement("div");h.className="file-card";const M=re(y.mimetype),p=pe(y.size);h.innerHTML=`
|
|
623
623
|
<div class="file-icon">${M}</div>
|
|
624
624
|
<div class="file-info">
|
|
625
625
|
<h4>${y.originalName}</h4>
|
|
@@ -639,7 +639,7 @@
|
|
|
639
639
|
<h2>文件管理</h2>
|
|
640
640
|
</div>
|
|
641
641
|
<div class="empty-state">加载文件失败: ${u.message}</div>
|
|
642
|
-
`}}function
|
|
642
|
+
`}}function re(m){return m.startsWith("image/")?"🖼️":m==="application/pdf"?"📕":m.includes("word")||m.includes("document")?"📘":m.includes("excel")||m.includes("spreadsheet")?"📗":m.includes("zip")||m.includes("compressed")?"📦":"📄"}function pe(m){if(m===0)return"0 Bytes";const u=1024,g=["Bytes","KB","MB","GB"],y=Math.floor(Math.log(m)/Math.log(u));return Math.round(m/Math.pow(u,y)*100)/100+" "+g[y]}async function q(m){if(!d){m.innerHTML='<div class="empty-state">请先选择一个群组</div>';return}let g=(await n.getGroup(d._id)).group;function y(f){if(f.startsWith("[白板作品]")){const T=f.replace("[白板作品]","").trim(),x=T.includes("/api/files/")&&T.includes("/download");let R=T;if(x&&!T.includes("token=")){const H=localStorage.getItem("token");R=T.includes("?")?`${T}&token=${H}`:`${T}?token=${H}`}return`
|
|
643
643
|
<div style="background: linear-gradient(135deg, rgb(99, 102, 241) 0%, rgb(139, 92, 246) 100%); padding: 16px; border-radius: 12px; box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);">
|
|
644
644
|
<div style="margin-bottom: 12px; font-weight: 600; color: white; display: flex; align-items: center; gap: 6px; font-size: 15px;">
|
|
645
645
|
<span style="font-size: 18px;">🎨</span>
|
|
@@ -673,7 +673,7 @@
|
|
|
673
673
|
📋 查看投票详情
|
|
674
674
|
</div>
|
|
675
675
|
</div>
|
|
676
|
-
`}return f}window.viewPollDetail=async f=>{try{const T=localStorage.getItem("token"),x=await fetch(`http://localhost:3000/api/polls/${f}`,{headers:{Authorization:`Bearer ${T}`}});if(!x.ok)throw new Error("获取投票详情失败");const
|
|
676
|
+
`}return f}window.viewPollDetail=async f=>{try{const T=localStorage.getItem("token"),x=await fetch(`http://localhost:3000/api/polls/${f}`,{headers:{Authorization:`Bearer ${T}`}});if(!x.ok)throw new Error("获取投票详情失败");const H=(await x.json()).poll,K=H.options.reduce((Q,ge)=>Q+ge.votes.length,0),Z=H.status==="ended"||H.endTime&&new Date(H.endTime)<new Date,J=`
|
|
677
677
|
<div id="pollDetailModal" class="modal" style="display: flex;">
|
|
678
678
|
<div class="modal-content" style="max-width: 800px; max-height: 90vh; overflow-y: auto;">
|
|
679
679
|
<div class="modal-header">
|
|
@@ -681,15 +681,15 @@
|
|
|
681
681
|
<button class="modal-close" id="closePollDetailModal">×</button>
|
|
682
682
|
</div>
|
|
683
683
|
<div class="modal-body" style="padding: 24px;">
|
|
684
|
-
<h2 style="margin: 0 0 12px 0;">${
|
|
685
|
-
${
|
|
684
|
+
<h2 style="margin: 0 0 12px 0;">${H.title}</h2>
|
|
685
|
+
${H.description?`<p style="color: var(--text-secondary); margin: 0 0 20px 0;">${H.description}</p>`:""}
|
|
686
686
|
|
|
687
687
|
<div style="display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 20px;">
|
|
688
688
|
<span style="font-size: 13px; padding: 6px 12px; background: var(--bg-tertiary); border-radius: 14px;">
|
|
689
|
-
${
|
|
689
|
+
${H.allowMultiple?"✓ 多选投票":"○ 单选投票"}
|
|
690
690
|
</span>
|
|
691
691
|
<span style="font-size: 13px; padding: 6px 12px; background: var(--bg-tertiary); border-radius: 14px;">
|
|
692
|
-
${
|
|
692
|
+
${H.anonymous?"🔒 匿名投票":"👤 实名投票"}
|
|
693
693
|
</span>
|
|
694
694
|
<span style="font-size: 13px; padding: 6px 12px; background: ${Z?"var(--danger)":"var(--success)"}; border-radius: 14px; color: white;">
|
|
695
695
|
${Z?"已结束":"进行中"}
|
|
@@ -700,7 +700,7 @@
|
|
|
700
700
|
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px;">
|
|
701
701
|
<div>
|
|
702
702
|
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">创建者</div>
|
|
703
|
-
<div style="font-weight: 600;">👤 ${
|
|
703
|
+
<div style="font-weight: 600;">👤 ${H.creatorName||H.creator&&H.creator.username||"未知用户"}</div>
|
|
704
704
|
</div>
|
|
705
705
|
<div>
|
|
706
706
|
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">总投票数</div>
|
|
@@ -708,19 +708,19 @@
|
|
|
708
708
|
</div>
|
|
709
709
|
<div>
|
|
710
710
|
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">创建时间</div>
|
|
711
|
-
<div style="font-weight: 600;">⏰ ${new Date(
|
|
711
|
+
<div style="font-weight: 600;">⏰ ${new Date(H.createdAt).toLocaleString("zh-CN")}</div>
|
|
712
712
|
</div>
|
|
713
|
-
${
|
|
713
|
+
${H.endTime?`
|
|
714
714
|
<div>
|
|
715
715
|
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">截止时间</div>
|
|
716
|
-
<div style="font-weight: 600;">⏰ ${new Date(
|
|
716
|
+
<div style="font-weight: 600;">⏰ ${new Date(H.endTime).toLocaleString("zh-CN")}</div>
|
|
717
717
|
</div>
|
|
718
718
|
`:""}
|
|
719
719
|
</div>
|
|
720
720
|
</div>
|
|
721
721
|
|
|
722
722
|
<h3 style="margin-bottom: 16px;">投票选项</h3>
|
|
723
|
-
${
|
|
723
|
+
${H.options.map((Q,ge)=>{const Y=K>0?(Q.votes.length/K*100).toFixed(1):0;let ee="";if(!H.anonymous&&Q.votes.length>0){const me=Q.votes.map(ie=>{if(ie&&typeof ie=="object"){if(ie.user&&typeof ie.user=="object"&&ie.user.username)return ie.user.username;if(ie.username&&typeof ie.username=="string")return ie.username}return null}).filter(ie=>ie!==null&&ie!=="未知用户");me.length>0&&(ee=`
|
|
724
724
|
<div style="font-size: 13px; color: var(--text-secondary); margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border);">
|
|
725
725
|
<strong style="color: var(--text-primary);">👥 投票者:</strong> ${me.join(", ")}
|
|
726
726
|
</div>
|
|
@@ -937,17 +937,17 @@
|
|
|
937
937
|
</div>
|
|
938
938
|
</div>
|
|
939
939
|
</div>
|
|
940
|
-
`;const h=document.getElementById("messages"),M=document.getElementById("messageInput"),p=document.getElementById("sendBtn"),i=document.getElementById("emojiBtn"),t=document.getElementById("emojiPicker");let r=new Set((g.mutedUsers||[]).map(String)),l=!!g.mutedAll;i.addEventListener("click",()=>{t.classList.toggle("hidden")}),t.addEventListener("emoji-click",f=>{M.value+=f.detail.unicode,M.focus(),t.classList.add("hidden")}),document.addEventListener("click",f=>{!i.contains(f.target)&&!t.contains(f.target)&&t.classList.add("hidden")}),"Notification"in window&&Notification.permission==="default"&&Notification.requestPermission();try{const f=await n.getGroupMessages(d._id);f.messages&&(f.messages.forEach(T=>{const x=document.createElement("div");x.className=`message ${T.sender===s?"own":""}`;const R=y(T.content),
|
|
940
|
+
`;const h=document.getElementById("messages"),M=document.getElementById("messageInput"),p=document.getElementById("sendBtn"),i=document.getElementById("emojiBtn"),t=document.getElementById("emojiPicker");let r=new Set((g.mutedUsers||[]).map(String)),l=!!g.mutedAll;i.addEventListener("click",()=>{t.classList.toggle("hidden")}),t.addEventListener("emoji-click",f=>{M.value+=f.detail.unicode,M.focus(),t.classList.add("hidden")}),document.addEventListener("click",f=>{!i.contains(f.target)&&!t.contains(f.target)&&t.classList.add("hidden")}),"Notification"in window&&Notification.permission==="default"&&Notification.requestPermission();try{const f=await n.getGroupMessages(d._id);f.messages&&(f.messages.forEach(T=>{const x=document.createElement("div");x.className=`message ${T.sender===s?"own":""}`;const R=y(T.content),H=T.content.startsWith("[白板作品]")||T.content.startsWith("[投票]"),K=T.content.startsWith("[白板作品]");x.innerHTML=`
|
|
941
941
|
<div class="message-header">
|
|
942
942
|
<span class="message-user">${T.username}</span>
|
|
943
943
|
<span class="message-time">${new Date(T.timestamp).toLocaleTimeString()}</span>
|
|
944
944
|
</div>
|
|
945
|
-
<div class="message-content" style="${K?"background: linear-gradient(135deg, rgb(99, 102, 241) 0%, rgb(139, 92, 246) 100%); color: white; padding: 16px; border-radius: 12px; box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);":
|
|
945
|
+
<div class="message-content" style="${K?"background: linear-gradient(135deg, rgb(99, 102, 241) 0%, rgb(139, 92, 246) 100%); color: white; padding: 16px; border-radius: 12px; box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);":H?"background: transparent; padding: 0;":""}">${R}</div>
|
|
946
946
|
`,h.appendChild(x)}),h.scrollTop=h.scrollHeight)}catch(f){console.error("加载历史消息失败:",f)}const b=()=>{const f=document.getElementById("muteAllBtn");f.textContent=l?"取消全体禁言":"全体禁言",f.style.background=l?"var(--danger)":""};b(),document.getElementById("clearChatBtn").addEventListener("click",async()=>{if(!confirm(`⚠️ 警告:此操作将永久删除该群组的所有聊天记录!
|
|
947
947
|
|
|
948
948
|
确定要清除吗?`))return;if(prompt(`请输入群组名称以确认删除:
|
|
949
949
|
|
|
950
|
-
群组名称:`+d.name)!==d.name){alert("❌ 群组名称不匹配,操作已取消");return}try{const x=document.getElementById("clearChatBtn"),R=x.innerHTML;x.innerHTML="⏳ 清除中...",x.disabled=!0;const
|
|
950
|
+
群组名称:`+d.name)!==d.name){alert("❌ 群组名称不匹配,操作已取消");return}try{const x=document.getElementById("clearChatBtn"),R=x.innerHTML;x.innerHTML="⏳ 清除中...",x.disabled=!0;const H=localStorage.getItem("token"),K=await fetch(`http://localhost:3000/api/messages/group/${d._id}/clear`,{method:"DELETE",headers:{Authorization:`Bearer ${H}`,"Content-Type":"application/json"}});if(!K.ok){const J=await K.json();throw new Error(J.message||"清除失败")}const Z=await K.json();h.innerHTML='<div class="empty-state" style="padding: 40px; text-align: center; color: var(--text-secondary);">✨ 聊天记录已清空</div>',alert(`✅ 成功清除 ${Z.deletedCount||0} 条聊天记录!`),x.innerHTML=R,x.disabled=!1}catch(x){console.error("清除聊天记录失败:",x),alert("❌ 清除失败: "+x.message);const R=document.getElementById("clearChatBtn");R.innerHTML="🗑️ 清除记录",R.disabled=!1}}),document.getElementById("muteAllBtn").addEventListener("click",async()=>{try{const f=!l;l=!!(await n.setMuteAll(d._id,f)).mutedAll,b();const x=document.createElement("div");x.className="notification",x.textContent=l?"已开启全体禁言(成员无法发言)":"已取消全体禁言",h.appendChild(x),h.scrollTop=h.scrollHeight}catch(f){alert("设置失败: "+f.message)}}),document.getElementById("manageMuteBtn").addEventListener("click",async()=>{g=(await n.getGroup(d._id)).group,r=new Set((g.mutedUsers||[]).map(String));const T=document.getElementById("membersList");T.innerHTML=g.members.filter(x=>x._id.toString()!==s).map(x=>{const R=r.has(x._id.toString());return`
|
|
951
951
|
<div style="display: flex; align-items: center; justify-content: space-between; padding: 12px; border-bottom: 1px solid var(--border);">
|
|
952
952
|
<div style="display: flex; align-items: center; gap: 10px;">
|
|
953
953
|
<div class="avatar" style="width: 35px; height: 35px;">${x.username[0].toUpperCase()}</div>
|
|
@@ -960,13 +960,13 @@
|
|
|
960
960
|
`}).join(""),document.getElementById("manageMuteModal").classList.remove("hidden")}),document.getElementById("closeMuteModal").addEventListener("click",()=>{document.getElementById("manageMuteModal").classList.add("hidden")}),document.getElementById("createPollBtn").addEventListener("click",()=>{document.getElementById("createPollModal").classList.remove("hidden")}),document.getElementById("closePollModal").addEventListener("click",()=>{document.getElementById("createPollModal").classList.add("hidden"),document.getElementById("createPollForm").reset()}),document.getElementById("cancelPollModal").addEventListener("click",()=>{document.getElementById("createPollModal").classList.add("hidden"),document.getElementById("createPollForm").reset()}),document.getElementById("addPollOption").addEventListener("click",()=>{const f=document.getElementById("pollOptions"),T=f.querySelectorAll(".poll-option-input").length+1,x=document.createElement("div");x.className="poll-option-input",x.style.cssText="display: flex; gap: 10px; margin-bottom: 10px;",x.innerHTML=`
|
|
961
961
|
<input type="text" class="poll-option" placeholder="选项 ${T}" required style="flex: 1; padding: 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-primary); color: white;">
|
|
962
962
|
<button type="button" class="btn-danger remove-option" style="padding: 10px 15px;">删除</button>
|
|
963
|
-
`,f.appendChild(x),x.querySelector(".remove-option").addEventListener("click",()=>{f.querySelectorAll(".poll-option-input").length>2?x.remove():alert("至少需要2个选项!")})}),document.getElementById("createPollForm").addEventListener("submit",async f=>{f.preventDefault();const T=document.getElementById("pollTitle").value.trim(),x=document.getElementById("pollDescription").value.trim(),R=document.getElementById("allowMultiple").checked,
|
|
963
|
+
`,f.appendChild(x),x.querySelector(".remove-option").addEventListener("click",()=>{f.querySelectorAll(".poll-option-input").length>2?x.remove():alert("至少需要2个选项!")})}),document.getElementById("createPollForm").addEventListener("submit",async f=>{f.preventDefault();const T=document.getElementById("pollTitle").value.trim(),x=document.getElementById("pollDescription").value.trim(),R=document.getElementById("allowMultiple").checked,H=document.getElementById("anonymous").checked,K=document.getElementById("pollEndTime").value,Z=document.querySelectorAll(".poll-option"),J=Array.from(Z).map(te=>te.value.trim()).filter(te=>te);if(J.length<2){alert("至少需要2个选项!");return}try{const te=localStorage.getItem("token"),Q=await fetch("http://localhost:3000/api/polls",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${te}`},body:JSON.stringify({title:T,description:x,options:J,groupId:d._id,allowMultiple:R,anonymous:H,endTime:K||null})});if(!Q.ok){const ee=await Q.json();throw new Error(ee.error||"创建投票失败")}const Y=(await Q.json()).poll;e.sendChatMessage(d._id,a.username,`[投票]${Y._id}`),alert("投票创建成功!"),document.getElementById("createPollModal").classList.add("hidden"),document.getElementById("createPollForm").reset()}catch(te){console.error("创建投票失败:",te),alert("创建投票失败: "+te.message)}}),window.toggleMute=async f=>{try{const T=!r.has(f),x=await n.setUserMute(d._id,f,T);r=new Set((x.mutedUsers||[]).map(String));const R=document.getElementById(`mute-${f}`);R.textContent=r.has(f)?"取消禁言":"禁言",R.style.background=r.has(f)?"var(--danger)":""}catch(T){alert("操作失败: "+T.message)}},e.on("chat_message",f=>{if(f.groupId===d._id){const T=document.createElement("div");T.className=`message ${f.userId===s?"own":""}`;const x=y(f.content),R=f.content.startsWith("[白板作品]")||f.content.startsWith("[投票]"),H=f.content.startsWith("[白板作品]");T.innerHTML=`
|
|
964
964
|
<div class="message-header">
|
|
965
965
|
<span class="message-user">${f.username}</span>
|
|
966
966
|
<span class="message-time">${new Date(f.timestamp).toLocaleTimeString()}</span>
|
|
967
967
|
</div>
|
|
968
|
-
<div class="message-content" style="${
|
|
969
|
-
`,h.appendChild(T),h.scrollTop=h.scrollHeight}}),e.on("chat_blocked",f=>{if(f.groupId===d._id){const T=document.createElement("div");T.className="notification",T.textContent=f.message||"消息发送失败",h.appendChild(T),h.scrollTop=h.scrollHeight}});const E=()=>{const f=M.value.trim();f&&(e.sendChatMessage(d._id,a.username,f),M.value="")};p.addEventListener("click",E),M.addEventListener("keypress",f=>{f.key==="Enter"&&E()}),document.getElementById("openAIBtn").addEventListener("click",()=>{document.getElementById("aiModal").classList.remove("hidden")}),document.getElementById("closeAIModal").addEventListener("click",()=>{document.getElementById("aiModal").classList.add("hidden")}),document.querySelectorAll(".quick-question-btn").forEach(f=>{f.addEventListener("click",()=>{document.getElementById("aiInputText").value=f.dataset.question,document.getElementById("aiSendBtnModal").click()}),f.addEventListener("mouseenter",T=>{T.target.style.background="var(--primary)",T.target.style.color="white",T.target.style.transform="translateY(-2px)"}),f.addEventListener("mouseleave",T=>{T.target.style.background="var(--bg)",T.target.style.color="inherit",T.target.style.transform="translateY(0)"})});const w=document.getElementById("aiInputText");w.addEventListener("input",()=>{w.style.height="auto",w.style.height=w.scrollHeight+"px"}),w.addEventListener("keydown",f=>{f.key==="Enter"&&!f.shiftKey&&(f.preventDefault(),document.getElementById("aiSendBtnModal").click())}),w.addEventListener("focus",()=>{w.style.borderColor="var(--primary)"}),w.addEventListener("blur",()=>{w.style.borderColor="var(--border)"});const P=document.getElementById("aiSendBtnModal");P.addEventListener("mouseenter",()=>{P.style.transform="scale(1.05)"}),P.addEventListener("mouseleave",()=>{P.style.transform="scale(1)"}),document.getElementById("aiSendBtnModal").addEventListener("click",async()=>{const f=document.getElementById("aiInputText"),T=f.value.trim();if(!T)return;const x=document.getElementById("aiChatMessages"),R=document.createElement("div");R.className="ai-message user",R.style.cssText="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 18px; border-radius: 18px 18px 4px 18px; margin: 10px 0; max-width: 75%; margin-left: auto; text-align: right; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); animation: slideInRight 0.3s ease;",R.textContent=T,x.appendChild(R),f.value="";const
|
|
968
|
+
<div class="message-content" style="${H?"background: linear-gradient(135deg, rgb(99, 102, 241) 0%, rgb(139, 92, 246) 100%); color: white; padding: 16px; border-radius: 12px; box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);":R?"background: transparent; padding: 0;":""}">${x}</div>
|
|
969
|
+
`,h.appendChild(T),h.scrollTop=h.scrollHeight}}),e.on("chat_blocked",f=>{if(f.groupId===d._id){const T=document.createElement("div");T.className="notification",T.textContent=f.message||"消息发送失败",h.appendChild(T),h.scrollTop=h.scrollHeight}});const E=()=>{const f=M.value.trim();f&&(e.sendChatMessage(d._id,a.username,f),M.value="")};p.addEventListener("click",E),M.addEventListener("keypress",f=>{f.key==="Enter"&&E()}),document.getElementById("openAIBtn").addEventListener("click",()=>{document.getElementById("aiModal").classList.remove("hidden")}),document.getElementById("closeAIModal").addEventListener("click",()=>{document.getElementById("aiModal").classList.add("hidden")}),document.querySelectorAll(".quick-question-btn").forEach(f=>{f.addEventListener("click",()=>{document.getElementById("aiInputText").value=f.dataset.question,document.getElementById("aiSendBtnModal").click()}),f.addEventListener("mouseenter",T=>{T.target.style.background="var(--primary)",T.target.style.color="white",T.target.style.transform="translateY(-2px)"}),f.addEventListener("mouseleave",T=>{T.target.style.background="var(--bg)",T.target.style.color="inherit",T.target.style.transform="translateY(0)"})});const w=document.getElementById("aiInputText");w.addEventListener("input",()=>{w.style.height="auto",w.style.height=w.scrollHeight+"px"}),w.addEventListener("keydown",f=>{f.key==="Enter"&&!f.shiftKey&&(f.preventDefault(),document.getElementById("aiSendBtnModal").click())}),w.addEventListener("focus",()=>{w.style.borderColor="var(--primary)"}),w.addEventListener("blur",()=>{w.style.borderColor="var(--border)"});const P=document.getElementById("aiSendBtnModal");P.addEventListener("mouseenter",()=>{P.style.transform="scale(1.05)"}),P.addEventListener("mouseleave",()=>{P.style.transform="scale(1)"}),document.getElementById("aiSendBtnModal").addEventListener("click",async()=>{const f=document.getElementById("aiInputText"),T=f.value.trim();if(!T)return;const x=document.getElementById("aiChatMessages"),R=document.createElement("div");R.className="ai-message user",R.style.cssText="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 18px; border-radius: 18px 18px 4px 18px; margin: 10px 0; max-width: 75%; margin-left: auto; text-align: right; box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); animation: slideInRight 0.3s ease;",R.textContent=T,x.appendChild(R),f.value="";const H=document.createElement("div");H.className="ai-message ai loading",H.style.cssText="background: var(--bg-tertiary); padding: 12px 16px; border-radius: 12px; margin: 10px 0; max-width: 70%;",H.textContent="思考中...",x.appendChild(H),x.scrollTop=x.scrollHeight;try{const K=localStorage.getItem("token"),J=await(await fetch("http://localhost:3000/api/ai/ask",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${K}`},body:JSON.stringify({question:T,groupId:d==null?void 0:d._id})})).json();H.remove();const te=document.createElement("div");te.className="ai-message ai",te.style.cssText="background: var(--bg-secondary); padding: 15px 18px; border-radius: 18px 18px 18px 4px; margin: 10px 0; max-width: 75%; border: 1px solid var(--border); box-shadow: 0 2px 4px rgba(0,0,0,0.05); animation: slideInLeft 0.3s ease; line-height: 1.6;",te.textContent=J.answer||"抱歉,我无法回答这个问题。",x.appendChild(te),x.scrollTop=x.scrollHeight}catch(K){H.remove();const Z=document.createElement("div");Z.className="ai-message ai error",Z.style.cssText="background: var(--danger); color: white; padding: 12px 16px; border-radius: 12px; margin: 10px 0; max-width: 70%;",Z.textContent="抱歉,发生了错误: "+K.message,x.appendChild(Z),x.scrollTop=x.scrollHeight}}),window.showImageModal=f=>{const T=document.getElementById("imagePreviewModal"),x=document.getElementById("previewImage"),R=document.getElementById("downloadImageBtn");x.src=f,T.classList.remove("hidden"),R.onclick=async()=>{try{const K=await(await fetch(f)).blob(),Z=window.URL.createObjectURL(K),J=document.createElement("a");J.href=Z,J.download=`whiteboard-${Date.now()}.png`,document.body.appendChild(J),J.click(),document.body.removeChild(J),window.URL.revokeObjectURL(Z)}catch(H){console.error("下载失败:",H),alert("下载失败,请重试")}}},document.getElementById("closeImagePreview").addEventListener("click",()=>{document.getElementById("imagePreviewModal").classList.add("hidden")}),document.getElementById("imagePreviewModal").addEventListener("click",f=>{f.target.id==="imagePreviewModal"&&document.getElementById("imagePreviewModal").classList.add("hidden")}),document.getElementById("openWhiteboardBtn").addEventListener("click",()=>{document.getElementById("whiteboardModal").classList.remove("hidden"),G()}),document.getElementById("closeWhiteboardModal").addEventListener("click",()=>{document.getElementById("whiteboardModal").classList.add("hidden")});function G(){const f=document.getElementById("whiteboardCanvas");if(!f)return;const T=f.getContext("2d");let x=!1,R="pen",H="#000000",K=3,Z=0,J=0;document.querySelectorAll(".tool-btn").forEach(Y=>{Y.onclick=()=>{document.querySelectorAll(".tool-btn").forEach(ee=>{ee.style.background="transparent",ee.style.borderColor="var(--border)",ee.style.color="inherit",ee.classList.remove("active")}),Y.style.background="var(--primary)",Y.style.borderColor="var(--primary)",Y.style.color="white",Y.classList.add("active"),R=Y.dataset.tool}});const te=document.getElementById("colorPickerCanvas");te&&(te.onchange=Y=>{H=Y.target.value});const Q=document.getElementById("brushSizeCanvas"),ge=document.getElementById("brushSizeLabel");Q&&ge&&(Q.oninput=Y=>{K=Y.target.value,ge.textContent=`大小: ${K}`}),f.onmousedown=Y=>{x=!0;const ee=f.getBoundingClientRect();Z=Y.clientX-ee.left,J=Y.clientY-ee.top},f.onmousemove=Y=>{if(!x)return;const ee=f.getBoundingClientRect(),me=Y.clientX-ee.left,ie=Y.clientY-ee.top;T.beginPath(),T.moveTo(Z,J),T.lineTo(me,ie),T.strokeStyle=R==="eraser"?"#ffffff":H,T.lineWidth=K,T.lineCap="round",T.stroke(),Z=me,J=ie},f.onmouseup=()=>{x=!1},f.onmouseleave=()=>{x=!1},document.getElementById("clearCanvasBtn").onclick=()=>{confirm("确定要清空画布吗?")&&T.clearRect(0,0,f.width,f.height)},document.getElementById("saveCanvasBtn").onclick=()=>{const Y=f.toDataURL("image/png"),ee=document.createElement("a");ee.download=`whiteboard-${Date.now()}.png`,ee.href=Y,ee.click(),alert("白板已保存!")},document.getElementById("sendToGroupBtn").onclick=async()=>{try{const Y=f.toDataURL("image/png"),ee=await fetch(Y).then(tt=>tt.blob()),me=new FormData;me.append("file",ee,`whiteboard-${Date.now()}.png`),me.append("groupId",d._id),me.append("description","协作白板作品");const ie=localStorage.getItem("token"),et=await fetch("http://localhost:3000/api/files/upload",{method:"POST",headers:{Authorization:`Bearer ${ie}`},body:me});if(et.ok){const Dt=`http://localhost:3000/api/files/${(await et.json()).file._id}/download?token=${ie}`;e.sendChatMessage(d._id,a.username,`[白板作品]${Dt}`),alert("白板作品已发送到群聊!"),document.getElementById("whiteboardModal").classList.add("hidden")}else throw new Error("上传失败")}catch(Y){console.error("发送白板作品错误:",Y),alert("发送失败: "+Y.message)}}}}async function V(m){if(!d){m.innerHTML='<div class="empty-state">请先选择一个群组</div>';return}m.innerHTML=`
|
|
970
970
|
<div class="view-header">
|
|
971
971
|
<h2>随机点名 - ${d.name}</h2>
|
|
972
972
|
</div>
|
|
@@ -1061,7 +1061,7 @@
|
|
|
1061
1061
|
<div>资源</div>
|
|
1062
1062
|
<div>详情</div>
|
|
1063
1063
|
</div>
|
|
1064
|
-
${E.logs.map(G=>{var f,T,x,R,
|
|
1064
|
+
${E.logs.map(G=>{var f,T,x,R,H;return`
|
|
1065
1065
|
<div class="audit-row" onclick="showAuditDetail('${G._id}')">
|
|
1066
1066
|
<div class="audit-time">${new Date(G.createdAt).toLocaleString()}</div>
|
|
1067
1067
|
<div class="audit-user">
|
|
@@ -1072,11 +1072,11 @@
|
|
|
1072
1072
|
<span class="action-badge action-${G.action}">${A(G.action)}</span>
|
|
1073
1073
|
</div>
|
|
1074
1074
|
<div class="audit-resource">${G.resourceTitle||G.resourceId}</div>
|
|
1075
|
-
<div class="audit-description">${((
|
|
1075
|
+
<div class="audit-description">${((H=G.details)==null?void 0:H.description)||"-"}</div>
|
|
1076
1076
|
</div>
|
|
1077
1077
|
`}).join("")}
|
|
1078
1078
|
</div>
|
|
1079
|
-
`;const w=document.getElementById("auditPagination"),P=document.getElementById("pageInfo");P.textContent=`第 ${E.pagination.page} 页,共 ${E.pagination.pages} 页`,document.getElementById("prevPage").disabled=E.pagination.page<=1,document.getElementById("nextPage").disabled=E.pagination.page>=E.pagination.pages,w.style.display=E.pagination.pages>1?"flex":"none"}catch(l){console.error("加载审计日志失败:",l),document.getElementById("auditLogs").innerHTML='<div class="error-state">加载失败: '+l.message+"</div>"}}async function h(){try{const t=new Date,r=new Date(t.getTime()-7*24*60*60*1e3),l=await n.getAuditSummary({startDate:t.toISOString().split("T")[0],endDate:t.toISOString().split("T")[0]}),b=await n.getAuditSummary({startDate:r.toISOString().split("T")[0],endDate:t.toISOString().split("T")[0]});document.getElementById("todayCount").textContent=l.summary.totalLogs,document.getElementById("weekCount").textContent=b.summary.totalLogs,document.getElementById("activeUsers").textContent=b.summary.topUsers.length}catch(t){console.error("加载统计信息失败:",t)}}function M(t){var w,P,G,f,T,x;const r=((w=t.user)==null?void 0:w.username)||"未知用户",l=A(t.action),b=t.resourceTitle||t.resourceId;let E='<strong style="color: #6366f1;">'+r+"</strong> ";switch(t.action){case"document_create":E+='创建了文档 <strong>"'+b+'"</strong>';break;case"document_update":E+='更新了文档 <strong>"'+b+'"</strong>',(P=t.details)!=null&&P.field&&(E+=" 的 <strong>"+p(t.details.field)+"</strong>");break;case"document_delete":E+='删除了文档 <strong>"'+b+'"</strong>';break;case"content_edit":if(E+='编辑了文档 <strong>"'+b+'"</strong> 的内容',t.changes){const R=((G=t.changes.insertions)==null?void 0:G.reduce((K,Z)=>K+Z.length,0))||0,
|
|
1079
|
+
`;const w=document.getElementById("auditPagination"),P=document.getElementById("pageInfo");P.textContent=`第 ${E.pagination.page} 页,共 ${E.pagination.pages} 页`,document.getElementById("prevPage").disabled=E.pagination.page<=1,document.getElementById("nextPage").disabled=E.pagination.page>=E.pagination.pages,w.style.display=E.pagination.pages>1?"flex":"none"}catch(l){console.error("加载审计日志失败:",l),document.getElementById("auditLogs").innerHTML='<div class="error-state">加载失败: '+l.message+"</div>"}}async function h(){try{const t=new Date,r=new Date(t.getTime()-7*24*60*60*1e3),l=await n.getAuditSummary({startDate:t.toISOString().split("T")[0],endDate:t.toISOString().split("T")[0]}),b=await n.getAuditSummary({startDate:r.toISOString().split("T")[0],endDate:t.toISOString().split("T")[0]});document.getElementById("todayCount").textContent=l.summary.totalLogs,document.getElementById("weekCount").textContent=b.summary.totalLogs,document.getElementById("activeUsers").textContent=b.summary.topUsers.length}catch(t){console.error("加载统计信息失败:",t)}}function M(t){var w,P,G,f,T,x;const r=((w=t.user)==null?void 0:w.username)||"未知用户",l=A(t.action),b=t.resourceTitle||t.resourceId;let E='<strong style="color: #6366f1;">'+r+"</strong> ";switch(t.action){case"document_create":E+='创建了文档 <strong>"'+b+'"</strong>';break;case"document_update":E+='更新了文档 <strong>"'+b+'"</strong>',(P=t.details)!=null&&P.field&&(E+=" 的 <strong>"+p(t.details.field)+"</strong>");break;case"document_delete":E+='删除了文档 <strong>"'+b+'"</strong>';break;case"content_edit":if(E+='编辑了文档 <strong>"'+b+'"</strong> 的内容',t.changes){const R=((G=t.changes.insertions)==null?void 0:G.reduce((K,Z)=>K+Z.length,0))||0,H=((f=t.changes.deletions)==null?void 0:f.reduce((K,Z)=>K+Z.length,0))||0;(R>0||H>0)&&(E+=' (<span style="color: #10b981;">+'+R+'</span> / <span style="color: #ef4444;">-'+H+"</span> 字符)")}break;case"title_edit":E+='修改了文档 <strong>"'+b+'"</strong> 的标题',(T=t.details)!=null&&T.oldValue&&((x=t.details)!=null&&x.newValue)&&(E+=' 从 <strong>"'+t.details.oldValue+'"</strong> 改为 <strong>"'+t.details.newValue+'"</strong>');break;case"document_permission_change":E+='修改了文档 <strong>"'+b+'"</strong> 的权限设置';break;default:E+="执行了 <strong>"+l+"</strong> 操作"}return E}function p(t){return{title:"标题",content:"内容",permissions:"权限",status:"状态",tags:"标签",category:"分类",description:"描述"}[t]||t}function i(t){if(t==null)return'<span style="color: var(--text-tertiary); font-style: italic;">空</span>';if(typeof t=="object")return JSON.stringify(t,null,2);const r=String(t);return r.length>500?r.substring(0,500)+'... <span style="color: var(--text-tertiary); font-style: italic;">(内容过长,已截断)</span>':r.replace(/</g,"<").replace(/>/g,">")}window.showAuditDetail=async t=>{var r,l,b,E,w,P;try{const G=localStorage.getItem("token"),x=(await(await fetch(`http://localhost:3000/api/audit/${t}`,{headers:{Authorization:`Bearer ${G}`}})).json()).log,R=document.getElementById("auditDetailModal"),H=document.getElementById("auditDetailContent"),K=te=>({create:"#10b981",update:"#f59e0b",delete:"#ef4444",login:"#6366f1",logout:"#8b5cf6"})[te]||"#6366f1";H.innerHTML=`
|
|
1080
1080
|
<div class="audit-detail" style="padding: 0; background: linear-gradient(135deg, rgba(99, 102, 241, 0.03) 0%, rgba(168, 85, 247, 0.03) 100%);">
|
|
1081
1081
|
|
|
1082
1082
|
<!-- 操作信息 -->
|
|
@@ -1559,7 +1559,7 @@
|
|
|
1559
1559
|
<span class="user-badge">👤 ${a.username}</span>
|
|
1560
1560
|
</div>
|
|
1561
1561
|
</div>
|
|
1562
|
-
`;const u=document.getElementById("whiteboard"),g=u.getContext("2d");let y=!1,h="pen",M="#000000",p=3;document.querySelectorAll(".tool-btn").forEach(r=>{r.addEventListener("click",()=>{document.querySelectorAll(".tool-btn").forEach(l=>l.classList.remove("active")),r.classList.add("active"),h=r.dataset.tool})}),document.getElementById("colorPicker").addEventListener("change",r=>{M=r.target.value}),document.getElementById("brushSize").addEventListener("input",r=>{p=r.target.value});let i=0,t=0;u.addEventListener("mousedown",r=>{y=!0;const l=u.getBoundingClientRect();i=r.clientX-l.left,t=r.clientY-l.top}),u.addEventListener("mousemove",r=>{if(!y)return;const l=u.getBoundingClientRect(),b=r.clientX-l.left,E=r.clientY-l.top;g.beginPath(),g.moveTo(i,t),g.lineTo(b,E),g.strokeStyle=h==="eraser"?"#ffffff":M,g.lineWidth=p,g.lineCap="round",g.stroke(),i=b,t=E,e.sendWhiteboardData(d._id,{tool:h,color:M,size:p,from:{x:i,y:t},to:{x:b,y:E}})}),u.addEventListener("mouseup",()=>{y=!1}),u.addEventListener("mouseleave",()=>{y=!1}),document.getElementById("clearCanvas").addEventListener("click",()=>{confirm("确定要清空画布吗?")&&g.clearRect(0,0,u.width,u.height)}),document.getElementById("saveCanvas").addEventListener("click",()=>{const r=u.toDataURL("image/png"),l=document.createElement("a");l.download=`whiteboard-${Date.now()}.png`,l.href=r,l.click(),alert("白板已保存!")}),e.on("whiteboard_draw",r=>{r.groupId===d._id&&r.userId!==s&&(g.beginPath(),g.moveTo(r.from.x,r.from.y),g.lineTo(r.to.x,r.to.y),g.strokeStyle=r.tool==="eraser"?"#ffffff":r.color,g.lineWidth=r.size,g.lineCap="round",g.stroke())})}async function
|
|
1562
|
+
`;const u=document.getElementById("whiteboard"),g=u.getContext("2d");let y=!1,h="pen",M="#000000",p=3;document.querySelectorAll(".tool-btn").forEach(r=>{r.addEventListener("click",()=>{document.querySelectorAll(".tool-btn").forEach(l=>l.classList.remove("active")),r.classList.add("active"),h=r.dataset.tool})}),document.getElementById("colorPicker").addEventListener("change",r=>{M=r.target.value}),document.getElementById("brushSize").addEventListener("input",r=>{p=r.target.value});let i=0,t=0;u.addEventListener("mousedown",r=>{y=!0;const l=u.getBoundingClientRect();i=r.clientX-l.left,t=r.clientY-l.top}),u.addEventListener("mousemove",r=>{if(!y)return;const l=u.getBoundingClientRect(),b=r.clientX-l.left,E=r.clientY-l.top;g.beginPath(),g.moveTo(i,t),g.lineTo(b,E),g.strokeStyle=h==="eraser"?"#ffffff":M,g.lineWidth=p,g.lineCap="round",g.stroke(),i=b,t=E,e.sendWhiteboardData(d._id,{tool:h,color:M,size:p,from:{x:i,y:t},to:{x:b,y:E}})}),u.addEventListener("mouseup",()=>{y=!1}),u.addEventListener("mouseleave",()=>{y=!1}),document.getElementById("clearCanvas").addEventListener("click",()=>{confirm("确定要清空画布吗?")&&g.clearRect(0,0,u.width,u.height)}),document.getElementById("saveCanvas").addEventListener("click",()=>{const r=u.toDataURL("image/png"),l=document.createElement("a");l.download=`whiteboard-${Date.now()}.png`,l.href=r,l.click(),alert("白板已保存!")}),e.on("whiteboard_draw",r=>{r.groupId===d._id&&r.userId!==s&&(g.beginPath(),g.moveTo(r.from.x,r.from.y),g.lineTo(r.to.x,r.to.y),g.strokeStyle=r.tool==="eraser"?"#ffffff":r.color,g.lineWidth=r.size,g.lineCap="round",g.stroke())})}async function de(m){m.innerHTML=`
|
|
1563
1563
|
<div class="view-header" style="margin-bottom: 30px;">
|
|
1564
1564
|
<h2 style="display: flex; align-items: center; gap: 12px; font-size: 28px;">
|
|
1565
1565
|
<span style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">⚙️ 设置中心</span>
|
|
@@ -1835,7 +1835,7 @@
|
|
|
1835
1835
|
</div>
|
|
1836
1836
|
</div>
|
|
1837
1837
|
</div>
|
|
1838
|
-
`,document.querySelectorAll(".help-card").forEach(u=>{u.addEventListener("mouseenter",()=>{u.style.transform="translateY(-4px)",u.style.boxShadow="0 8px 24px rgba(0,0,0,0.12)"}),u.addEventListener("mouseleave",()=>{u.style.transform="translateY(0)";const g=u.style.background.includes("gradient");u.style.boxShadow=g?"0 4px 12px rgba(102, 126, 234, 0.3)":"0 2px 8px rgba(0,0,0,0.05)"})})}async function ue(m){const u=document.getElementById("contentArea");switch(m){case"groups":await $(u);break;case"tasks":await _(u);break;case"documents":await U(u);break;case"chat":await q(u);break;case"files":await ae(u);break;case"search":await B(u);break;case"call":await V(u);break;case"audit":await ne(u);break;case"polls":await oe(u);break;case"knowledge":await k(u);break;case"workflow":await D(u);break;case"backup":await L(u);break;case"export":await W(u);break;case"ai":await z(u);break;case"export":await W(u);break;case"whiteboard":await F(u);break;case"settings":await
|
|
1838
|
+
`,document.querySelectorAll(".help-card").forEach(u=>{u.addEventListener("mouseenter",()=>{u.style.transform="translateY(-4px)",u.style.boxShadow="0 8px 24px rgba(0,0,0,0.12)"}),u.addEventListener("mouseleave",()=>{u.style.transform="translateY(0)";const g=u.style.background.includes("gradient");u.style.boxShadow=g?"0 4px 12px rgba(102, 126, 234, 0.3)":"0 2px 8px rgba(0,0,0,0.05)"})})}async function ue(m){const u=document.getElementById("contentArea");switch(m){case"groups":await $(u);break;case"tasks":await _(u);break;case"documents":await U(u);break;case"chat":await q(u);break;case"files":await ae(u);break;case"search":await B(u);break;case"call":await V(u);break;case"audit":await ne(u);break;case"polls":await oe(u);break;case"knowledge":await k(u);break;case"workflow":await D(u);break;case"backup":await L(u);break;case"export":await W(u);break;case"ai":await z(u);break;case"export":await W(u);break;case"whiteboard":await F(u);break;case"settings":await de(u);break;case"help":await ce(u);break}}ue("groups")}function yn(a){const e=document.documentElement,o={dark:{primary:"#6366f1",primaryDark:"#4f46e5",secondary:"#8b5cf6",bgDark:"#0f172a",bgCard:"#1e293b",bgHover:"#334155",textPrimary:"#f1f5f9",textSecondary:"#94a3b8",border:"#334155"},blue:{primary:"#3b82f6",primaryDark:"#2563eb",secondary:"#06b6d4",bgDark:"#0c1222",bgCard:"#1a2332",bgHover:"#2a3442",textPrimary:"#e0f2fe",textSecondary:"#7dd3fc",border:"#2a3442"},green:{primary:"#10b981",primaryDark:"#059669",secondary:"#34d399",bgDark:"#0a1f1a",bgCard:"#1a2f2a",bgHover:"#2a3f3a",textPrimary:"#d1fae5",textSecondary:"#6ee7b7",border:"#2a3f3a"},orange:{primary:"#f59e0b",primaryDark:"#d97706",secondary:"#fb923c",bgDark:"#1f1a0a",bgCard:"#2f2a1a",bgHover:"#3f3a2a",textPrimary:"#fef3c7",textSecondary:"#fcd34d",border:"#3f3a2a"},pink:{primary:"#ec4899",primaryDark:"#db2777",secondary:"#f472b6",bgDark:"#1f0a1a",bgCard:"#2f1a2a",bgHover:"#3f2a3a",textPrimary:"#fce7f3",textSecondary:"#f9a8d4",border:"#3f2a3a"},light:{primary:"#6366f1",primaryDark:"#4f46e5",secondary:"#8b5cf6",bgDark:"#ffffff",bgCard:"#f8fafc",bgHover:"#e2e8f0",textPrimary:"#0f172a",textSecondary:"#475569",border:"#cbd5e1"}},n=o[a]||o.dark;e.style.setProperty("--primary",n.primary),e.style.setProperty("--primary-dark",n.primaryDark),e.style.setProperty("--secondary",n.secondary),e.style.setProperty("--bg-dark",n.bgDark),e.style.setProperty("--bg-card",n.bgCard),e.style.setProperty("--bg-hover",n.bgHover),e.style.setProperty("--text-primary",n.textPrimary),e.style.setProperty("--text-secondary",n.textSecondary),e.style.setProperty("--border",n.border)}function bn(a,e){const o=document.getElementById("app"),n=new gt,c=new ze,s=a.id||a._id;let d=null,S=[];function N(B){if(B.startsWith("[白板作品]")){const I=B.replace("[白板作品]","").trim(),A=I.includes("/api/files/")&&I.includes("/download");let v=I;if(A&&!I.includes("token=")){const k=localStorage.getItem("token");v=I.includes("?")?`${I}&token=${k}`:`${I}?token=${k}`}return`
|
|
1839
1839
|
<div style="background: linear-gradient(135deg, rgb(99, 102, 241) 0%, rgb(139, 92, 246) 100%); padding: 16px; border-radius: 12px; box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);">
|
|
1840
1840
|
<div style="margin-bottom: 12px; font-weight: 600; color: white; display: flex; align-items: center; gap: 6px; font-size: 15px;">
|
|
1841
1841
|
<span style="font-size: 18px;">🎨</span>
|
|
@@ -1869,7 +1869,7 @@
|
|
|
1869
1869
|
📋 查看投票详情
|
|
1870
1870
|
</div>
|
|
1871
1871
|
</div>
|
|
1872
|
-
`}return B}window.viewPollDetail=async B=>{try{const A=(await n.getPoll(B)).poll,v=A.options.reduce((F,
|
|
1872
|
+
`}return B}window.viewPollDetail=async B=>{try{const A=(await n.getPoll(B)).poll,v=A.options.reduce((F,de)=>F+de.votes.length,0),k=A.options.some(F=>F.votes.includes(s)),C=A.status==="ended"||A.endTime&&new Date(A.endTime)<new Date;let D="";A.options.forEach((F,de)=>{const X=v>0?(F.votes.length/v*100).toFixed(1):0,ce=F.votes.includes(s);D+=`
|
|
1873
1873
|
<div class="poll-option" style="margin-bottom: 12px; padding: 12px; background: var(--bg-tertiary); border-radius: 8px; border: 2px solid ${ce?"var(--primary)":"var(--border)"};">
|
|
1874
1874
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
|
1875
1875
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
@@ -1937,11 +1937,11 @@
|
|
|
1937
1937
|
<h3 style="margin-bottom: 16px; color: var(--text-primary);">投票选项</h3>
|
|
1938
1938
|
${!C&&!k?`
|
|
1939
1939
|
<form id="voteForm">
|
|
1940
|
-
${A.options.map((F,
|
|
1940
|
+
${A.options.map((F,de)=>{const X=v>0?(F.votes.length/v*100).toFixed(1):0;return`
|
|
1941
1941
|
<div class="poll-option" style="margin-bottom: 12px; padding: 12px; background: var(--bg-tertiary); border-radius: 8px; border: 2px solid var(--border); cursor: pointer;" onmouseover="this.style.borderColor='var(--primary)'" onmouseout="this.style.borderColor='var(--border)'">
|
|
1942
1942
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
|
1943
1943
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
1944
|
-
<input type="${A.allowMultiple?"checkbox":"radio"}" name="poll-option" value="${
|
|
1944
|
+
<input type="${A.allowMultiple?"checkbox":"radio"}" name="poll-option" value="${de}" style="width: 18px; height: 18px; cursor: pointer;">
|
|
1945
1945
|
<span style="font-weight: 500; color: var(--text-primary);">${F.text}</span>
|
|
1946
1946
|
</div>
|
|
1947
1947
|
<span style="font-weight: 600; color: var(--primary);">${F.votes.length} 票 (${X}%)</span>
|
|
@@ -1965,7 +1965,7 @@
|
|
|
1965
1965
|
</div>
|
|
1966
1966
|
</div>
|
|
1967
1967
|
</div>
|
|
1968
|
-
`,z=document.getElementById("pollDetailModal");z&&z.remove(),document.body.insertAdjacentHTML("beforeend",L),document.getElementById("closePollDetailModal").addEventListener("click",()=>{document.getElementById("pollDetailModal").remove()}),document.getElementById("closePollDetailBtn").addEventListener("click",()=>{document.getElementById("pollDetailModal").remove()});const W=document.getElementById("voteForm");W&&!C&&!k&&W.addEventListener("submit",async F=>{F.preventDefault();const
|
|
1968
|
+
`,z=document.getElementById("pollDetailModal");z&&z.remove(),document.body.insertAdjacentHTML("beforeend",L),document.getElementById("closePollDetailModal").addEventListener("click",()=>{document.getElementById("pollDetailModal").remove()}),document.getElementById("closePollDetailBtn").addEventListener("click",()=>{document.getElementById("pollDetailModal").remove()});const W=document.getElementById("voteForm");W&&!C&&!k&&W.addEventListener("submit",async F=>{F.preventDefault();const de=Array.from(document.querySelectorAll('input[name="poll-option"]:checked')).map(X=>parseInt(X.value));if(de.length===0){alert("请选择至少一个选项!");return}try{await n.vote(B,de),alert("投票成功!"),document.getElementById("pollDetailModal").remove();const X=document.querySelector(".nav-item.active");if(X&&X.dataset.view==="tasks"){const ce=document.getElementById("contentArea");await _(ce)}}catch(X){console.error("投票失败:",X),alert("投票失败:"+X.message)}})}catch(I){console.error("加载投票详情失败:",I),alert("加载投票详情失败:"+I.message)}},o.innerHTML=`
|
|
1969
1969
|
<div class="dashboard">
|
|
1970
1970
|
<aside class="sidebar">
|
|
1971
1971
|
<div class="sidebar-header">
|
|
@@ -2147,7 +2147,7 @@
|
|
|
2147
2147
|
</form>
|
|
2148
2148
|
</div>
|
|
2149
2149
|
</div>
|
|
2150
|
-
`;const A=document.getElementById("filesList");!I.files||I.files.length===0?A.innerHTML='<div class="empty-state">暂无文件</div>':(I.files.forEach(v=>{const k=document.createElement("div");k.className="file-card";const C=
|
|
2150
|
+
`;const A=document.getElementById("filesList");!I.files||I.files.length===0?A.innerHTML='<div class="empty-state">暂无文件</div>':(I.files.forEach(v=>{const k=document.createElement("div");k.className="file-card";const C=re(v.mimetype),D=pe(v.size),L=v.uploader._id||v.uploader.id||v.uploader,z=String(L)===String(s);k.innerHTML=`
|
|
2151
2151
|
<div class="file-icon">${C}</div>
|
|
2152
2152
|
<div class="file-info">
|
|
2153
2153
|
<h4>${v.originalName}</h4>
|
|
@@ -2167,7 +2167,7 @@
|
|
|
2167
2167
|
<h2>文件共享</h2>
|
|
2168
2168
|
</div>
|
|
2169
2169
|
<div class="empty-state">加载文件失败: ${I.message}</div>
|
|
2170
|
-
`}}function
|
|
2170
|
+
`}}function re(B){return B.startsWith("image/")?"🖼️":B==="application/pdf"?"📕":B.includes("word")||B.includes("document")?"📘":B.includes("excel")||B.includes("spreadsheet")?"📗":B.includes("zip")||B.includes("compressed")?"📦":"📄"}function pe(B){if(B===0)return"0 Bytes";const I=1024,A=["Bytes","KB","MB","GB"],v=Math.floor(Math.log(B)/Math.log(I));return Math.round(B/Math.pow(I,v)*100)/100+" "+A[v]}async function q(B){if(!d){B.innerHTML=`
|
|
2171
2171
|
<div class="empty-state" style="text-align: center; padding: 60px 20px; background: var(--bg-secondary); border-radius: 16px; border: 2px dashed var(--border);">
|
|
2172
2172
|
<div style="font-size: 64px; margin-bottom: 20px;">💬</div>
|
|
2173
2173
|
<h3 style="font-size: 24px; margin-bottom: 12px; color: var(--text-primary);">群聊</h3>
|
|
@@ -2176,7 +2176,7 @@
|
|
|
2176
2176
|
前往我的群组
|
|
2177
2177
|
</button>
|
|
2178
2178
|
</div>
|
|
2179
|
-
`;return}try{let ce=function(i,t,r="💬"){"Notification"in window&&Notification.permission==="granted"&&new Notification(i,{body:t,icon:"/icon.png",badge:"/icon.png",tag:"chat-message"})},g=function(){const i=document.getElementById("whiteboard");if(!i||i.dataset.initialized)return;i.dataset.initialized="true";const t=i.getContext("2d");i.width=i.offsetWidth,i.height=i.offsetHeight;let r=!1,l="pen",b="#667eea",E=3;document.querySelectorAll(".tool-btn").forEach(w=>{w.addEventListener("click",()=>{l=w.dataset.tool,document.querySelectorAll(".tool-btn").forEach(P=>{P.style.background="var(--bg-secondary)",P.style.color="var(--text-primary)",P.style.border="1px solid var(--border)"}),w.style.background="var(--primary)",w.style.color="white",w.style.border="none"})}),document.getElementById("colorPicker").addEventListener("change",w=>{b=w.target.value}),document.getElementById("brushSize").addEventListener("input",w=>{E=w.target.value}),document.getElementById("clearCanvas").addEventListener("click",()=>{confirm("确定要清空画布吗?")&&(t.clearRect(0,0,i.width,i.height),e.sendWhiteboardClear(d._id))}),document.getElementById("sendWhiteboardBtn").addEventListener("click",async()=>{try{const w=i.toDataURL("image/png"),P=document.createElement("canvas");P.width=i.width,P.height=i.height;const G=P.toDataURL("image/png");if(w===G){alert("画布是空的,请先绘制内容!");return}if(w.length*.75/1024/1024<1)e.sendChatMessage(d._id,a.username,`[白板作品]${w}`),alert("白板作品已发送到群聊!");else{const T=await fetch(w).then(K=>K.blob()),x=new FormData;x.append("file",T,`whiteboard-${Date.now()}.png`),x.append("groupId",d._id),x.append("description","协作白板作品");const R=localStorage.getItem("token"),
|
|
2179
|
+
`;return}try{let ce=function(i,t,r="💬"){"Notification"in window&&Notification.permission==="granted"&&new Notification(i,{body:t,icon:"/icon.png",badge:"/icon.png",tag:"chat-message"})},g=function(){const i=document.getElementById("whiteboard");if(!i||i.dataset.initialized)return;i.dataset.initialized="true";const t=i.getContext("2d");i.width=i.offsetWidth,i.height=i.offsetHeight;let r=!1,l="pen",b="#667eea",E=3;document.querySelectorAll(".tool-btn").forEach(w=>{w.addEventListener("click",()=>{l=w.dataset.tool,document.querySelectorAll(".tool-btn").forEach(P=>{P.style.background="var(--bg-secondary)",P.style.color="var(--text-primary)",P.style.border="1px solid var(--border)"}),w.style.background="var(--primary)",w.style.color="white",w.style.border="none"})}),document.getElementById("colorPicker").addEventListener("change",w=>{b=w.target.value}),document.getElementById("brushSize").addEventListener("input",w=>{E=w.target.value}),document.getElementById("clearCanvas").addEventListener("click",()=>{confirm("确定要清空画布吗?")&&(t.clearRect(0,0,i.width,i.height),e.sendWhiteboardClear(d._id))}),document.getElementById("sendWhiteboardBtn").addEventListener("click",async()=>{try{const w=i.toDataURL("image/png"),P=document.createElement("canvas");P.width=i.width,P.height=i.height;const G=P.toDataURL("image/png");if(w===G){alert("画布是空的,请先绘制内容!");return}if(w.length*.75/1024/1024<1)e.sendChatMessage(d._id,a.username,`[白板作品]${w}`),alert("白板作品已发送到群聊!");else{const T=await fetch(w).then(K=>K.blob()),x=new FormData;x.append("file",T,`whiteboard-${Date.now()}.png`),x.append("groupId",d._id),x.append("description","协作白板作品");const R=localStorage.getItem("token"),H=await fetch("http://localhost:3000/api/files/upload",{method:"POST",headers:{Authorization:`Bearer ${R}`},body:x});if(H.ok){const J=`http://localhost:3000/api/files/${(await H.json()).file._id}/download?token=${R}`;e.sendChatMessage(d._id,a.username,`[白板作品]${J}`),alert("白板作品已发送到群聊!")}else throw new Error("上传失败")}}catch(w){console.error("发送白板失败:",w),alert("发送失败,请重试!")}}),i.addEventListener("mousedown",w=>{r=!0;const P=i.getBoundingClientRect(),G=w.clientX-P.left,f=w.clientY-P.top;t.beginPath(),t.moveTo(G,f)}),i.addEventListener("mousemove",w=>{if(!r)return;const P=i.getBoundingClientRect(),G=w.clientX-P.left,f=w.clientY-P.top;t.lineWidth=E,t.lineCap="round",l==="pen"?(t.strokeStyle=b,t.globalCompositeOperation="source-over"):l==="eraser"&&(t.globalCompositeOperation="destination-out"),t.lineTo(G,f),t.stroke(),e.sendWhiteboardDraw(d._id,{tool:l,color:b,size:E,x:G,y:f})}),i.addEventListener("mouseup",()=>{r=!1}),i.addEventListener("mouseleave",()=>{r=!1}),e.on("whiteboard_draw",w=>{w.groupId===d._id&&(t.lineWidth=w.size,t.lineCap="round",w.tool==="pen"?(t.strokeStyle=w.color,t.globalCompositeOperation="source-over"):w.tool==="eraser"&&(t.globalCompositeOperation="destination-out"),t.lineTo(w.x,w.y),t.stroke())}),e.on("whiteboard_clear",w=>{w.groupId===d._id&&t.clearRect(0,0,i.width,i.height)})};var I=ce,A=g;const k=(await n.getGroup(d._id)).group,C=!!k.mutedAll,D=(k.mutedUsers||[]).map(String).includes(String(s)),L=!C&&!D;B.innerHTML=`
|
|
2180
2180
|
<div class="view-header" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 12px; margin-bottom: 20px;">
|
|
2181
2181
|
<h2 style="margin: 0; display: flex; align-items: center; gap: 12px;">
|
|
2182
2182
|
<span style="font-size: 32px;">💬</span>
|
|
@@ -2251,7 +2251,7 @@
|
|
|
2251
2251
|
<div style="font-size: 48px; margin-bottom: 16px;">💬</div>
|
|
2252
2252
|
<p>还没有消息,开始聊天吧!</p>
|
|
2253
2253
|
</div>
|
|
2254
|
-
`:(i.messages.forEach(t=>{const r=document.createElement("div"),l=String(t.sender)===String(s)||t.username===a.username,b=
|
|
2254
|
+
`:(i.messages.forEach(t=>{const r=document.createElement("div"),l=String(t.sender)===String(s)||t.username===a.username,b=N(t.content),E=t.content.startsWith("[白板作品]")||t.content.startsWith("[投票]"),w=t.content.startsWith("[白板作品]");r.className=`message ${l?"own":""}`,r.style.cssText=`
|
|
2255
2255
|
margin-bottom: 16px;
|
|
2256
2256
|
display: flex;
|
|
2257
2257
|
flex-direction: column;
|
|
@@ -2268,12 +2268,12 @@
|
|
|
2268
2268
|
<p>加载历史消息失败</p>
|
|
2269
2269
|
<p style="font-size: 14px; color: var(--text-tertiary);">${i.message}</p>
|
|
2270
2270
|
</div>
|
|
2271
|
-
`}const
|
|
2271
|
+
`}const de=document.getElementById("emojiBtn"),X=document.getElementById("emojiPicker");L&&(de.addEventListener("click",()=>{X.classList.toggle("hidden")}),X.addEventListener("emoji-click",i=>{W.value+=i.detail.unicode,W.focus(),X.classList.add("hidden")}),document.addEventListener("click",i=>{!de.contains(i.target)&&!X.contains(i.target)&&X.classList.add("hidden")})),"Notification"in window&&Notification.permission==="default"&&Notification.requestPermission(),e.on("chat_message",i=>{if(i.groupId===d._id){const t=document.createElement("div"),r=String(i.userId)===String(s)||i.username===a.username;t.className=`message ${r?"own":""}`,t.style.cssText=`
|
|
2272
2272
|
margin-bottom: 16px;
|
|
2273
2273
|
display: flex;
|
|
2274
2274
|
flex-direction: column;
|
|
2275
2275
|
align-items: ${r?"flex-end":"flex-start"};
|
|
2276
|
-
`;const l=
|
|
2276
|
+
`;const l=N(i.content),b=i.content.startsWith("[白板作品]")||i.content.startsWith("[投票]"),E=i.content.startsWith("[白板作品]");t.innerHTML=`
|
|
2277
2277
|
<div class="message-header" style="display: flex; gap: 8px; margin-bottom: 4px; font-size: 12px; color: var(--text-tertiary);">
|
|
2278
2278
|
<span class="message-user">${i.username}</span>
|
|
2279
2279
|
<span class="message-time">${new Date(i.timestamp).toLocaleTimeString("zh-CN")}</span>
|
package/dist/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>CollabDocChat - 协作文档聊天平台</title>
|
|
7
7
|
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
|
8
8
|
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-9XAJPSRa.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/assets/index-D8ZqeoaM.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
package/package.json
CHANGED
package/scripts/start-simple.js
CHANGED
package/server/routes/polls.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import Poll from '../models/Poll.js';
|
|
3
3
|
import Group from '../models/Group.js';
|
|
4
|
+
import User from '../models/User.js';
|
|
4
5
|
import { authenticate, isAdmin } from '../middleware/auth.js';
|
|
5
6
|
|
|
6
7
|
const router = express.Router();
|
|
@@ -86,8 +87,7 @@ router.get('/:pollId', authenticate, async (req, res) => {
|
|
|
86
87
|
const { pollId } = req.params;
|
|
87
88
|
|
|
88
89
|
const poll = await Poll.findById(pollId)
|
|
89
|
-
.populate('creator', 'username')
|
|
90
|
-
.populate('options.votes.user', 'username');
|
|
90
|
+
.populate('creator', 'username');
|
|
91
91
|
|
|
92
92
|
if (!poll) {
|
|
93
93
|
return res.status(404).json({ error: '投票不存在' });
|
|
@@ -99,8 +99,27 @@ router.get('/:pollId', authenticate, async (req, res) => {
|
|
|
99
99
|
return res.status(403).json({ error: '您不是该群组成员' });
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
//
|
|
103
|
-
if (poll.anonymous) {
|
|
102
|
+
// 手动 populate 每个 vote 的 user(Mongoose 的嵌套 populate 可能不工作)
|
|
103
|
+
if (!poll.anonymous) {
|
|
104
|
+
for (let option of poll.options) {
|
|
105
|
+
for (let vote of option.votes) {
|
|
106
|
+
// 如果 user 是 ObjectId 且没有 username,尝试 populate
|
|
107
|
+
if (vote.user && (!vote.user.username || typeof vote.user === 'object' && vote.user.toString && vote.user.toString().length === 24)) {
|
|
108
|
+
try {
|
|
109
|
+
const userId = vote.user.toString ? vote.user.toString() : vote.user;
|
|
110
|
+
const user = await User.findById(userId).select('username');
|
|
111
|
+
if (user) {
|
|
112
|
+
vote.user = { _id: user._id, username: user.username };
|
|
113
|
+
}
|
|
114
|
+
} catch (err) {
|
|
115
|
+
// 如果 populate 失败,使用已保存的 username
|
|
116
|
+
console.error('Populate vote user failed:', err);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
// 如果是匿名投票,隐藏投票者信息
|
|
104
123
|
poll.options.forEach(option => {
|
|
105
124
|
option.votes = option.votes.map(vote => ({
|
|
106
125
|
votedAt: vote.votedAt
|
|
@@ -1329,7 +1329,7 @@ export function renderAdminDashboard(user, wsService) {
|
|
|
1329
1329
|
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px;">
|
|
1330
1330
|
<div>
|
|
1331
1331
|
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">创建者</div>
|
|
1332
|
-
<div style="font-weight: 600;">👤 ${poll.creatorName}</div>
|
|
1332
|
+
<div style="font-weight: 600;">👤 ${poll.creatorName || (poll.creator && poll.creator.username) || '未知用户'}</div>
|
|
1333
1333
|
</div>
|
|
1334
1334
|
<div>
|
|
1335
1335
|
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">总投票数</div>
|
|
@@ -1356,14 +1356,19 @@ export function renderAdminDashboard(user, wsService) {
|
|
|
1356
1356
|
let votersList = '';
|
|
1357
1357
|
if (!poll.anonymous && option.votes.length > 0) {
|
|
1358
1358
|
const voters = option.votes.map(v => {
|
|
1359
|
-
//
|
|
1360
|
-
if (typeof v === 'object'
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1359
|
+
// 处理不同的数据结构:优先使用 populate 后的 user.username,其次使用直接保存的 username
|
|
1360
|
+
if (v && typeof v === 'object') {
|
|
1361
|
+
// 如果 user 被 populate 了,使用 user.username
|
|
1362
|
+
if (v.user && typeof v.user === 'object' && v.user.username) {
|
|
1363
|
+
return v.user.username;
|
|
1364
|
+
}
|
|
1365
|
+
// 如果 user 是 ObjectId,但直接保存了 username
|
|
1366
|
+
if (v.username && typeof v.username === 'string') {
|
|
1367
|
+
return v.username;
|
|
1368
|
+
}
|
|
1364
1369
|
}
|
|
1365
|
-
return
|
|
1366
|
-
}).filter(name => name !== '未知用户');
|
|
1370
|
+
return null;
|
|
1371
|
+
}).filter(name => name !== null && name !== '未知用户');
|
|
1367
1372
|
|
|
1368
1373
|
if (voters.length > 0) {
|
|
1369
1374
|
votersList = `
|