cli-jaw 1.6.12 → 1.6.13
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/bin/commands/dispatch.js +41 -14
- package/dist/bin/commands/dispatch.js.map +1 -1
- package/dist/bin/commands/launchd.js +3 -2
- package/dist/bin/commands/launchd.js.map +1 -1
- package/dist/bin/commands/service.js +4 -3
- package/dist/bin/commands/service.js.map +1 -1
- package/dist/server.js +32 -5
- package/dist/server.js.map +1 -1
- package/dist/src/agent/lifecycle-handler.js +10 -4
- package/dist/src/agent/lifecycle-handler.js.map +1 -1
- package/dist/src/agent/spawn.js +48 -13
- package/dist/src/agent/spawn.js.map +1 -1
- package/dist/src/cli/handlers-runtime.js +4 -1
- package/dist/src/cli/handlers-runtime.js.map +1 -1
- package/dist/src/cli/handlers.js +4 -4
- package/dist/src/cli/handlers.js.map +1 -1
- package/dist/src/core/config.js +40 -3
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/db.js +12 -0
- package/dist/src/core/db.js.map +1 -1
- package/dist/src/core/instance.js +18 -4
- package/dist/src/core/instance.js.map +1 -1
- package/dist/src/core/runtime-path.js +69 -0
- package/dist/src/core/runtime-path.js.map +1 -0
- package/dist/src/memory/shared.js +48 -15
- package/dist/src/memory/shared.js.map +1 -1
- package/dist/src/routes/avatar.js +120 -0
- package/dist/src/routes/avatar.js.map +1 -0
- package/dist/src/telegram/bot.js +7 -1
- package/dist/src/telegram/bot.js.map +1 -1
- package/package.json +1 -1
- package/public/css/chat.css +27 -2
- package/public/css/layout.css +26 -0
- package/public/dist/assets/{employees-CA_DI2gy.js → employees-C2G0-Rg9.js} +1 -1
- package/public/dist/assets/index-CDdXQQmm.css +1 -0
- package/public/dist/assets/{index-6UFnW9uO.js → index-CIWCSFl-.js} +4 -4
- package/public/dist/assets/{render-C2tuSVTL.js → render-BFAkzW1S.js} +1 -1
- package/public/dist/assets/{settings-BJcG1r6G.js → settings-BtX9STQd.js} +1 -1
- package/public/dist/assets/settings-DUWhygHi.js +1 -0
- package/public/dist/assets/skills-C6aTdbWY.js +1 -0
- package/public/dist/assets/{skills-D3cWRZOl.js → skills-C9o5E1Pc.js} +1 -1
- package/public/dist/assets/slash-commands-C1p8kRBv.js +1 -0
- package/public/dist/assets/{slash-commands-PkW1NPle.js → slash-commands-DveLHSQt.js} +1 -1
- package/public/dist/assets/ui-BpZlLDtM.js +1 -0
- package/public/dist/assets/ui-Dx3w-H-4.js +131 -0
- package/public/dist/assets/{ws-Dcq99IkD.js → ws-D39_cIa_.js} +1 -1
- package/public/dist/index.html +14 -4
- package/public/index.html +12 -2
- package/public/js/features/avatar.ts +206 -32
- package/public/js/main.ts +1 -1
- package/public/js/ui.ts +15 -9
- package/public/js/uuid.ts +24 -0
- package/public/js/virtual-scroll.ts +21 -5
- package/public/js/ws.ts +6 -2
- package/public/locales/en.json +2 -3
- package/public/locales/ko.json +2 -3
- package/public/dist/assets/index-ck7lqnh7.css +0 -1
- package/public/dist/assets/settings-DV5X1U8f.js +0 -1
- package/public/dist/assets/skills-idPvxY0n.js +0 -1
- package/public/dist/assets/slash-commands-BHtBaFWh.js +0 -1
- package/public/dist/assets/ui-BdW-cWnY.js +0 -131
- package/public/dist/assets/ui-qcenMIau.js +0 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/vendor-render-Bjnw0wQ6.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{t as e}from"./state-O6NVkWcL.js";import{i as t,t as n}from"./api-DygAf_G_.js";import{Z as r}from"./vendor-mermaid-C2RBgdM6.js";import{c as i,h as a,i as o,l as s,n as c,o as l,r as u,s as d}from"./render-BFAkzW1S.js";import{a as f,i as p,r as m,t as ee}from"./idb-cache-DbK81tgv.js";function h(){let e=globalThis.crypto;if(typeof e?.randomUUID==`function`)return e.randomUUID();if(typeof e?.getRandomValues!=`function`)return`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)});let t=new Uint8Array(16);e.getRandomValues(t),t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,e=>e.toString(16).padStart(2,`0`)).join(``);return`${n.slice(0,8)}-${n.slice(8,12)}-${n.slice(12,16)}-${n.slice(16,20)}-${n.slice(20)}`}var te=`agentName`,g=`CLI-JAW`,_=g;function v(){return _}function ne(e){_=(e||``).trim()||g,localStorage.setItem(te,_);let t=document.getElementById(`appNameInput`);t&&(t.value=_)}function re(){_=localStorage.getItem(te)||g;let e=document.getElementById(`appNameInput`);e&&(e.value=_),document.getElementById(`appNameSave`)?.addEventListener(`click`,()=>{let e=document.getElementById(`appNameInput`);e&&ne(e.value)}),document.getElementById(`appNameInput`)?.addEventListener(`keydown`,e=>{let t=e;t.key===`Enter`&&(t.preventDefault(),ne(t.target.value),t.target.blur())})}var ie=`agentAvatar`,ae=`userAvatar`,y=`🦈`,b=`👤`,oe={agent:{emoji:y,imageUrl:``,updatedAt:null},user:{emoji:b,imageUrl:``,updatedAt:null}},se=!1;function x(e){return oe[e]}function ce(e){return e===`agent`?ie:ae}function le(e){return e===`agent`?`agentAvatarInput`:`userAvatarInput`}function ue(e){return e===`agent`?`agentAvatarStatus`:`userAvatarStatus`}function de(e){return e===`agent`?`.agent-icon`:`.user-icon`}function fe(e){return x(e).emoji}function S(e){let t=document.getElementById(le(e));t&&(t.value=fe(e));let n=document.getElementById(ue(e));n&&(n.textContent=x(e).imageUrl?`image active`:`emoji active`)}function C(e,t){let n=document.getElementById(ue(e));n&&(n.textContent=t)}function w(e){let t=x(e);return t.imageUrl?`<img class="avatar-image" src="${c(t.imageUrl)}" alt="" loading="lazy" decoding="async">`:c(t.emoji)}function T(e){let t=w(e),n=x(e).imageUrl?`image`:`emoji`;document.querySelectorAll(de(e)).forEach(e=>{e.innerHTML=t,e.setAttribute(`data-avatar-kind`,n)})}function E(e,t){t?.kind===`image`&&t.imageUrl?(x(e).imageUrl=t.imageUrl,x(e).updatedAt=t.updatedAt??Date.now()):(x(e).imageUrl=``,x(e).updatedAt=t?.updatedAt??null),S(e),T(e)}async function pe(){let e=await n(`/api/avatar`);e&&(E(`agent`,e.agent),E(`user`,e.user))}async function me(e,n){let r=await t(),i=new Headers(n.headers||{});return r&&i.set(`Authorization`,`Bearer ${r}`),fetch(e,{...n,headers:i})}async function he(e,t){C(e,`uploading...`);let n=await me(`/api/avatar/${e}/upload`,{method:`POST`,headers:{"X-Filename":encodeURIComponent(t.name)},body:t}),r=await n.json().catch(()=>null);if(!n.ok)throw C(e,`upload failed`),Error(r?.error||`avatar upload failed (${n.status})`);E(e,r?.data||r)}async function ge(e){C(e,`resetting...`);let t=await me(`/api/avatar/${e}/image`,{method:`DELETE`}),n=await t.json().catch(()=>null);if(!t.ok)throw C(e,`reset failed`),Error(n?.error||`avatar reset failed (${t.status})`);E(e,n?.data||n)}function _e(e){let t=e===`agent`?`agentAvatarUploadBtn`:`userAvatarUploadBtn`,n=e===`agent`?`agentAvatarResetBtn`:`userAvatarResetBtn`,r=e===`agent`?`agentAvatarFile`:`userAvatarFile`;document.getElementById(t)?.addEventListener(`click`,()=>{document.getElementById(r)?.click()}),document.getElementById(n)?.addEventListener(`click`,async()=>{try{await ge(e)}catch(e){console.warn(`[avatar:reset]`,e.message)}}),document.getElementById(r)?.addEventListener(`change`,async t=>{let n=t.target,r=n.files?.[0];if(r)try{await he(e,r)}catch(e){console.warn(`[avatar:upload]`,e.message)}finally{n.value=``}})}function ve(){return w(`agent`)}function D(){return w(`user`)}function ye(e){let t=(e||``).trim()||y;x(`agent`).emoji=t,localStorage.setItem(ce(`agent`),t),S(`agent`),x(`agent`).imageUrl||T(`agent`)}function be(e){let t=(e||``).trim()||b;x(`user`).emoji=t,localStorage.setItem(ce(`user`),t),S(`user`),x(`user`).imageUrl||T(`user`)}async function xe(){if(x(`agent`).emoji=localStorage.getItem(ie)||y,x(`user`).emoji=localStorage.getItem(ae)||b,S(`agent`),S(`user`),!se){se=!0,document.getElementById(`avatarSave`)?.addEventListener(`click`,()=>{let e=document.getElementById(`agentAvatarInput`),t=document.getElementById(`userAvatarInput`);e&&ye(e.value),t&&be(t.value)});for(let e of[`agentAvatarInput`,`userAvatarInput`])document.getElementById(e)?.addEventListener(`keydown`,e=>{let t=e;t.key===`Enter`&&(t.preventDefault(),document.getElementById(`avatarSave`)?.click(),t.target.blur())});_e(`agent`),_e(`user`)}await pe(),T(`agent`),T(`user`)}var Se=5,O=80,Ce=class{items=[];container;spacerTop;spacerBottom;viewport;_active=!1;_totalHeight=0;rafId=null;firstVisible=0;lastVisible=0;onLazyRender=null;onPostRender=null;constructor(e){this.container=document.getElementById(e),this.spacerTop=document.createElement(`div`),this.spacerTop.className=`vs-spacer-top`,this.spacerBottom=document.createElement(`div`),this.spacerBottom.className=`vs-spacer-bottom`,this.viewport=document.createElement(`div`),this.viewport.className=`vs-viewport`}get active(){return this._active}get count(){return this.items.length}flushToDOM(){this._active&&(this.container.classList.remove(`vs-active`),this.container.removeEventListener(`scroll`,this.scrollHandler),this.rafId&&=(cancelAnimationFrame(this.rafId),null),this.container.innerHTML=this.items.map(e=>e.html).join(``),this._active=!1,this.firstVisible=0,this.lastVisible=0,this.items=[],this._totalHeight=0)}addItem(e,t){let n={id:e,html:t,height:O};this.items.push(n),this._totalHeight+=O,!this._active&&this.items.length>=80&&this.activate(),this._active&&this.scheduleRender()}appendLiveItem(e){if(!this._active)return;let t=e.outerHTML,n={id:h(),html:t,height:O};this.items.push(n),this._totalHeight+=O,this.render(),this.scrollToBottom()}updateItemHtml(e,t){this.items[e]&&(this.items[e].html=t)}scrollHandler=()=>this.scheduleRender();activate(){this._active=!0,this._totalHeight=0,this.container.querySelectorAll(`.msg`).forEach((e,t)=>{this.items[t]&&(this.items[t].height=e.getBoundingClientRect().height)});for(let e of this.items)this._totalHeight+=e.height;this.container.classList.add(`vs-active`),this.container.replaceChildren(this.spacerTop,this.viewport,this.spacerBottom),this.container.addEventListener(`scroll`,this.scrollHandler,{passive:!0}),this.render()}scheduleRender(){this.rafId||=requestAnimationFrame(()=>{this.rafId=null,this.render()})}render(){let e=this.container.scrollTop,t=this.container.clientHeight,n=0,r=this.items.length-1;for(let t=0;t<this.items.length;t++){if(n+this.items[t].height>e){r=t;break}n+=this.items[t].height}let i=Math.max(0,r-Se),a=n,o=r;for(let n=r;n<this.items.length&&(a+=this.items[n].height,o=n,!(a>e+t));n++);let s=Math.min(this.items.length-1,o+Se);if(i===this.firstVisible&&s===this.lastVisible)return;this.firstVisible=i,this.lastVisible=s;let c=0;for(let e=0;e<i;e++)c+=this.items[e].height;let l=0;for(let e=s+1;e<this.items.length;e++)l+=this.items[e].height;this.spacerTop.style.height=`${c}px`,this.spacerBottom.style.height=`${l}px`;let u=new Map;for(let e of Array.from(this.viewport.children)){let t=Number(e.dataset.vsIdx);isNaN(t)||u.set(t,e)}for(let[e,t]of u)(e<i||e>s)&&(t.remove(),u.delete(e));let d=[];for(let e=i;e<=s;e++){let t=u.get(e);if(t)d.push(t);else{let t=this.items[e],n=document.createElement(`div`);n.innerHTML=t.html;let r=n.firstElementChild;r&&(r.dataset.vsIdx=String(e),d.push(r))}}let f=this.viewport.firstChild;for(let e of d)e===f?f=f.nextSibling:this.viewport.insertBefore(e,f);if(this.onLazyRender){let e=this.viewport.querySelectorAll(`.lazy-pending`);e.length>0&&this.onLazyRender(Array.from(e))}this.onPostRender&&this.onPostRender(this.viewport),this.remeasureVisible()}remeasureVisible(){let e=this.container.scrollHeight-this.container.scrollTop-this.container.clientHeight<80,t=[];this.viewport.querySelectorAll(`[data-vs-idx]`).forEach(e=>{let n=Number(e.dataset.vsIdx);this.items[n]&&t.push({idx:n,newH:e.getBoundingClientRect().height})});let n=!1;for(let{idx:e,newH:r}of t){let t=this.items[e].height;t!==r&&(this.items[e].height=r,this._totalHeight+=r-t,n=!0)}n&&e&&this.scrollToBottom()}scrollToBottom(){this.container.scrollTop=this.container.scrollHeight,this.scheduleRender()}clear(){this.items=[],this._totalHeight=0,this._active&&(this.container.classList.remove(`vs-active`),this.container.removeEventListener(`scroll`,this.scrollHandler),this.viewport.innerHTML=``,this.spacerTop.style.height=`0`,this.spacerBottom.style.height=`0`,this.container.innerHTML=``),this._active=!1,this.firstVisible=0,this.lastVisible=0,this.onLazyRender=null,this.onPostRender=null,this.rafId&&=(cancelAnimationFrame(this.rafId),null)}},we=null;function k(){return we||=new Ce(`chatMessages`),we}var Te=2e3,Ee=80;function De(e){return{chunks:[],fullText:``,textDirty:!1,element:e,pendingRAF:null,isFinalized:!1,lastRenderTime:0}}function A(e){return e.textDirty&&=(e.fullText=e.chunks.join(``),!1),e.fullText}function Oe(e,t){e.chunks.push(t),e.textDirty=!0,!e.pendingRAF&&!e.isFinalized&&(e.pendingRAF=requestAnimationFrame(()=>{if(e.pendingRAF=null,e.isFinalized)return;let t=performance.now(),n=A(e);n.length<Te||t-e.lastRenderTime>Ee?(e.element.innerHTML=o(n,!0)+`<span class="stream-cursor" aria-hidden="true"></span>`,e.lastRenderTime=t):e.pendingRAF=requestAnimationFrame(()=>{e.pendingRAF=null,!e.isFinalized&&(e.element.innerHTML=o(A(e),!0)+`<span class="stream-cursor" aria-hidden="true"></span>`,e.lastRenderTime=performance.now())})}))}function ke(e,t=!1){e.isFinalized=!0,e.pendingRAF&&=(cancelAnimationFrame(e.pendingRAF),null);let n=A(e);return t||(e.element.innerHTML=o(n)),n}var Ae=[`cdnjs.cloudflare.com`,`cdn.jsdelivr.net`,`unpkg.com`,`esm.sh`,`fonts.googleapis.com`,`fonts.gstatic.com`],je=[/\beval\s*\(/,/\bnew\s+Function\s*\(/,/\bdocument\.cookie\b/,/\bwindow\.opener\b/,/\bwindow\.top\b/,/\bparent\.postMessage\b(?!.*jaw-)/,/\blocation\.href\s*=/,/\bwindow\.location\b/,/\bsetTimeout\s*\(\s*["'`]/,/\bsetInterval\s*\(\s*["'`]/,/\.constructor\s*\.\s*constructor/,/\bdocument\.write\s*\(/,/\binsertAdjacentHTML\s*\(/,/\bimport\s*\(/],Me=[];function Ne(e){let t=[];if(e.length>524288)return{valid:!1,reason:`Payload too large (>512KB)`,warnings:t};let n=/(?:src|href)\s*=\s*["']https?:\/\/([^/"']+)/gi,r;for(;(r=n.exec(e))!==null;){let e=r[1];if(!Ae.some(t=>e===t||e.endsWith(`.`+t)))return{valid:!1,reason:`Blocked domain: ${e}`,warnings:t}}let i=/url\s*\(\s*['"]?https?:\/\/([^)'"]+)/gi;for(;(r=i.exec(e))!==null;){let e=r[1].split(`/`)[0];Ae.some(t=>e===t||e.endsWith(`.`+t))||t.push(`CSS url() references external domain: ${e}`)}for(let n of je)if(n.test(e))return{valid:!1,reason:`Dangerous pattern: ${n.source}`,warnings:t};for(let n of Me)n.test(e)&&t.push(`DOM sink detected: ${n.source}`);return{valid:!0,warnings:t}}function Pe(){let e=document.createElement(`button`);return e.className=`diagram-copy-btn`,e.type=`button`,e.ariaLabel=`Copy source`,e.title=`Copy`,e.innerHTML=d.copy,e}function Fe(){let e=document.createElement(`button`);return e.className=`diagram-save-btn`,e.type=`button`,e.ariaLabel=`Save as image`,e.title=`Save`,e.innerHTML=d.download,e}var Ie=[`cdnjs.cloudflare.com`,`cdn.jsdelivr.net`,`unpkg.com`,`esm.sh`,`fonts.googleapis.com`,`fonts.gstatic.com`],j=new Set,M=new Map,N=null;function Le(){if(N)return;let e=document.getElementById(`chatMessages`);e&&(N=new MutationObserver(e=>{if(j.size)for(let t of e)for(let e of t.removedNodes)e instanceof HTMLIFrameElement&&e.contentWindow&&(j.delete(e.contentWindow),M.delete(e.contentWindow)),e instanceof HTMLElement&&e.querySelectorAll(`iframe`).forEach(e=>{e.contentWindow&&(j.delete(e.contentWindow),M.delete(e.contentWindow))})}),N.observe(e,{childList:!0,subtree:!0}))}function Re(e){if(e.includes(`"importmap"`)||e.includes(`'importmap'`))return``;let t={},n=e.match(/(?:cdn\.jsdelivr\.net\/npm|unpkg\.com)\/three@([\d.]+)/);if(n){let r=n[1],i=e.includes(`unpkg.com/three@`)?`unpkg.com`:`cdn.jsdelivr.net/npm`;t.three=`https://${i}/three@${r}/${i===`unpkg.com`?`build/three.module.js`:`build/three.module.min.js`}`,t[`three/addons/`]=`https://${i}/three@${r}/examples/jsm/`}return Object.keys(t).length===0?``:`<script type="importmap">${JSON.stringify({imports:t})}<\/script>`}function ze(e){let t=`'none'`;(e.includes(`cdn.jsdelivr.net/npm/us-atlas`)||e.includes(`cdn.jsdelivr.net/npm/world-atlas`)||e.includes(`cdn.jsdelivr.net/npm/datamaps`))&&(t=`https://cdn.jsdelivr.net`);let n=/Tone\.min\.js|tone@/.test(e)?`worker-src blob:;`:``,r=Ie.map(e=>`https://${e}`),i=r.join(` `),a=[`data:`,`blob:`,...r],o=[`'unsafe-inline'`,`https://fonts.googleapis.com`];/L\.(map|tileLayer|marker|geoJSON|polyline|polygon|circle)\(|leaflet[\w.@/-]*\.(js|css)|tile\.openstreetmap\.org/.test(e)&&(a.push(`https://a.tile.openstreetmap.org`,`https://b.tile.openstreetmap.org`,`https://c.tile.openstreetmap.org`),o.push(`https://cdnjs.cloudflare.com`,`https://cdn.jsdelivr.net`));let s=a.join(` `);return`<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' ${i}; style-src ${o.join(` `)}; img-src ${s}; font-src https://fonts.gstatic.com; connect-src ${t}; ${n} base-uri 'none';">`}function Be(){let e=!document.documentElement.hasAttribute(`data-theme`)||document.documentElement.getAttribute(`data-theme`)===`dark`,t=getComputedStyle(document.documentElement);return{isDark:e,tokens:{"--bg":t.getPropertyValue(`--bg`).trim(),"--surface":t.getPropertyValue(`--surface`).trim(),"--border":t.getPropertyValue(`--border`).trim(),"--text":t.getPropertyValue(`--text`).trim(),"--text-dim":t.getPropertyValue(`--text-dim`).trim(),"--accent":t.getPropertyValue(`--accent`).trim(),"--font-ui":t.getPropertyValue(`--font-ui`).trim(),"--font-mono":t.getPropertyValue(`--font-mono`).trim(),"--radius-sm":t.getPropertyValue(`--radius-sm`).trim(),"--radius-md":t.getPropertyValue(`--radius-md`).trim()}}}function Ve(e){return`
|
|
3
|
+
<script>
|
|
4
|
+
(function() {
|
|
5
|
+
var __nonce = '${e}';
|
|
6
|
+
|
|
7
|
+
window.addEventListener('message', function(e) {
|
|
8
|
+
if (e.source !== window.parent) return;
|
|
9
|
+
if (!e.data || typeof e.data !== 'object') return;
|
|
10
|
+
|
|
11
|
+
if (e.data.type === 'jaw-theme-update') {
|
|
12
|
+
window.__jawTheme = { isDark: !!e.data.isDark };
|
|
13
|
+
window.__jawTokens = e.data.tokens || {};
|
|
14
|
+
window.dispatchEvent(new CustomEvent('jaw-theme-change', { detail: window.__jawTheme }));
|
|
15
|
+
}
|
|
16
|
+
if (e.data.type === 'jaw-request-resize') {
|
|
17
|
+
postHeight();
|
|
18
|
+
}
|
|
19
|
+
if (e.data.type === 'jaw-request-screenshot') {
|
|
20
|
+
var canvas = document.querySelector('canvas');
|
|
21
|
+
if (canvas) {
|
|
22
|
+
try {
|
|
23
|
+
var dataUrl = canvas.toDataURL('image/png');
|
|
24
|
+
window.parent.postMessage({ type: 'jaw-screenshot', dataUrl: dataUrl, nonce: __nonce }, '*');
|
|
25
|
+
} catch(ex) { /* tainted canvas or other error */ }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
function postHeight() {
|
|
31
|
+
var h = Math.max(
|
|
32
|
+
document.body.scrollHeight,
|
|
33
|
+
document.body.offsetHeight,
|
|
34
|
+
document.documentElement.scrollHeight
|
|
35
|
+
);
|
|
36
|
+
window.parent.postMessage({ type: 'jaw-diagram-resize', height: h, nonce: __nonce }, '*');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
40
|
+
var ro = new ResizeObserver(function() {
|
|
41
|
+
clearTimeout(ro._t);
|
|
42
|
+
ro._t = setTimeout(postHeight, 50);
|
|
43
|
+
});
|
|
44
|
+
ro.observe(document.body);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
window.addEventListener('load', function() {
|
|
48
|
+
postHeight();
|
|
49
|
+
// Deferred re-measure for async chart renders (Chart.js animation, CDN loading)
|
|
50
|
+
setTimeout(postHeight, 200);
|
|
51
|
+
setTimeout(postHeight, 800);
|
|
52
|
+
window.parent.postMessage({ type: 'jaw-widget-ready', nonce: __nonce }, '*');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
var lastSend = 0;
|
|
56
|
+
window.sendPrompt = function(text) {
|
|
57
|
+
var now = Date.now();
|
|
58
|
+
if (now - lastSend < 3000) return;
|
|
59
|
+
lastSend = now;
|
|
60
|
+
window.parent.postMessage({ type: 'jaw-send-prompt', text: String(text).slice(0, 500), nonce: __nonce }, '*');
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Ctrl+C / Cmd+C: forward selected text to host for clipboard access
|
|
64
|
+
document.addEventListener('copy', function() {
|
|
65
|
+
var sel = window.getSelection();
|
|
66
|
+
if (sel && sel.toString().trim()) {
|
|
67
|
+
window.parent.postMessage({
|
|
68
|
+
type: 'jaw-copy-text',
|
|
69
|
+
text: sel.toString().slice(0, 512),
|
|
70
|
+
nonce: __nonce
|
|
71
|
+
}, '*');
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
})();
|
|
75
|
+
<\/script>`}var He=[[/\/p5\.js\/1\.11\.1[1-9]\//g,`/p5.js/1.11.10/`]];function Ue(e){for(let[t,n]of He)e=e.replace(t,n);return e}function We(e){Le(),Ge(),e=Ue(e);let t=Array.from(crypto.getRandomValues(new Uint8Array(16)),e=>e.toString(16).padStart(2,`0`)).join(``),n=Be(),r=ze(e),i=Re(e),a=Ve(t),o=`<!DOCTYPE html>
|
|
76
|
+
<html>
|
|
77
|
+
<head>
|
|
78
|
+
<meta charset="utf-8">
|
|
79
|
+
${r}
|
|
80
|
+
${i}
|
|
81
|
+
<style>
|
|
82
|
+
:root { ${Object.entries(n.tokens).map(([e,t])=>`${e}: ${t};`).join(`
|
|
83
|
+
`)} }
|
|
84
|
+
* { margin: 0; box-sizing: border-box; }
|
|
85
|
+
body {
|
|
86
|
+
font-family: var(--font-ui), system-ui, sans-serif;
|
|
87
|
+
color: var(--text);
|
|
88
|
+
background: transparent;
|
|
89
|
+
padding: 16px;
|
|
90
|
+
overflow: hidden;
|
|
91
|
+
}
|
|
92
|
+
</style>
|
|
93
|
+
</head>
|
|
94
|
+
<body>
|
|
95
|
+
<script>
|
|
96
|
+
window.__jawTheme = ${JSON.stringify({isDark:n.isDark})};
|
|
97
|
+
window.__jawTokens = ${JSON.stringify(n.tokens).replace(/<\//g,`<\\/`)};
|
|
98
|
+
<\/script>
|
|
99
|
+
${a}
|
|
100
|
+
${e}
|
|
101
|
+
</body>
|
|
102
|
+
</html>`,s=document.createElement(`iframe`);return s.sandbox.add(`allow-scripts`),s.srcdoc=o,s.style.cssText=`width: 100%; border: none; overflow: hidden; display: block;`,s.setAttribute(`aria-label`,`Interactive diagram widget`),{iframe:s,nonce:t}}function P(e){(e||document).querySelectorAll(`.diagram-widget-pending`).forEach(e=>{let t=e.dataset.diagramHtml;if(!t)return;let n;try{if(t.length>524288)throw Error(`Widget payload too large`);n=decodeURIComponent(escape(atob(t)))}catch{e.replaceWith(Object.assign(document.createElement(`div`),{className:`diagram-error`,textContent:`Failed to decode widget content`,role:`alert`}));return}let r=Ne(n);if(!r.valid){e.replaceWith(Object.assign(document.createElement(`div`),{className:`diagram-error`,textContent:`Widget blocked: ${r.reason}`,role:`alert`}));return}r.warnings.length&&console.warn(`[jaw-diagram] Widget warnings:`,r.warnings);let i=document.createElement(`div`);i.className=`diagram-container diagram-widget`,i.dataset.widgetHtml=t,i.appendChild(Fe()),i.appendChild(Pe());let{iframe:a,nonce:o}=We(n);i.appendChild(a),e.replaceWith(i);let s=!1;a.addEventListener(`load`,()=>{s?(a.contentWindow&&(j.delete(a.contentWindow),M.delete(a.contentWindow)),console.warn(`[jaw-diagram] iframe navigated — postMessage channel revoked`)):(s=!0,a.contentWindow&&(j.add(a.contentWindow),M.set(a.contentWindow,o),a.contentWindow.postMessage({type:`jaw-request-resize`},`*`),setTimeout(()=>a.contentWindow?.postMessage({type:`jaw-request-resize`},`*`),300),setTimeout(()=>a.contentWindow?.postMessage({type:`jaw-request-resize`},`*`),1e3)))});let c=Number(i.dataset.gen||`0`);i.dataset.gen=String(c);let l=!1,u=e=>{e.source===a.contentWindow&&e.data?.type===`jaw-widget-ready`&&e.data.nonce===o&&(l=!0,window.removeEventListener(`message`,u))};window.addEventListener(`message`,u),setTimeout(()=>{if(window.removeEventListener(`message`,u),Number(i.dataset.gen||`0`)===c&&!l&&i.isConnected){let e=a.contentWindow;e&&(j.delete(e),M.delete(e)),i.innerHTML=`<div class="diagram-error" role="alert">
|
|
103
|
+
Widget failed to load within 10 seconds.
|
|
104
|
+
</div>`,console.warn(`[jaw-diagram] Widget timeout — iframe deregistered`)}},1e4)})}var F=null;function Ge(){if(F)return;let e=document.getElementById(`chatMessages`);e&&(F=new MutationObserver(t=>{for(let n of t)for(let t of n.addedNodes)if(t instanceof HTMLElement&&(t.classList?.contains(`diagram-widget-pending`)||t.querySelector?.(`.diagram-widget-pending`))){requestAnimationFrame(()=>P(t.parentElement||e));return}}),F.observe(e,{childList:!0}))}var I=new WeakMap;function Ke(e,t){I.has(e)||(I.set(e,window.setTimeout(()=>I.delete(e),100)),document.querySelectorAll(`iframe`).forEach(n=>{n.contentWindow===e&&(n.style.height=`${Math.min(Math.max(t,60),2e3)}px`)}))}function qe(){document.querySelectorAll(`.diagram-widget`).forEach(e=>{let t=e.dataset.widgetHtml;if(!t)return;let n;try{n=decodeURIComponent(escape(atob(t)))}catch{return}let r=e.querySelector(`iframe`);r?.contentWindow&&(j.delete(r.contentWindow),M.delete(r.contentWindow));let i=e;i.dataset.gen=String((Number(i.dataset.gen||`0`)||0)+1);let{iframe:a,nonce:o}=We(n);e.innerHTML=``,e.appendChild(Fe()),e.appendChild(Pe()),e.appendChild(a);let s=!1;a.addEventListener(`load`,()=>{s?a.contentWindow&&(j.delete(a.contentWindow),M.delete(a.contentWindow)):(s=!0,a.contentWindow&&(j.add(a.contentWindow),M.set(a.contentWindow,o),a.contentWindow.postMessage({type:`jaw-request-resize`},`*`),setTimeout(()=>a.contentWindow?.postMessage({type:`jaw-request-resize`},`*`),300),setTimeout(()=>a.contentWindow?.postMessage({type:`jaw-request-resize`},`*`),1e3)))})})}var Je=0;window.addEventListener(`message`,e=>{if(!e.data||typeof e.data!=`object`||!e.source||e.origin!==`null`||!j.has(e.source)||![...document.querySelectorAll(`iframe`)].find(t=>t.contentWindow===e.source)?.isConnected)return;let t=M.get(e.source);if(!(!t||e.data.nonce!==t))switch(e.data.type){case`jaw-diagram-resize`:{let t=Number(e.data.height);if(!Number.isFinite(t)||t<0)return;Ke(e.source,t);break}case`jaw-send-prompt`:{let t=Date.now();if(t-Je<3e3)return;Je=t;let n=String(e.data.text||``).trim().slice(0,500);if(!n)return;let r=document.getElementById(`chatInput`);r&&(r.value=n,r.dispatchEvent(new Event(`input`,{bubbles:!0})),r.focus());break}case`jaw-copy-text`:{let t=String(e.data.text||``).trim().slice(0,512);if(!t)return;navigator.clipboard.writeText(t).catch(()=>{});break}case`jaw-screenshot`:{let t=String(e.data.dataUrl||``);if(!t.startsWith(`data:image/`)||t.length>5242880)return;fetch(t).then(e=>e.blob()).then(e=>{let t=URL.createObjectURL(e),n=document.createElement(`a`);n.href=t,n.download=`widget-${Date.now()}.png`,n.click(),setTimeout(()=>URL.revokeObjectURL(t),1e3)}).catch(()=>{});break}case`jaw-widget-ready`:break}});function Ye(e){e.dataset.toolItemBound!==`1`&&(e.addEventListener(`click`,e=>{let t=e.target;if(!t)return;let n=t.closest(`.tool-item-toggle`);if(!n)return;let r=n.closest(`.tool-item`),i=r?.querySelector(`.tool-item-details`),a=n.querySelector(`.tool-item-chevron`);if(!r||!i)return;let o=i.classList.contains(`collapsed`);i.classList.toggle(`collapsed`,!o),r.classList.toggle(`expanded`,o),n.setAttribute(`aria-expanded`,o?`true`:`false`),a&&(a.innerHTML=o?d.chevronDown:d.chevronRight)}),e.dataset.toolItemBound=`1`)}function Xe(){document.querySelectorAll(`.tool-activity-live`).forEach(e=>e.remove()),document.querySelectorAll(`.msg-system.tool-activity`).forEach(e=>e.remove())}function Ze(e){let t={};for(let n of e){let e=n.type===`thinking`?`${d.thinking} Thinking`:n.type===`search`?`${d.search} Search`:`${d.tool} Tool`;t[e]=(t[e]||0)+1}return Object.entries(t).map(([e,t])=>t>1?`${e}×${t}`:e).join(` + `)}function Qe(e,t=120){let n=e.replace(/\s+/g,` `).trim();return n?n.length>t?`${n.slice(0,t-1)}…`:n:``}function $e(e){let t=(e.detail||``).trim();return t?t!==(e.label||``).trim():!1}function L(e){let t=`process-step-dot ${e.status}`,n=`process-step-badge ${e.type}`,r=e.type.toUpperCase(),i=c(e.label||e.icon||``),a=e.detail||``,o=`process-detail-${e.id}`,s=$e(e)?Qe(a,e.type===`thinking`?120:100):``,l=s?`<span class="process-step-snippet">${c(s)}</span>`:``;return $e(e)?`<div class="process-step process-step-expandable" data-step-id="${e.id}" data-type="${e.type}">
|
|
105
|
+
<button class="process-step-toggle" aria-expanded="false" aria-controls="${o}">
|
|
106
|
+
<span class="${t}"></span>
|
|
107
|
+
<span class="${n}">${r}</span>
|
|
108
|
+
<span class="process-step-main">
|
|
109
|
+
<span class="process-step-label">${i}</span>
|
|
110
|
+
${l}
|
|
111
|
+
</span>
|
|
112
|
+
<span class="process-step-chevron">${d.chevronRight}</span>
|
|
113
|
+
</button>
|
|
114
|
+
<div class="process-step-details collapsed" id="${o}">
|
|
115
|
+
<pre class="process-step-full">${c(a)}</pre>
|
|
116
|
+
</div>
|
|
117
|
+
</div>`:`<div class="process-step" data-step-id="${e.id}" data-type="${e.type}">
|
|
118
|
+
<span class="${t}"></span>
|
|
119
|
+
<span class="${n}">${r}</span>
|
|
120
|
+
<span class="process-step-label">${i}</span>
|
|
121
|
+
</div>`}function et(e=``,t=!1){return`<div class="process-block${t?` collapsed`:``}">
|
|
122
|
+
<button class="process-summary" aria-expanded="${t?`false`:`true`}">
|
|
123
|
+
<span class="process-dot ${t?`done`:`running`}"></span>
|
|
124
|
+
<span class="process-summary-text">${e}</span>
|
|
125
|
+
<span class="process-duration"></span>
|
|
126
|
+
<span class="process-chevron">${t?d.chevronRight:d.chevronDown}</span>
|
|
127
|
+
</button>
|
|
128
|
+
<div class="process-details">
|
|
129
|
+
<div class="process-steps-inner"></div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>`}function tt(e){let t=e.closest(`.process-step`),n=t?.querySelector(`.process-step-details`),r=e.querySelector(`.process-step-chevron`);if(!t||!n)return;let i=n.classList.contains(`collapsed`);n.classList.toggle(`collapsed`,!i),t.classList.toggle(`expanded`,i),e.setAttribute(`aria-expanded`,i?`true`:`false`),r&&(r.innerHTML=i?d.chevronDown:d.chevronRight)}function nt(e){e.dataset.processBlockBound!==`1`&&(e.addEventListener(`click`,e=>{let t=e.target;if(!t)return;let n=t.closest(`.process-step-toggle`);if(n){tt(n);return}let r=t.closest(`.process-summary`);if(r){let e=r.closest(`.process-block`);if(!e)return;let t=e.classList.contains(`collapsed`);e.classList.toggle(`collapsed`,!t),r.setAttribute(`aria-expanded`,t?`true`:`false`);let n=r.querySelector(`.process-chevron`);n&&(n.innerHTML=t?d.chevronDown:d.chevronRight)}}),e.dataset.processBlockBound=`1`)}function R(e,t=!0){let n=et(Ze(e),t),r=document.createElement(`div`);r.innerHTML=n;let i=r.querySelector(`.process-steps-inner`);i&&(i.innerHTML=e.map(L).join(``));let a=r.querySelector(`.process-dot`);if(a){let n=e.some(e=>e.status===`running`);a.classList.toggle(`running`,n&&!t),a.classList.toggle(`done`,!n||t)}return r.innerHTML}function z(e){let t=e.element.querySelector(`.process-summary-text`);t&&(t.innerHTML=Ze(e.steps));let n=e.steps.some(e=>e.status===`running`),r=e.element.querySelector(`.process-dot`);r&&(r.classList.toggle(`running`,n&&!e.collapsed),r.classList.toggle(`done`,!n||e.collapsed));let i=e.steps.length>0?Math.round((Date.now()-e.steps[0].startTime)/1e3):0,a=e.element.querySelector(`.process-duration`);a&&(a.textContent=i>0?`${i}s`:``)}function B(e){let t=document.createElement(`div`);t.innerHTML=et(``,!1);let n=t.firstElementChild,r=e.querySelector(`.msg-content`);return r?r.before(n):e.appendChild(n),{element:n,steps:[],collapsed:!1}}function V(e,t){e.steps.push(t);let n=e.element.querySelector(`.process-steps-inner`);n&&n.insertAdjacentHTML(`beforeend`,L(t)),z(e)}function H(e,t,n){let r=e.steps.findIndex(e=>e.id===t);if(r===-1)return;e.steps[r]=n;let i=e.element.querySelector(`[data-step-id="${t}"]`);if(i){let e=document.createElement(`div`);e.innerHTML=L(n);let t=e.firstElementChild;t&&i.replaceWith(t)}z(e)}function rt(e,t,n){let r=e.steps.find(e=>e.id===t);if(!r)return;r.status=n;let i=e.element.querySelector(`[data-step-id="${t}"]`);if(i){let e=i.querySelector(`.process-step-dot`);e&&(e.classList.remove(`running`,`done`,`error`),e.classList.add(n))}z(e)}function U(e){e.collapsed=!0,e.element.classList.add(`collapsed`);let t=e.element.querySelector(`.process-summary`);t&&t.setAttribute(`aria-expanded`,`false`);let n=e.element.querySelector(`.process-chevron`);n&&(n.innerHTML=d.chevronRight);for(let t of e.steps)t.status===`running`&&(t.status=`done`);e.element.querySelectorAll(`.process-step-dot.running`).forEach(e=>{e.classList.remove(`running`),e.classList.add(`done`)}),z(e)}function W(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function G(e){return ve()}function K(e){return e.map(e=>({id:h(),icon:e.icon?i(e.icon):d.tool,label:e.label||e.name||`tool`,type:e.toolType||`tool`,detail:e.detail||``,stepRef:e.stepRef||``,status:e.status||`done`,startTime:Date.now()}))}function it(t){let n=document.getElementById(`statusBadge`),r=document.getElementById(`btnSend`);e.agentBusy=t===`running`,document.getElementById(`typingIndicator`)?.classList.toggle(`active`,e.agentBusy),t===`running`?(n&&(n.className=`status-badge status-running`,n.textContent=`running`),r&&(r.innerHTML=d.stop,r.title=a(`btn.stop`),r.classList.add(`stop-mode`)),ot()):(n&&(n.className=`status-badge status-idle`,n.textContent=`idle`),r&&(r.innerHTML=d.send,r.title=`Send`,r.classList.remove(`stop-mode`)),q(),at(0))}function at(e){let t=document.getElementById(`queueBadge`);if(!t){t=document.createElement(`span`),t.id=`queueBadge`,t.className=`queue-badge`;let e=document.getElementById(`btnSend`);e?.parentElement&&(e.parentElement.style.position=`relative`),e&&(e.style.position=`relative`,e.appendChild(t))}t.textContent=e>0?String(e):``,t.style.display=e>0?`flex`:`none`}function ot(){let e=document.getElementById(`chatMessages`);if(!e||e.querySelector(`.skeleton-msg`))return;J();let t=document.createElement(`div`);t.className=`skeleton-msg`,t.innerHTML=`<div class="skeleton-line"></div><div class="skeleton-line"></div><div class="skeleton-line"></div>`,e.appendChild(t),Q()}function q(){document.querySelectorAll(`.skeleton-msg`).forEach(e=>e.remove())}function J(){document.getElementById(`emptyState`)?.classList.remove(`visible`)}function Y(){let e=document.getElementById(`chatMessages`);e&&e.children.length===0&&document.getElementById(`emptyState`)?.classList.add(`visible`)}function st(e,t,n){let r=document.getElementById(`chatMessages`);if(!r)return;let i=k();J();let a=document.createElement(`div`);a.className=`msg msg-system`+(n?` msg-type-${n}`:``)+(t?` `+t:``),a.innerHTML=e,i.active?i.appendLiveItem(a):r.appendChild(a),Q()}function ct(){Xe(),e.currentAgentDiv=null,e.currentProcessBlock=null}function lt(t){if(q(),(!e.currentAgentDiv||!e.currentAgentDiv.isConnected)&&(e.currentAgentDiv=Z(`agent`,``),e.currentProcessBlock=null),!e.currentProcessBlock){let t=e.currentAgentDiv.querySelector(`.agent-body`);t&&(e.currentProcessBlock=B(t))}if(e.currentProcessBlock){let n=t.status&&t.status!==`running`?t.status:s(t.icon);if(n===`done`||n===`error`){let r=t.stepRef,a=r?[...e.currentProcessBlock.steps].reverse().find(e=>e.status===`running`&&e.stepRef===r):[...e.currentProcessBlock.steps].reverse().find(e=>e.status===`running`&&e.label===t.label);if(a){t.detail&&!a.detail?(t.icon=i(t.icon),H(e.currentProcessBlock,a.id,{...t,id:a.id})):rt(e.currentProcessBlock,a.id,n),Q();return}let o=[...e.currentProcessBlock.steps].reverse().find(e=>e.status===`running`);if(o){t.detail&&!o.detail?(t.icon=i(t.icon),H(e.currentProcessBlock,o.id,{...t,id:o.id})):rt(e.currentProcessBlock,o.id,n),Q();return}}if(t.detail){let n=[...e.currentProcessBlock.steps].reverse().find(e=>e.status===`running`&&e.label===t.label&&e.type===t.type&&!e.detail);if(n){H(e.currentProcessBlock,n.id,t),Q();return}}t.icon=i(t.icon),V(e.currentProcessBlock,t)}Q()}var X=null;function ut(t){if(!t)return;q(),(!e.currentAgentDiv||!e.currentAgentDiv.isConnected)&&(e.currentAgentDiv=Z(`agent`,``),X=null);let n=e.currentAgentDiv?.querySelector(`.msg-content`);n&&(X||=De(n),Oe(X,t)),Q()}var dt=0;function ft(t,n){let r=Date.now();if(!e.currentAgentDiv&&r-dt<500)return;Xe(),q();let i=!!e.currentProcessBlock;e.currentProcessBlock&&=(U(e.currentProcessBlock),null);let a=n&&n.length>0;if(t||a){(!e.currentAgentDiv||!e.currentAgentDiv.isConnected)&&(e.currentAgentDiv=Z(`agent`,``));let r=e.currentAgentDiv?.querySelector(`.msg-content`),s=X?ke(X,!0):``,c=t||s;X=null;let u=a&&!i?R(K(n),!0):``;r&&(r.innerHTML=u+o(c)),r&&r.setAttribute(`data-raw`,l(c)),r&&P(r);let d=k();d.active&&e.currentAgentDiv&&e.currentAgentDiv.isConnected&&(d.appendLiveItem(e.currentAgentDiv),e.currentAgentDiv.remove()),c&&f({role:`assistant`,content:c,tool_log:n?JSON.stringify(n):null,timestamp:Date.now()}).catch(()=>{})}X=null,e.currentAgentDiv=null,dt=Date.now(),it(`idle`),gt()}function Z(e,t,n){let r=document.getElementById(`chatMessages`),i=k();J(),q();let s=o(t),d=c(e===`user`?a(`msg.you`):v()),f=document.createElement(`div`);e===`agent`?(f.className=`msg msg-agent`,f.innerHTML=`<div class="agent-icon" aria-hidden="true">${G(n)}</div><div class="agent-body"><div class="msg-content">${s}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div>`):(f.className=`msg msg-${e}`,f.innerHTML=`<div class="user-body"><div class="msg-label">${d}</div><div class="msg-content">${s}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div><div class="user-icon" aria-hidden="true">${D()}</div>`);let p=f.querySelector(`.msg-content`);p&&p.setAttribute(`data-raw`,l(t));let m=e===`agent`&&!t;return i.active&&!m?i.appendLiveItem(f):(r?.appendChild(f),P(f),!i.active&&!m&&r&&r.querySelectorAll(`.msg`).length>=80&&(r.querySelectorAll(`.msg`).forEach(e=>{i.addItem(h(),e.outerHTML)}),i.onPostRender=e=>{P(e),u(e)})),Q(),f}var pt=null;function Q(){let e=k();if(e.active){e.scrollToBottom();return}pt||=requestAnimationFrame(()=>{pt=null;let e=document.getElementById(`chatMessages`);e&&(e.scrollTop=e.scrollHeight)})}function mt(e,t){document.querySelectorAll(`.tab-btn`).forEach(e=>{e.classList.remove(`active`),e.setAttribute(`aria-selected`,`false`)}),document.querySelectorAll(`.tab-content`).forEach(e=>e.classList.remove(`active`)),document.getElementById({agents:`tabAgents`,settings:`tabSettings`,skills:`tabSkills`}[e])?.classList.add(`active`),t&&(t.classList.add(`active`),t.setAttribute(`aria-selected`,`true`)),e===`settings`&&r(()=>import(`./settings-DUWhygHi.js`).then(e=>e.loadSettings()),__vite__mapDeps([0])),e===`agents`&&r(()=>import(`./employees-C2G0-Rg9.js`).then(e=>e.loadEmployees()),__vite__mapDeps([0])),e===`skills`&&r(()=>import(`./skills-C6aTdbWY.js`).then(e=>e.loadSkills()),__vite__mapDeps([0]))}function ht(){document.getElementById(`tabSettings`)?.classList.contains(`active`)?r(()=>import(`./settings-DUWhygHi.js`).then(e=>e.savePerCli()),__vite__mapDeps([0])):r(()=>import(`./settings-DUWhygHi.js`).then(e=>e.updateSettings()),__vite__mapDeps([0]))}function $(e){let t=document.getElementById(`statMsgs`);t&&(t.textContent=a(`stat.messages`,{count:e}))}async function gt(){let e=await n(`/api/messages`);e&&$(e.length)}async function _t(){let e=k(),t=document.getElementById(`chatMessages`);try{let e=await n(`/api/settings`);e?.workingDir&&p(e.workingDir)}catch{}let r=await n(`/api/messages`);if(r!==null){if(e.clear(),t&&(t.innerHTML=``),r.length>=80){for(let t of r){let n=t.role===`assistant`?`agent`:t.role,r=l(t.content),i=c(n===`user`?a(`msg.you`):v()),o=t.role===`assistant`?W(t.tool_log):[],s=o.length>0?R(K(o),!0):``,u=`<div class="skeleton-line"></div><div class="skeleton-line"></div>`,d=n===`agent`?`<div class="msg msg-agent"><div class="agent-icon" aria-hidden="true">${G(t.cli)}</div><div class="agent-body">${s}<div class="msg-content lazy-pending" data-raw="${c(r)}">${u}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div></div>`:`<div class="msg msg-${n}"><div class="user-body"><div class="msg-label">${i}</div><div class="msg-content lazy-pending" data-raw="${c(r)}">${u}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div><div class="user-icon" aria-hidden="true">${D()}</div></div>`;e.addItem(h(),d)}e.onLazyRender=t=>{for(let n of t){if(!n.classList.contains(`lazy-pending`))continue;let t=n.getAttribute(`data-raw`)||``;n.innerHTML=t?o(t):``,n.classList.remove(`lazy-pending`),P(n);let r=n.closest(`[data-vs-idx]`);if(r){let t=Number(r.dataset.vsIdx);e.updateItemHtml(t,r.outerHTML)}}},e.onPostRender=e=>{P(e),u(e)},e.scrollToBottom()}else r.forEach(e=>{let t=Z(e.role===`assistant`?`agent`:e.role,e.content,e.cli);if(e.role===`assistant`){let n=W(e.tool_log);if(n.length>0){let e=t.querySelector(`.agent-body`);if(e){let t=B(e);for(let e of K(n))V(t,e);U(t)}}}});ee(r.map(e=>({role:e.role,content:e.content,cli:e.cli??null,tool_log:e.tool_log??null,timestamp:Date.now()}))).catch(()=>{}),$(r.length),Y();return}if(t&&t.children.length>0){Y();return}let i=await m();if(i.length>0){if(i.length>=80){for(let t of i){let n=t.role===`assistant`?`agent`:t.role,r=l(t.content),i=c(n===`user`?a(`msg.you`):v()),o=t.role===`assistant`&&t.tool_log?W(t.tool_log):[],s=o.length>0?R(K(o),!0):``,u=`<div class="skeleton-line"></div><div class="skeleton-line"></div>`,d=n===`agent`?`<div class="msg msg-agent"><div class="agent-icon" aria-hidden="true">${G(t.cli)}</div><div class="agent-body">${s}<div class="msg-content lazy-pending" data-raw="${c(r)}">${u}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div></div>`:`<div class="msg msg-${n}"><div class="user-body"><div class="msg-label">${i}</div><div class="msg-content lazy-pending" data-raw="${c(r)}">${u}</div><button class="msg-copy" title="Copy" aria-label="Copy message"></button></div><div class="user-icon" aria-hidden="true">${D()}</div></div>`;e.addItem(h(),d)}e.onLazyRender=t=>{for(let n of t){if(!n.classList.contains(`lazy-pending`))continue;let t=n.getAttribute(`data-raw`)||``;n.innerHTML=t?o(t):``,n.classList.remove(`lazy-pending`),P(n);let r=n.closest(`[data-vs-idx]`);if(r){let t=Number(r.dataset.vsIdx);e.updateItemHtml(t,r.outerHTML)}}},e.onPostRender=e=>{P(e),u(e)},e.scrollToBottom()}else i.forEach(e=>{let t=Z(e.role===`assistant`?`agent`:e.role,e.content,e.cli);if(e.role===`assistant`&&e.tool_log){let n=W(e.tool_log);if(n.length>0){let e=t.querySelector(`.agent-body`);if(e){let t=B(e);for(let e of K(n))V(t,e);U(t)}}}});st(`${d.warning} 오프라인 모드 — 캐시된 메시지 표시 중`),$(i.length)}Y()}function vt(){let e=document.getElementById(`chatMessages`);e&&(nt(e),Ye(e),e.addEventListener(`click`,e=>{let t=e.target,n=t.closest(`.tool-group-summary`);if(n){let e=n.closest(`.tool-group`),t=n.nextElementSibling;if(e&&t){let r=!e.classList.contains(`expanded`);e.classList.toggle(`expanded`),t.classList.toggle(`collapsed`),n.setAttribute(`aria-expanded`,r?`true`:`false`)}return}let r=t.closest(`.msg-copy`);if(!r)return;let i=r.closest(`.msg`)?.querySelector(`.msg-content`);if(!i)return;let a=i.getAttribute(`data-raw`)||i.innerText||i.textContent||``;navigator.clipboard.writeText(a).then(()=>{r.classList.add(`copied`),r.innerHTML=d.checkSimple,setTimeout(()=>{r.classList.remove(`copied`),r.textContent=``},600)}).catch(()=>{})}))}export{xe as _,ft as a,_t as c,it as d,lt as f,k as g,qe as h,ct as i,gt as l,at as m,st as n,ht as o,mt as p,ut as r,vt as s,Z as t,Q as u,re as v};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/vendor-render-Bjnw0wQ6.css"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{t as e}from"./state-O6NVkWcL.js";import{Z as t}from"./vendor-mermaid-C2RBgdM6.js";import{f as n,h as r,n as i,s as a,t as o}from"./render-
|
|
2
|
+
import{t as e}from"./state-O6NVkWcL.js";import{Z as t}from"./vendor-mermaid-C2RBgdM6.js";import{f as n,h as r,n as i,s as a,t as o}from"./render-BFAkzW1S.js";import{a as s,d as c,f as l,g as u,i as d,m as f,n as p,r as m,t as h}from"./ui-Dx3w-H-4.js";var g=[`P`,`A`,`B`,`C`],_=null;function v(e,t,n){let r=g.indexOf(n);if(r<0)return;let i=e.getBoundingClientRect(),a=t.offsetWidth||36,o=document.getElementById(`dot-${n}`);if(!o)return;let s=g[r+1],c=s?document.getElementById(`dot-${s}`):null;if(c){let e=o.getBoundingClientRect(),n=c.getBoundingClientRect(),r=(e.right+n.left)/2;t.style.left=r-i.left-a/2+`px`}else{let e=o.getBoundingClientRect();t.style.left=e.left-i.left+e.width/2-a/2+`px`}}var y={},b=``,x=0;function S(e){for(let e of Object.keys(y))delete y[e];for(let t of e)t.state===`running`&&t.phase&&(y[t.agentId]={phase:t.phase,phaseLabel:t.phaseLabel||``})}function C(t,n){let r=new Set([`IDLE`,`P`,`A`,`B`,`C`,`D`]).has(t)?t:`IDLE`;if(e.orcState=r,r===`IDLE`||r===`D`)document.body.removeAttribute(`data-orc-state`),document.body.style.removeProperty(`--orc-glow`);else{document.body.setAttribute(`data-orc-state`,r);let e=`--orc-glow-${r}`,t=getComputedStyle(document.documentElement).getPropertyValue(e).trim();document.body.style.setProperty(`--orc-glow`,t)}document.body.classList.add(`orc-pulse`),setTimeout(()=>document.body.classList.remove(`orc-pulse`),700);let i=document.getElementById(`orcStateBadge`);i&&(i.textContent={IDLE:``,P:`PLAN`,A:`AUDIT`,B:`BUILD`,C:`CHECK`,D:`DONE`}[r],i.style.display=r===`IDLE`?`none`:`inline-block`);let a=document.getElementById(`pabcRoadmap`),o=document.getElementById(`sharkRunner`),s=document.getElementById(`pabcBrand`);if(a&&o){if(!a.dataset.resizeObserved){a.dataset.resizeObserved=`1`,new ResizeObserver(()=>{_&&o.classList.contains(`running`)&&v(a,o,_)}).observe(a);let e=0;window.addEventListener(`resize`,()=>{cancelAnimationFrame(e),e=requestAnimationFrame(()=>{_&&o.classList.contains(`running`)&&v(a,o,_)})})}if(r===`IDLE`)a.classList.remove(`visible`,`shimmer-out`),o.classList.remove(`running`),_=null;else if(r===`D`){g.forEach(e=>{let t=document.getElementById(`dot-${e}`);t&&(t.className=`pabc-dot done`,t.setAttribute(`data-phase`,e))});for(let e=0;e<4;e++){let t=document.getElementById(`pabc-conn-${e}`);t&&(t.className=`pabc-connector done`)}o.classList.remove(`running`),_=null,a.classList.add(`shimmer-out`),setTimeout(()=>a.classList.remove(`visible`,`shimmer-out`),1e3)}else{a.classList.remove(`shimmer-out`),a.classList.add(`visible`),o.classList.add(`running`);let e=g.indexOf(r);g.forEach((t,n)=>{let r=document.getElementById(`dot-${t}`);r&&(r.className=`pabc-dot ${n<e?`done`:n===e?`active`:`future`}`,r.setAttribute(`data-phase`,t))});for(let t=0;t<4;t++){let n=document.getElementById(`pabc-conn-${t}`);n&&(n.className=`pabc-connector ${t<e?`done`:``}`)}_=r,requestAnimationFrame(()=>v(a,o,r))}s&&n&&(s.textContent=n)}}function w(){let g=`ws://${location.host}`;e.ws=new WebSocket(`${g}?lang=${n()}`),e.ws.onmessage=e=>{let n;try{n=JSON.parse(e.data)}catch{console.warn(`[ws] malformed message:`,e.data);return}if(!n||typeof n!=`object`||typeof n.type!=`string`){console.warn(`[ws] invalid message shape:`,n);return}if(n.type===`agent_status`)n.running===void 0?c(n.status||`idle`):c(n.running?`running`:`idle`),n.agentId&&n.phase&&(y[n.agentId]={phase:n.phase,phaseLabel:n.phaseLabel||``},t(()=>import(`./employees-C2G0-Rg9.js`).then(e=>e.loadEmployees()),__vite__mapDeps([0])));else if(n.type===`queue_update`)f(n.pending||0);else if(n.type===`worklog_created`)p(`${a.clipboard} Worklog: ${i(n.path||``)}`);else if(n.type===`round_start`){let e=n.agentPhases||n.subtasks||[],t=e.map(e=>i(e.agent||e.name||``)).join(`, `);p(r(`ws.roundStart`,{round:n.round||0,count:e.length,names:t}))}else if(n.type===`round_done`)n.action===`complete`?p(r(`ws.roundDone`,{round:n.round||0})):n.action===`next`?p(r(`ws.roundNext`,{round:n.round||0})):p(r(`ws.roundRetry`,{round:n.round||0}));else if(n.type===`agent_tool`){let e=n.toolType===`thinking`?`thinking`:n.toolType===`search`?`search`:`tool`;l({id:`step-${Date.now()}-${Math.random().toString(36).slice(2,6)}`,type:e,icon:n.icon||a.tool,label:n.label||``,detail:n.detail||``,stepRef:n.stepRef||``,status:n.status||`running`,startTime:Date.now()})}else if(n.type===`agent_output`)m(n.text||``);else if(n.type===`agent_retry`)p(r(`ws.retry`,{cli:i(n.cli||``),delay:n.delay||10}),`tool-activity`);else if(n.type===`agent_fallback`)p(r(`ws.fallback`,{from:i(n.from||``),to:i(n.to||``)}),`tool-activity`);else if(n.type===`agent_smoke`)p(`${a.warning} ${i(n.cli||`agent`)}: smoke response detected — auto-continuing`,`tool-activity`);else if(n.type===`agent_done`)s(n.text||``,n.toolLog);else if(n.type===`orchestrate_done`)s(n.text||``);else if(n.type===`clear`){o(),d(),u().clear();let e=document.getElementById(`chatMessages`);e&&(e.innerHTML=``),t(()=>import(`./idb-cache-C7z4qE00.js`).then(e=>e.clearCache()),[]).catch(()=>{})}else if(n.type===`session_reset`)p(`${a.refresh} Session reset — history preserved`,`tool-activity`);else if(n.type===`agent_added`||n.type===`agent_updated`||n.type===`agent_deleted`)t(()=>import(`./employees-C2G0-Rg9.js`).then(e=>e.loadEmployees()),__vite__mapDeps([0]));else if(n.type===`orc_state`){if(n.scope&&b&&n.scope!==b)return;C(typeof n.state==`string`?n.state:`IDLE`,n.title)}else n.type===`new_message`&&(n.source===`telegram`||n.source===`discord`)&&h(n.role===`assistant`?`agent`:n.role||`user`,n.content||``,n.cli)},e.ws.onopen=()=>{console.log(`[ws] connected`);let e=Date.now()-x<1e4;t(()=>import(`./ui-BpZlLDtM.js`).then(async t=>{if(t.cleanupToolActivity(),!e)try{await t.loadMessages(),x=Date.now()}catch(e){console.error(`[ws] loadMessages failed`,e)}t.setStatus(`idle`)}),__vite__mapDeps([0])),fetch(`/api/orchestrate/snapshot`).then(e=>e.json()).then(e=>{b=String(e.orc.scope||``),C(e.orc.state),S(e.workers),f(e.runtime.queuePending),c(e.runtime.busy?`running`:`idle`),t(()=>import(`./employees-C2G0-Rg9.js`).then(e=>{typeof e.renderEmployees==`function`&&e.renderEmployees()}),__vite__mapDeps([0]))}).catch(()=>{})},e.ws.onclose=()=>{console.log(`[ws] disconnected, reconnecting in 2s...`),t(()=>import(`./ui-BpZlLDtM.js`).then(e=>e.cleanupToolActivity()),__vite__mapDeps([0])),c(`idle`),p(`${a.exec} 연결 끊김 — 재연결 중...`,`tool-activity`),setTimeout(w,2e3)}}function T(e){return y[e]||null}export{T as n,w as t};
|
package/public/dist/index.html
CHANGED
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@400;500;600;700&family=Outfit:wght@400;500;600;700&display=swap"
|
|
26
26
|
rel="stylesheet">
|
|
27
27
|
<!-- Vite handles module bundling in dev (HMR) and production (build) -->
|
|
28
|
-
<script type="module" crossorigin src="/dist/assets/index-
|
|
28
|
+
<script type="module" crossorigin src="/dist/assets/index-CIWCSFl-.js"></script>
|
|
29
29
|
<link rel="stylesheet" crossorigin href="/dist/assets/vendor-render-Bjnw0wQ6.css">
|
|
30
|
-
<link rel="stylesheet" crossorigin href="/dist/assets/index-
|
|
30
|
+
<link rel="stylesheet" crossorigin href="/dist/assets/index-CDdXQQmm.css">
|
|
31
31
|
</head>
|
|
32
32
|
|
|
33
33
|
<body>
|
|
@@ -89,16 +89,26 @@
|
|
|
89
89
|
|
|
90
90
|
<div class="section-title" style="margin-top:12px" data-i18n="sidebar.avatar">아바타</div>
|
|
91
91
|
<div class="flex gap-2 items-center">
|
|
92
|
-
<div class="flex gap-1 items-center flex-1">
|
|
92
|
+
<div class="flex gap-1 items-center flex-1 avatar-control">
|
|
93
93
|
<label class="avatar-label">🤖</label>
|
|
94
94
|
<input type="text" id="agentAvatarInput" value="🦈" class="input-avatar" maxlength="2" placeholder="🦈">
|
|
95
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto avatar-upload-btn" id="agentAvatarUploadBtn" title="Upload agent avatar image">Img</button>
|
|
96
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto avatar-reset-btn" id="agentAvatarResetBtn" title="Reset agent avatar image">↺</button>
|
|
97
|
+
<input type="file" id="agentAvatarFile" hidden accept="image/png,image/jpeg,image/webp,image/gif">
|
|
95
98
|
</div>
|
|
96
|
-
<div class="flex gap-1 items-center flex-1">
|
|
99
|
+
<div class="flex gap-1 items-center flex-1 avatar-control">
|
|
97
100
|
<label class="avatar-label">👤</label>
|
|
98
101
|
<input type="text" id="userAvatarInput" value="👤" class="input-avatar" maxlength="2" placeholder="👤">
|
|
102
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto avatar-upload-btn" id="userAvatarUploadBtn" title="Upload user avatar image">Img</button>
|
|
103
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto avatar-reset-btn" id="userAvatarResetBtn" title="Reset user avatar image">↺</button>
|
|
104
|
+
<input type="file" id="userAvatarFile" hidden accept="image/png,image/jpeg,image/webp,image/gif">
|
|
99
105
|
</div>
|
|
100
106
|
<button class="sidebar-hb-btn btn-action-sm w-auto" id="avatarSave">✓</button>
|
|
101
107
|
</div>
|
|
108
|
+
<div class="avatar-status-row">
|
|
109
|
+
<span class="avatar-status" id="agentAvatarStatus">emoji active</span>
|
|
110
|
+
<span class="avatar-status" id="userAvatarStatus">emoji active</span>
|
|
111
|
+
</div>
|
|
102
112
|
<button class="sidebar-hb-btn" id="btnClearChat">/clear</button>
|
|
103
113
|
<button class="sidebar-hb-btn" id="hbSidebarBtn"><span data-icon="heartPulse"></span> Heartbeat (0)</button>
|
|
104
114
|
<button class="sidebar-hb-btn" data-action="openTemplates"><span data-icon="plan"></span> 프롬프트 템플릿</button>
|
package/public/index.html
CHANGED
|
@@ -95,16 +95,26 @@
|
|
|
95
95
|
|
|
96
96
|
<div class="section-title" style="margin-top:12px" data-i18n="sidebar.avatar">아바타</div>
|
|
97
97
|
<div class="flex gap-2 items-center">
|
|
98
|
-
<div class="flex gap-1 items-center flex-1">
|
|
98
|
+
<div class="flex gap-1 items-center flex-1 avatar-control">
|
|
99
99
|
<label class="avatar-label">🤖</label>
|
|
100
100
|
<input type="text" id="agentAvatarInput" value="🦈" class="input-avatar" maxlength="2" placeholder="🦈">
|
|
101
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto avatar-upload-btn" id="agentAvatarUploadBtn" title="Upload agent avatar image">Img</button>
|
|
102
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto avatar-reset-btn" id="agentAvatarResetBtn" title="Reset agent avatar image">↺</button>
|
|
103
|
+
<input type="file" id="agentAvatarFile" hidden accept="image/png,image/jpeg,image/webp,image/gif">
|
|
101
104
|
</div>
|
|
102
|
-
<div class="flex gap-1 items-center flex-1">
|
|
105
|
+
<div class="flex gap-1 items-center flex-1 avatar-control">
|
|
103
106
|
<label class="avatar-label">👤</label>
|
|
104
107
|
<input type="text" id="userAvatarInput" value="👤" class="input-avatar" maxlength="2" placeholder="👤">
|
|
108
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto avatar-upload-btn" id="userAvatarUploadBtn" title="Upload user avatar image">Img</button>
|
|
109
|
+
<button class="sidebar-hb-btn btn-action-sm w-auto avatar-reset-btn" id="userAvatarResetBtn" title="Reset user avatar image">↺</button>
|
|
110
|
+
<input type="file" id="userAvatarFile" hidden accept="image/png,image/jpeg,image/webp,image/gif">
|
|
105
111
|
</div>
|
|
106
112
|
<button class="sidebar-hb-btn btn-action-sm w-auto" id="avatarSave">✓</button>
|
|
107
113
|
</div>
|
|
114
|
+
<div class="avatar-status-row">
|
|
115
|
+
<span class="avatar-status" id="agentAvatarStatus">emoji active</span>
|
|
116
|
+
<span class="avatar-status" id="userAvatarStatus">emoji active</span>
|
|
117
|
+
</div>
|
|
108
118
|
<button class="sidebar-hb-btn" id="btnClearChat">/clear</button>
|
|
109
119
|
<button class="sidebar-hb-btn" id="hbSidebarBtn"><span data-icon="heartPulse"></span> Heartbeat (0)</button>
|
|
110
120
|
<button class="sidebar-hb-btn" data-action="openTemplates"><span data-icon="plan"></span> 프롬프트 템플릿</button>
|
|
@@ -1,50 +1,224 @@
|
|
|
1
|
-
|
|
1
|
+
import { escapeHtml } from '../render.js';
|
|
2
|
+
import { api, getAuthToken } from '../api.js';
|
|
3
|
+
|
|
4
|
+
type AvatarRole = 'agent' | 'user';
|
|
5
|
+
type AvatarServerEntry = {
|
|
6
|
+
kind?: 'emoji' | 'image';
|
|
7
|
+
imageUrl?: string;
|
|
8
|
+
updatedAt?: number | null;
|
|
9
|
+
};
|
|
10
|
+
type AvatarServerState = Record<AvatarRole, AvatarServerEntry>;
|
|
11
|
+
type AvatarState = {
|
|
12
|
+
emoji: string;
|
|
13
|
+
imageUrl: string;
|
|
14
|
+
updatedAt: number | null;
|
|
15
|
+
};
|
|
16
|
+
|
|
2
17
|
const AGENT_KEY = 'agentAvatar';
|
|
3
18
|
const USER_KEY = 'userAvatar';
|
|
4
19
|
const DEFAULT_AGENT = '🦈';
|
|
5
20
|
const DEFAULT_USER = '👤';
|
|
6
21
|
|
|
7
|
-
|
|
8
|
-
|
|
22
|
+
const avatarState: Record<AvatarRole, AvatarState> = {
|
|
23
|
+
agent: { emoji: DEFAULT_AGENT, imageUrl: '', updatedAt: null },
|
|
24
|
+
user: { emoji: DEFAULT_USER, imageUrl: '', updatedAt: null },
|
|
25
|
+
};
|
|
9
26
|
|
|
10
|
-
|
|
11
|
-
export function getUserAvatar(): string { return userAvatar; }
|
|
27
|
+
let initialized = false;
|
|
12
28
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
localStorage.setItem(AGENT_KEY, agentAvatar);
|
|
16
|
-
document.querySelectorAll('.agent-icon').forEach(el => { el.textContent = agentAvatar; });
|
|
29
|
+
function stateFor(role: AvatarRole): AvatarState {
|
|
30
|
+
return avatarState[role];
|
|
17
31
|
}
|
|
18
32
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
function defaultEmoji(role: AvatarRole): string {
|
|
34
|
+
return role === 'agent' ? DEFAULT_AGENT : DEFAULT_USER;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function storageKey(role: AvatarRole): string {
|
|
38
|
+
return role === 'agent' ? AGENT_KEY : USER_KEY;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function inputId(role: AvatarRole): string {
|
|
42
|
+
return role === 'agent' ? 'agentAvatarInput' : 'userAvatarInput';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function statusId(role: AvatarRole): string {
|
|
46
|
+
return role === 'agent' ? 'agentAvatarStatus' : 'userAvatarStatus';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function iconSelector(role: AvatarRole): string {
|
|
50
|
+
return role === 'agent' ? '.agent-icon' : '.user-icon';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getEmoji(role: AvatarRole): string {
|
|
54
|
+
return stateFor(role).emoji;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function syncInputs(role: AvatarRole): void {
|
|
58
|
+
const input = document.getElementById(inputId(role)) as HTMLInputElement | null;
|
|
59
|
+
if (input) input.value = getEmoji(role);
|
|
60
|
+
const status = document.getElementById(statusId(role));
|
|
61
|
+
if (status) status.textContent = stateFor(role).imageUrl ? 'image active' : 'emoji active';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function setStatus(role: AvatarRole, text: string): void {
|
|
65
|
+
const status = document.getElementById(statusId(role));
|
|
66
|
+
if (status) status.textContent = text;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function avatarMarkup(role: AvatarRole): string {
|
|
70
|
+
const current = stateFor(role);
|
|
71
|
+
if (current.imageUrl) {
|
|
72
|
+
return `<img class="avatar-image" src="${escapeHtml(current.imageUrl)}" alt="" loading="lazy" decoding="async">`;
|
|
73
|
+
}
|
|
74
|
+
return escapeHtml(current.emoji);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function applyAvatar(role: AvatarRole): void {
|
|
78
|
+
const html = avatarMarkup(role);
|
|
79
|
+
const kind = stateFor(role).imageUrl ? 'image' : 'emoji';
|
|
80
|
+
document.querySelectorAll(iconSelector(role)).forEach((el) => {
|
|
81
|
+
el.innerHTML = html;
|
|
82
|
+
el.setAttribute('data-avatar-kind', kind);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function setServerAvatar(role: AvatarRole, payload?: AvatarServerEntry | null): void {
|
|
87
|
+
if (payload?.kind === 'image' && payload.imageUrl) {
|
|
88
|
+
stateFor(role).imageUrl = payload.imageUrl;
|
|
89
|
+
stateFor(role).updatedAt = payload.updatedAt ?? Date.now();
|
|
90
|
+
} else {
|
|
91
|
+
stateFor(role).imageUrl = '';
|
|
92
|
+
stateFor(role).updatedAt = payload?.updatedAt ?? null;
|
|
93
|
+
}
|
|
94
|
+
syncInputs(role);
|
|
95
|
+
applyAvatar(role);
|
|
23
96
|
}
|
|
24
97
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
98
|
+
async function loadServerAvatars(): Promise<void> {
|
|
99
|
+
const payload = await api<AvatarServerState>('/api/avatar');
|
|
100
|
+
if (!payload) return;
|
|
101
|
+
setServerAvatar('agent', payload.agent);
|
|
102
|
+
setServerAvatar('user', payload.user);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function authorizedFetch(path: string, init: RequestInit): Promise<Response> {
|
|
106
|
+
const token = await getAuthToken();
|
|
107
|
+
const headers = new Headers(init.headers || {});
|
|
108
|
+
if (token) headers.set('Authorization', `Bearer ${token}`);
|
|
109
|
+
return fetch(path, { ...init, headers });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function uploadAvatar(role: AvatarRole, file: File): Promise<void> {
|
|
113
|
+
setStatus(role, 'uploading...');
|
|
114
|
+
const res = await authorizedFetch(`/api/avatar/${role}/upload`, {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: { 'X-Filename': encodeURIComponent(file.name) },
|
|
117
|
+
body: file,
|
|
118
|
+
});
|
|
119
|
+
const json = await res.json().catch(() => null);
|
|
120
|
+
if (!res.ok) {
|
|
121
|
+
setStatus(role, 'upload failed');
|
|
122
|
+
throw new Error(json?.error || `avatar upload failed (${res.status})`);
|
|
123
|
+
}
|
|
124
|
+
setServerAvatar(role, json?.data || json);
|
|
125
|
+
}
|
|
28
126
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
127
|
+
async function resetAvatarImage(role: AvatarRole): Promise<void> {
|
|
128
|
+
setStatus(role, 'resetting...');
|
|
129
|
+
const res = await authorizedFetch(`/api/avatar/${role}/image`, { method: 'DELETE' });
|
|
130
|
+
const json = await res.json().catch(() => null);
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
setStatus(role, 'reset failed');
|
|
133
|
+
throw new Error(json?.error || `avatar reset failed (${res.status})`);
|
|
134
|
+
}
|
|
135
|
+
setServerAvatar(role, json?.data || json);
|
|
136
|
+
}
|
|
33
137
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
138
|
+
function bindRoleControls(role: AvatarRole): void {
|
|
139
|
+
const uploadBtnId = role === 'agent' ? 'agentAvatarUploadBtn' : 'userAvatarUploadBtn';
|
|
140
|
+
const resetBtnId = role === 'agent' ? 'agentAvatarResetBtn' : 'userAvatarResetBtn';
|
|
141
|
+
const fileInputId = role === 'agent' ? 'agentAvatarFile' : 'userAvatarFile';
|
|
142
|
+
|
|
143
|
+
document.getElementById(uploadBtnId)?.addEventListener('click', () => {
|
|
144
|
+
(document.getElementById(fileInputId) as HTMLInputElement | null)?.click();
|
|
39
145
|
});
|
|
40
146
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
147
|
+
document.getElementById(resetBtnId)?.addEventListener('click', async () => {
|
|
148
|
+
try {
|
|
149
|
+
await resetAvatarImage(role);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.warn('[avatar:reset]', (error as Error).message);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
document.getElementById(fileInputId)?.addEventListener('change', async (event: Event) => {
|
|
156
|
+
const input = event.target as HTMLInputElement;
|
|
157
|
+
const file = input.files?.[0];
|
|
158
|
+
if (!file) return;
|
|
159
|
+
try {
|
|
160
|
+
await uploadAvatar(role, file);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.warn('[avatar:upload]', (error as Error).message);
|
|
163
|
+
} finally {
|
|
164
|
+
input.value = '';
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function getAgentAvatar(): string { return getEmoji('agent'); }
|
|
170
|
+
export function getUserAvatar(): string { return getEmoji('user'); }
|
|
171
|
+
export function getAgentAvatarMarkup(): string { return avatarMarkup('agent'); }
|
|
172
|
+
export function getUserAvatarMarkup(): string { return avatarMarkup('user'); }
|
|
173
|
+
|
|
174
|
+
export function setAgentAvatar(emoji: string): void {
|
|
175
|
+
const next = (emoji || '').trim() || DEFAULT_AGENT;
|
|
176
|
+
stateFor('agent').emoji = next;
|
|
177
|
+
localStorage.setItem(storageKey('agent'), next);
|
|
178
|
+
syncInputs('agent');
|
|
179
|
+
if (!stateFor('agent').imageUrl) applyAvatar('agent');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function setUserAvatar(emoji: string): void {
|
|
183
|
+
const next = (emoji || '').trim() || DEFAULT_USER;
|
|
184
|
+
stateFor('user').emoji = next;
|
|
185
|
+
localStorage.setItem(storageKey('user'), next);
|
|
186
|
+
syncInputs('user');
|
|
187
|
+
if (!stateFor('user').imageUrl) applyAvatar('user');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function initAvatar(): Promise<void> {
|
|
191
|
+
stateFor('agent').emoji = localStorage.getItem(AGENT_KEY) || DEFAULT_AGENT;
|
|
192
|
+
stateFor('user').emoji = localStorage.getItem(USER_KEY) || DEFAULT_USER;
|
|
193
|
+
syncInputs('agent');
|
|
194
|
+
syncInputs('user');
|
|
195
|
+
|
|
196
|
+
if (!initialized) {
|
|
197
|
+
initialized = true;
|
|
198
|
+
|
|
199
|
+
document.getElementById('avatarSave')?.addEventListener('click', () => {
|
|
200
|
+
const agentInput = document.getElementById('agentAvatarInput') as HTMLInputElement | null;
|
|
201
|
+
const userInput = document.getElementById('userAvatarInput') as HTMLInputElement | null;
|
|
202
|
+
if (agentInput) setAgentAvatar(agentInput.value);
|
|
203
|
+
if (userInput) setUserAvatar(userInput.value);
|
|
48
204
|
});
|
|
205
|
+
|
|
206
|
+
for (const id of ['agentAvatarInput', 'userAvatarInput']) {
|
|
207
|
+
document.getElementById(id)?.addEventListener('keydown', (e: Event) => {
|
|
208
|
+
const keyEvent = e as KeyboardEvent;
|
|
209
|
+
if (keyEvent.key === 'Enter') {
|
|
210
|
+
keyEvent.preventDefault();
|
|
211
|
+
document.getElementById('avatarSave')?.click();
|
|
212
|
+
(keyEvent.target as HTMLInputElement).blur();
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
bindRoleControls('agent');
|
|
218
|
+
bindRoleControls('user');
|
|
49
219
|
}
|
|
220
|
+
|
|
221
|
+
await loadServerAvatars();
|
|
222
|
+
applyAvatar('agent');
|
|
223
|
+
applyAvatar('user');
|
|
50
224
|
}
|