cli-jaw 1.6.12 → 1.6.14

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.
Files changed (84) hide show
  1. package/dist/bin/commands/dispatch.js +41 -14
  2. package/dist/bin/commands/dispatch.js.map +1 -1
  3. package/dist/bin/commands/launchd.js +3 -2
  4. package/dist/bin/commands/launchd.js.map +1 -1
  5. package/dist/bin/commands/memory.js +11 -0
  6. package/dist/bin/commands/memory.js.map +1 -1
  7. package/dist/bin/commands/service.js +4 -3
  8. package/dist/bin/commands/service.js.map +1 -1
  9. package/dist/server.js +34 -6
  10. package/dist/server.js.map +1 -1
  11. package/dist/src/agent/lifecycle-handler.js +10 -4
  12. package/dist/src/agent/lifecycle-handler.js.map +1 -1
  13. package/dist/src/agent/memory-flush-controller.js +50 -13
  14. package/dist/src/agent/memory-flush-controller.js.map +1 -1
  15. package/dist/src/agent/spawn.js +48 -13
  16. package/dist/src/agent/spawn.js.map +1 -1
  17. package/dist/src/cli/handlers-runtime.js +4 -1
  18. package/dist/src/cli/handlers-runtime.js.map +1 -1
  19. package/dist/src/cli/handlers.js +4 -4
  20. package/dist/src/cli/handlers.js.map +1 -1
  21. package/dist/src/core/config.js +40 -3
  22. package/dist/src/core/config.js.map +1 -1
  23. package/dist/src/core/db.js +12 -0
  24. package/dist/src/core/db.js.map +1 -1
  25. package/dist/src/core/instance.js +18 -4
  26. package/dist/src/core/instance.js.map +1 -1
  27. package/dist/src/core/runtime-path.js +69 -0
  28. package/dist/src/core/runtime-path.js.map +1 -0
  29. package/dist/src/memory/identity.js +6 -3
  30. package/dist/src/memory/identity.js.map +1 -1
  31. package/dist/src/memory/reflect.js +47 -0
  32. package/dist/src/memory/reflect.js.map +1 -1
  33. package/dist/src/memory/shared.js +48 -15
  34. package/dist/src/memory/shared.js.map +1 -1
  35. package/dist/src/routes/avatar.js +120 -0
  36. package/dist/src/routes/avatar.js.map +1 -0
  37. package/dist/src/routes/jaw-memory.js +25 -1
  38. package/dist/src/routes/jaw-memory.js.map +1 -1
  39. package/dist/src/routes/memory.js +4 -1
  40. package/dist/src/routes/memory.js.map +1 -1
  41. package/dist/src/telegram/bot.js +7 -1
  42. package/dist/src/telegram/bot.js.map +1 -1
  43. package/package.json +1 -1
  44. package/public/css/chat.css +47 -5
  45. package/public/css/layout.css +57 -0
  46. package/public/css/variables.css +3 -15
  47. package/public/dist/assets/{employees-CA_DI2gy.js → employees-zxrU6ZV_.js} +1 -1
  48. package/public/dist/assets/index-D61icK-D.js +50 -0
  49. package/public/dist/assets/index-DVTRbkJF.css +1 -0
  50. package/public/dist/assets/locale-CxI5nTcf.js +3 -0
  51. package/public/dist/assets/render-CVr6a-dp.js +25 -0
  52. package/public/dist/assets/settings-BHIV4l1s.js +1 -0
  53. package/public/dist/assets/settings-Dl3RnWsB.js +40 -0
  54. package/public/dist/assets/{skills-D3cWRZOl.js → skills-DhiCSGws.js} +1 -1
  55. package/public/dist/assets/skills-JuDja1UC.js +1 -0
  56. package/public/dist/assets/{slash-commands-PkW1NPle.js → slash-commands-B1k1vFJG.js} +1 -1
  57. package/public/dist/assets/slash-commands-DyLS0abr.js +1 -0
  58. package/public/dist/assets/ui-BXZhbE_1.js +131 -0
  59. package/public/dist/assets/ui-qR28iS0L.js +1 -0
  60. package/public/dist/assets/{ws-Dcq99IkD.js → ws-CleMWrLF.js} +1 -1
  61. package/public/dist/index.html +28 -11
  62. package/public/index.html +26 -9
  63. package/public/js/features/avatar.ts +165 -33
  64. package/public/js/features/memory.ts +17 -5
  65. package/public/js/features/settings-templates.ts +6 -5
  66. package/public/js/locale.ts +30 -0
  67. package/public/js/main.ts +1 -1
  68. package/public/js/render.ts +2 -1
  69. package/public/js/ui.ts +30 -22
  70. package/public/js/uuid.ts +24 -0
  71. package/public/js/virtual-scroll.ts +60 -21
  72. package/public/js/ws.ts +6 -2
  73. package/public/locales/en.json +2 -3
  74. package/public/locales/ko.json +2 -3
  75. package/public/dist/assets/index-6UFnW9uO.js +0 -50
  76. package/public/dist/assets/index-ck7lqnh7.css +0 -1
  77. package/public/dist/assets/locale-DVVWjxKN.js +0 -1
  78. package/public/dist/assets/render-C2tuSVTL.js +0 -25
  79. package/public/dist/assets/settings-BJcG1r6G.js +0 -41
  80. package/public/dist/assets/settings-DV5X1U8f.js +0 -1
  81. package/public/dist/assets/skills-idPvxY0n.js +0 -1
  82. package/public/dist/assets/slash-commands-BHtBaFWh.js +0 -1
  83. package/public/dist/assets/ui-BdW-cWnY.js +0 -131
  84. 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-CVr6a-dp.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`,oe=`🦈`,se=`👤`,ce={agent:{emoji:oe,imageUrl:``,updatedAt:null},user:{emoji:se,imageUrl:``,updatedAt:null}},le=!1;function y(e){return ce[e]}function ue(e){return e===`agent`?`agentAvatarPreview`:`userAvatarPreview`}function de(e){return e===`agent`?`.agent-icon`:`.user-icon`}function b(e){let t=document.getElementById(ue(e));if(t){t.innerHTML=x(e);let n=y(e).imageUrl?`image`:`emoji`;t.setAttribute(`data-avatar-kind`,n)}}function x(e){let t=y(e);return t.imageUrl?`<img class="avatar-image" src="${c(t.imageUrl)}" alt="" loading="lazy" decoding="async">`:c(t.emoji)}function S(e){let t=x(e),n=y(e).imageUrl?`image`:`emoji`;document.querySelectorAll(de(e)).forEach(e=>{e.innerHTML=t,e.setAttribute(`data-avatar-kind`,n)})}function C(e,t){t?.kind===`image`&&t.imageUrl?(y(e).imageUrl=t.imageUrl,y(e).updatedAt=t.updatedAt??Date.now()):(y(e).imageUrl=``,y(e).updatedAt=t?.updatedAt??null),b(e),S(e)}async function fe(){let e=await n(`/api/avatar`);e&&(C(`agent`,e.agent),C(`user`,e.user))}async function pe(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 me(e,t){let n=await pe(`/api/avatar/${e}/upload`,{method:`POST`,headers:{"X-Filename":encodeURIComponent(t.name)},body:t}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||`avatar upload failed (${n.status})`);C(e,r?.data||r)}async function he(e){let t=await pe(`/api/avatar/${e}/image`,{method:`DELETE`}),n=await t.json().catch(()=>null);if(!t.ok)throw Error(n?.error||`avatar reset failed (${t.status})`);C(e,n?.data||n)}function ge(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 he(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 me(e,r)}catch(e){console.warn(`[avatar:upload]`,e.message)}finally{n.value=``}})}function _e(){return x(`agent`)}function w(){return x(`user`)}async function ve(){y(`agent`).emoji=localStorage.getItem(ie)||oe,y(`user`).emoji=localStorage.getItem(ae)||se,b(`agent`),b(`user`),le||(le=!0,ge(`agent`),ge(`user`)),await fe(),S(`agent`),S(`user`)}var ye=5,T=80,be=class{items=[];container;spacerTop;spacerBottom;viewport;_active=!1;_totalHeight=0;rafId=null;firstVisible=-1;lastVisible=-1;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=-1,this.lastVisible=-1,this.items=[],this._totalHeight=0)}setItems(e){this.items=e,this._totalHeight=e.reduce((e,t)=>e+t.height,0),!this._active&&this.items.length>=80&&this.activate(!0)}addItem(e,t){let n={id:e,html:t,height:T};this.items.push(n),this._totalHeight+=T,!this._active&&this.items.length>=80&&this.activate(!0),this._active&&this.scheduleRender()}appendLiveItem(e){if(!this._active)return;let t=e.outerHTML,n={id:h(),html:t,height:T};this.items.push(n),this._totalHeight+=T,this.scrollToBottom()}updateItemHtml(e,t){this.items[e]&&(this.items[e].html=t)}scrollHandler=()=>this.scheduleRender();activate(e=!1){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}),e&&(this.spacerTop.style.height=`${this._totalHeight}px`,this.spacerBottom.style.height=`0px`,this.container.scrollTop=this.container.scrollHeight,this.firstVisible=-1,this.lastVisible=-1),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-ye),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+ye),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;if(this.spacerTop.style.height=`${c}px`,this.spacerBottom.style.height=`${l}px`,i===this.firstVisible&&s===this.lastVisible)return;this.firstVisible=i,this.lastVisible=s;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.rafId&&=(cancelAnimationFrame(this.rafId),null),this.spacerTop.style.height=`${this._totalHeight}px`,this.spacerBottom.style.height=`0px`,this.container.scrollTop=this.container.scrollHeight,this.firstVisible=-1,this.lastVisible=-1,this.render()}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=-1,this.lastVisible=-1,this.onLazyRender=null,this.onPostRender=null,this.rafId&&=(cancelAnimationFrame(this.rafId),null)}},xe=null;function E(){return xe||=new be(`chatMessages`),xe}var Se=2e3,Ce=80;function we(e){return{chunks:[],fullText:``,textDirty:!1,element:e,pendingRAF:null,isFinalized:!1,lastRenderTime:0}}function D(e){return e.textDirty&&=(e.fullText=e.chunks.join(``),!1),e.fullText}function Te(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=D(e);n.length<Se||t-e.lastRenderTime>Ce?(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(D(e),!0)+`<span class="stream-cursor" aria-hidden="true"></span>`,e.lastRenderTime=performance.now())})}))}function Ee(e,t=!1){e.isFinalized=!0,e.pendingRAF&&=(cancelAnimationFrame(e.pendingRAF),null);let n=D(e);return t||(e.element.innerHTML=o(n)),n}var O=[`cdnjs.cloudflare.com`,`cdn.jsdelivr.net`,`unpkg.com`,`esm.sh`,`fonts.googleapis.com`,`fonts.gstatic.com`],De=[/\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*\(/],Oe=[];function ke(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(!O.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];O.some(t=>e===t||e.endsWith(`.`+t))||t.push(`CSS url() references external domain: ${e}`)}for(let n of De)if(n.test(e))return{valid:!1,reason:`Dangerous pattern: ${n.source}`,warnings:t};for(let n of Oe)n.test(e)&&t.push(`DOM sink detected: ${n.source}`);return{valid:!0,warnings:t}}function Ae(){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 je(){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 Me=[`cdnjs.cloudflare.com`,`cdn.jsdelivr.net`,`unpkg.com`,`esm.sh`,`fonts.googleapis.com`,`fonts.gstatic.com`],k=new Set,A=new Map,j=null;function Ne(){if(j)return;let e=document.getElementById(`chatMessages`);e&&(j=new MutationObserver(e=>{if(k.size)for(let t of e)for(let e of t.removedNodes)e instanceof HTMLIFrameElement&&e.contentWindow&&(k.delete(e.contentWindow),A.delete(e.contentWindow)),e instanceof HTMLElement&&e.querySelectorAll(`iframe`).forEach(e=>{e.contentWindow&&(k.delete(e.contentWindow),A.delete(e.contentWindow))})}),j.observe(e,{childList:!0,subtree:!0}))}function Pe(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 Fe(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=Me.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 Ie(){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 Le(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 Re=[[/\/p5\.js\/1\.11\.1[1-9]\//g,`/p5.js/1.11.10/`]];function ze(e){for(let[t,n]of Re)e=e.replace(t,n);return e}function Be(e){Ne(),Ve(),e=ze(e);let t=Array.from(crypto.getRandomValues(new Uint8Array(16)),e=>e.toString(16).padStart(2,`0`)).join(``),n=Ie(),r=Fe(e),i=Pe(e),a=Le(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 M(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=ke(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(je()),i.appendChild(Ae());let{iframe:a,nonce:o}=Be(n);i.appendChild(a),e.replaceWith(i);let s=!1;a.addEventListener(`load`,()=>{s?(a.contentWindow&&(k.delete(a.contentWindow),A.delete(a.contentWindow)),console.warn(`[jaw-diagram] iframe navigated — postMessage channel revoked`)):(s=!0,a.contentWindow&&(k.add(a.contentWindow),A.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&&(k.delete(e),A.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 N=null;function Ve(){if(N)return;let e=document.getElementById(`chatMessages`);e&&(N=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(()=>M(t.parentElement||e));return}}),N.observe(e,{childList:!0}))}var P=new WeakMap;function He(e,t){P.has(e)||(P.set(e,window.setTimeout(()=>P.delete(e),100)),document.querySelectorAll(`iframe`).forEach(n=>{n.contentWindow===e&&(n.style.height=`${Math.min(Math.max(t,60),2e3)}px`)}))}function Ue(){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&&(k.delete(r.contentWindow),A.delete(r.contentWindow));let i=e;i.dataset.gen=String((Number(i.dataset.gen||`0`)||0)+1);let{iframe:a,nonce:o}=Be(n);e.innerHTML=``,e.appendChild(je()),e.appendChild(Ae()),e.appendChild(a);let s=!1;a.addEventListener(`load`,()=>{s?a.contentWindow&&(k.delete(a.contentWindow),A.delete(a.contentWindow)):(s=!0,a.contentWindow&&(k.add(a.contentWindow),A.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 We=0;window.addEventListener(`message`,e=>{if(!e.data||typeof e.data!=`object`||!e.source||e.origin!==`null`||!k.has(e.source)||![...document.querySelectorAll(`iframe`)].find(t=>t.contentWindow===e.source)?.isConnected)return;let t=A.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;He(e.source,t);break}case`jaw-send-prompt`:{let t=Date.now();if(t-We<3e3)return;We=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 Ge(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 Ke(){document.querySelectorAll(`.tool-activity-live`).forEach(e=>e.remove()),document.querySelectorAll(`.msg-system.tool-activity`).forEach(e=>e.remove())}function qe(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}&times;${t}`:e).join(` + `)}function Je(e,t=120){let n=e.replace(/\s+/g,` `).trim();return n?n.length>t?`${n.slice(0,t-1)}…`:n:``}function Ye(e){let t=(e.detail||``).trim();return t?t!==(e.label||``).trim():!1}function F(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=Ye(e)?Je(a,e.type===`thinking`?120:100):``,l=s?`<span class="process-step-snippet">${c(s)}</span>`:``;return Ye(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 Xe(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 Ze(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 Qe(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){Ze(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 I(e,t=!0){let n=Xe(qe(e),t),r=document.createElement(`div`);r.innerHTML=n;let i=r.querySelector(`.process-steps-inner`);i&&(i.innerHTML=e.map(F).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 L(e){let t=e.element.querySelector(`.process-summary-text`);t&&(t.innerHTML=qe(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 R(e){let t=document.createElement(`div`);t.innerHTML=Xe(``,!1);let n=t.firstElementChild,r=e.querySelector(`.msg-content`);return r?r.before(n):e.appendChild(n),{element:n,steps:[],collapsed:!1}}function z(e,t){e.steps.push(t);let n=e.element.querySelector(`.process-steps-inner`);n&&n.insertAdjacentHTML(`beforeend`,F(t)),L(e)}function B(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=F(n);let t=e.firstElementChild;t&&i.replaceWith(t)}L(e)}function $e(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))}L(e)}function V(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`)}),L(e)}function H(e){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function U(e){return _e()}function W(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 et(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`)),nt()):(n&&(n.className=`status-badge status-idle`,n.textContent=`idle`),r&&(r.innerHTML=d.send,r.title=`Send`,r.classList.remove(`stop-mode`)),G(),tt(0))}function tt(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 nt(){let e=document.getElementById(`chatMessages`);if(!e||e.querySelector(`.skeleton-msg`))return;K();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),Z()}function G(){document.querySelectorAll(`.skeleton-msg`).forEach(e=>e.remove())}function K(){document.getElementById(`emptyState`)?.classList.remove(`visible`)}function q(){let e=document.getElementById(`chatMessages`);e&&e.children.length===0&&document.getElementById(`emptyState`)?.classList.add(`visible`)}function J(e,t,n){let r=document.getElementById(`chatMessages`);if(!r)return;let i=E();K();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),Z()}function rt(){Ke(),e.currentAgentDiv=null,e.currentProcessBlock=null}function it(t){if(G(),(!e.currentAgentDiv||!e.currentAgentDiv.isConnected)&&(e.currentAgentDiv=X(`agent`,``),e.currentProcessBlock=null),!e.currentProcessBlock){let t=e.currentAgentDiv.querySelector(`.agent-body`);t&&(e.currentProcessBlock=R(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),B(e.currentProcessBlock,a.id,{...t,id:a.id})):$e(e.currentProcessBlock,a.id,n),Z();return}let o=[...e.currentProcessBlock.steps].reverse().find(e=>e.status===`running`);if(o){t.detail&&!o.detail?(t.icon=i(t.icon),B(e.currentProcessBlock,o.id,{...t,id:o.id})):$e(e.currentProcessBlock,o.id,n),Z();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){B(e.currentProcessBlock,n.id,t),Z();return}}t.icon=i(t.icon),z(e.currentProcessBlock,t)}Z()}var Y=null;function at(t){if(!t)return;G(),(!e.currentAgentDiv||!e.currentAgentDiv.isConnected)&&(e.currentAgentDiv=X(`agent`,``),Y=null);let n=e.currentAgentDiv?.querySelector(`.msg-content`);n&&(Y||=we(n),Te(Y,t)),Z()}var ot=0;function st(t,n){let r=Date.now();if(!e.currentAgentDiv&&r-ot<500)return;Ke(),G();let i=!!e.currentProcessBlock;e.currentProcessBlock&&=(V(e.currentProcessBlock),null);let a=n&&n.length>0;if(t||a){(!e.currentAgentDiv||!e.currentAgentDiv.isConnected)&&(e.currentAgentDiv=X(`agent`,``));let r=e.currentAgentDiv?.querySelector(`.msg-content`),s=Y?Ee(Y,!0):``,c=t||s;Y=null;let u=a&&!i?I(W(n),!0):``;r&&(r.innerHTML=u+o(c)),r&&r.setAttribute(`data-raw`,l(c)),r&&M(r);let d=E();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(()=>{})}Y=null,e.currentAgentDiv=null,ot=Date.now(),et(`idle`),$()}function X(e,t,n){let r=document.getElementById(`chatMessages`),i=E();K(),G();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">${U(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">${w()}</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),M(f),!i.active&&!m&&r&&r.querySelectorAll(`.msg`).length>=80&&(r.querySelectorAll(`.msg`).forEach(e=>{i.addItem(h(),e.outerHTML)}),i.onPostRender=e=>{M(e),u(e)})),Z(),f}var ct=null;function Z(){let e=E();if(e.active){e.scrollToBottom();return}ct||=requestAnimationFrame(()=>{ct=null;let e=document.getElementById(`chatMessages`);e&&(e.scrollTop=e.scrollHeight)})}function lt(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-BHIV4l1s.js`).then(e=>e.loadSettings()),__vite__mapDeps([0])),e===`agents`&&r(()=>import(`./employees-zxrU6ZV_.js`).then(e=>e.loadEmployees()),__vite__mapDeps([0])),e===`skills`&&r(()=>import(`./skills-JuDja1UC.js`).then(e=>e.loadSkills()),__vite__mapDeps([0]))}function ut(){document.getElementById(`tabSettings`)?.classList.contains(`active`)?r(()=>import(`./settings-BHIV4l1s.js`).then(e=>e.savePerCli()),__vite__mapDeps([0])):r(()=>import(`./settings-BHIV4l1s.js`).then(e=>e.updateSettings()),__vite__mapDeps([0]))}function Q(e){let t=document.getElementById(`statMsgs`);t&&(t.textContent=a(`stat.messages`,{count:e}))}async function $(){let e=await n(`/api/messages`);e&&Q(e.length)}async function dt(){let e=E(),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){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`),M(n);let r=n.closest(`[data-vs-idx]`);if(r){let t=Number(r.dataset.vsIdx);e.updateItemHtml(t,r.outerHTML)}}},e.onPostRender=e=>{M(e),u(e)};let t=[];for(let e of r){let n=e.role===`assistant`?`agent`:e.role,r=l(e.content),i=c(n===`user`?a(`msg.you`):v()),o=e.role===`assistant`?H(e.tool_log):[],s=o.length>0?I(W(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">${U(e.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">${w()}</div></div>`;t.push({id:h(),html:d,height:80})}e.setItems(t),e.scrollToBottom()}else r.forEach(e=>{let t=X(e.role===`assistant`?`agent`:e.role,e.content,e.cli);if(e.role===`assistant`){let n=H(e.tool_log);if(n.length>0){let e=t.querySelector(`.agent-body`);if(e){let t=R(e);for(let e of W(n))z(t,e);V(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(()=>{}),Q(r.length),q();return}if(t&&t.children.length>0){q();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?H(t.tool_log):[],s=o.length>0?I(W(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">${U(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">${w()}</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`),M(n);let r=n.closest(`[data-vs-idx]`);if(r){let t=Number(r.dataset.vsIdx);e.updateItemHtml(t,r.outerHTML)}}},e.onPostRender=e=>{M(e),u(e)},e.scrollToBottom()}else i.forEach(e=>{let t=X(e.role===`assistant`?`agent`:e.role,e.content,e.cli);if(e.role===`assistant`&&e.tool_log){let n=H(e.tool_log);if(n.length>0){let e=t.querySelector(`.agent-body`);if(e){let t=R(e);for(let e of W(n))z(t,e);V(t)}}}});J(`${d.warning} 오프라인 모드 — 캐시된 메시지 표시 중`),Q(i.length)}q()}function ft(){let e=document.getElementById(`chatMessages`);e&&(Qe(e),Ge(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{ve as _,st as a,dt as c,et as d,it as f,E as g,Ue as h,rt as i,$ as l,tt as m,J as n,ut as o,lt as p,at as r,ft as s,X as t,Z as u,re as v};
@@ -0,0 +1 @@
1
+ import{c as e,d as t,i as n,m as r}from"./ui-BXZhbE_1.js";export{n as cleanupToolActivity,e as loadMessages,t as setStatus,r as updateQueueBadge};
@@ -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-C2tuSVTL.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-BdW-cWnY.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-CA_DI2gy.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-CA_DI2gy.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-qcenMIau.js`).then(async t=>{t.cleanupToolActivity(),e||(await t.loadMessages(),x=Date.now()),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-CA_DI2gy.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-qcenMIau.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};
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-CVr6a-dp.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-BXZhbE_1.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-zxrU6ZV_.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-zxrU6ZV_.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-qR28iS0L.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-zxrU6ZV_.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-qR28iS0L.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};
@@ -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-6UFnW9uO.js"></script>
28
+ <script type="module" crossorigin src="/dist/assets/index-D61icK-D.js"></script>
29
29
  <link rel="stylesheet" crossorigin href="/dist/assets/vendor-render-Bjnw0wQ6.css">
30
- <link rel="stylesheet" crossorigin href="/dist/assets/index-ck7lqnh7.css">
30
+ <link rel="stylesheet" crossorigin href="/dist/assets/index-DVTRbkJF.css">
31
31
  </head>
32
32
 
33
33
  <body>
@@ -88,16 +88,33 @@
88
88
  </div>
89
89
 
90
90
  <div class="section-title" style="margin-top:12px" data-i18n="sidebar.avatar">아바타</div>
91
- <div class="flex gap-2 items-center">
92
- <div class="flex gap-1 items-center flex-1">
93
- <label class="avatar-label">🤖</label>
94
- <input type="text" id="agentAvatarInput" value="🦈" class="input-avatar" maxlength="2" placeholder="🦈">
95
- </div>
96
- <div class="flex gap-1 items-center flex-1">
97
- <label class="avatar-label">👤</label>
98
- <input type="text" id="userAvatarInput" value="👤" class="input-avatar" maxlength="2" placeholder="👤">
91
+ <div class="avatar-row">
92
+ <div class="avatar-card">
93
+ <div class="avatar-preview agent-icon" id="agentAvatarPreview" aria-hidden="true">🦈</div>
94
+ <span class="avatar-card-label">Agent</span>
95
+ <div class="avatar-card-actions">
96
+ <button class="avatar-action-btn" id="agentAvatarUploadBtn" title="Upload image">
97
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
98
+ </button>
99
+ <button class="avatar-action-btn" id="agentAvatarResetBtn" title="Reset to default">
100
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
101
+ </button>
102
+ </div>
103
+ <input type="file" id="agentAvatarFile" hidden accept="image/png,image/jpeg,image/webp,image/gif">
104
+ </div>
105
+ <div class="avatar-card">
106
+ <div class="avatar-preview user-icon" id="userAvatarPreview" aria-hidden="true">👤</div>
107
+ <span class="avatar-card-label">User</span>
108
+ <div class="avatar-card-actions">
109
+ <button class="avatar-action-btn" id="userAvatarUploadBtn" title="Upload image">
110
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
111
+ </button>
112
+ <button class="avatar-action-btn" id="userAvatarResetBtn" title="Reset to default">
113
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
114
+ </button>
115
+ </div>
116
+ <input type="file" id="userAvatarFile" hidden accept="image/png,image/jpeg,image/webp,image/gif">
99
117
  </div>
100
- <button class="sidebar-hb-btn btn-action-sm w-auto" id="avatarSave">✓</button>
101
118
  </div>
102
119
  <button class="sidebar-hb-btn" id="btnClearChat">/clear</button>
103
120
  <button class="sidebar-hb-btn" id="hbSidebarBtn"><span data-icon="heartPulse"></span> Heartbeat (0)</button>
package/public/index.html CHANGED
@@ -94,16 +94,33 @@
94
94
  </div>
95
95
 
96
96
  <div class="section-title" style="margin-top:12px" data-i18n="sidebar.avatar">아바타</div>
97
- <div class="flex gap-2 items-center">
98
- <div class="flex gap-1 items-center flex-1">
99
- <label class="avatar-label">🤖</label>
100
- <input type="text" id="agentAvatarInput" value="🦈" class="input-avatar" maxlength="2" placeholder="🦈">
101
- </div>
102
- <div class="flex gap-1 items-center flex-1">
103
- <label class="avatar-label">👤</label>
104
- <input type="text" id="userAvatarInput" value="👤" class="input-avatar" maxlength="2" placeholder="👤">
97
+ <div class="avatar-row">
98
+ <div class="avatar-card">
99
+ <div class="avatar-preview agent-icon" id="agentAvatarPreview" aria-hidden="true">🦈</div>
100
+ <span class="avatar-card-label">Agent</span>
101
+ <div class="avatar-card-actions">
102
+ <button class="avatar-action-btn" id="agentAvatarUploadBtn" title="Upload image">
103
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
104
+ </button>
105
+ <button class="avatar-action-btn" id="agentAvatarResetBtn" title="Reset to default">
106
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
107
+ </button>
108
+ </div>
109
+ <input type="file" id="agentAvatarFile" hidden accept="image/png,image/jpeg,image/webp,image/gif">
110
+ </div>
111
+ <div class="avatar-card">
112
+ <div class="avatar-preview user-icon" id="userAvatarPreview" aria-hidden="true">👤</div>
113
+ <span class="avatar-card-label">User</span>
114
+ <div class="avatar-card-actions">
115
+ <button class="avatar-action-btn" id="userAvatarUploadBtn" title="Upload image">
116
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
117
+ </button>
118
+ <button class="avatar-action-btn" id="userAvatarResetBtn" title="Reset to default">
119
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
120
+ </button>
121
+ </div>
122
+ <input type="file" id="userAvatarFile" hidden accept="image/png,image/jpeg,image/webp,image/gif">
105
123
  </div>
106
- <button class="sidebar-hb-btn btn-action-sm w-auto" id="avatarSave">✓</button>
107
124
  </div>
108
125
  <button class="sidebar-hb-btn" id="btnClearChat">/clear</button>
109
126
  <button class="sidebar-hb-btn" id="hbSidebarBtn"><span data-icon="heartPulse"></span> Heartbeat (0)</button>
@@ -1,50 +1,182 @@
1
- // ── Agent & User Avatar ──
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
- let agentAvatar = DEFAULT_AGENT;
8
- let userAvatar = DEFAULT_USER;
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
- export function getAgentAvatar(): string { return agentAvatar; }
11
- export function getUserAvatar(): string { return userAvatar; }
27
+ let initialized = false;
12
28
 
13
- export function setAgentAvatar(emoji: string): void {
14
- agentAvatar = (emoji || '').trim() || DEFAULT_AGENT;
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
- export function setUserAvatar(emoji: string): void {
20
- userAvatar = (emoji || '').trim() || DEFAULT_USER;
21
- localStorage.setItem(USER_KEY, userAvatar);
22
- document.querySelectorAll('.user-icon').forEach(el => { el.textContent = userAvatar; });
33
+ function storageKey(role: AvatarRole): string {
34
+ return role === 'agent' ? AGENT_KEY : USER_KEY;
35
+ }
36
+
37
+ function inputId(_role: AvatarRole): string {
38
+ return _role === 'agent' ? 'agentAvatarPreview' : 'userAvatarPreview';
39
+ }
40
+
41
+ function iconSelector(role: AvatarRole): string {
42
+ return role === 'agent' ? '.agent-icon' : '.user-icon';
43
+ }
44
+
45
+ function syncPreview(role: AvatarRole): void {
46
+ const preview = document.getElementById(inputId(role));
47
+ if (preview) {
48
+ preview.innerHTML = avatarMarkup(role);
49
+ const kind = stateFor(role).imageUrl ? 'image' : 'emoji';
50
+ preview.setAttribute('data-avatar-kind', kind);
51
+ }
52
+ }
53
+
54
+ function avatarMarkup(role: AvatarRole): string {
55
+ const current = stateFor(role);
56
+ if (current.imageUrl) {
57
+ return `<img class="avatar-image" src="${escapeHtml(current.imageUrl)}" alt="" loading="lazy" decoding="async">`;
58
+ }
59
+ return escapeHtml(current.emoji);
60
+ }
61
+
62
+ function applyAvatar(role: AvatarRole): void {
63
+ const html = avatarMarkup(role);
64
+ const kind = stateFor(role).imageUrl ? 'image' : 'emoji';
65
+ document.querySelectorAll(iconSelector(role)).forEach((el) => {
66
+ el.innerHTML = html;
67
+ el.setAttribute('data-avatar-kind', kind);
68
+ });
69
+ }
70
+
71
+ function setServerAvatar(role: AvatarRole, payload?: AvatarServerEntry | null): void {
72
+ if (payload?.kind === 'image' && payload.imageUrl) {
73
+ stateFor(role).imageUrl = payload.imageUrl;
74
+ stateFor(role).updatedAt = payload.updatedAt ?? Date.now();
75
+ } else {
76
+ stateFor(role).imageUrl = '';
77
+ stateFor(role).updatedAt = payload?.updatedAt ?? null;
78
+ }
79
+ syncPreview(role);
80
+ applyAvatar(role);
81
+ }
82
+
83
+ async function loadServerAvatars(): Promise<void> {
84
+ const payload = await api<AvatarServerState>('/api/avatar');
85
+ if (!payload) return;
86
+ setServerAvatar('agent', payload.agent);
87
+ setServerAvatar('user', payload.user);
88
+ }
89
+
90
+ async function authorizedFetch(path: string, init: RequestInit): Promise<Response> {
91
+ const token = await getAuthToken();
92
+ const headers = new Headers(init.headers || {});
93
+ if (token) headers.set('Authorization', `Bearer ${token}`);
94
+ return fetch(path, { ...init, headers });
23
95
  }
24
96
 
25
- export function initAvatar(): void {
26
- agentAvatar = localStorage.getItem(AGENT_KEY) || DEFAULT_AGENT;
27
- userAvatar = localStorage.getItem(USER_KEY) || DEFAULT_USER;
97
+ async function uploadAvatar(role: AvatarRole, file: File): Promise<void> {
98
+ const res = await authorizedFetch(`/api/avatar/${role}/upload`, {
99
+ method: 'POST',
100
+ headers: { 'X-Filename': encodeURIComponent(file.name) },
101
+ body: file,
102
+ });
103
+ const json = await res.json().catch(() => null);
104
+ if (!res.ok) throw new Error(json?.error || `avatar upload failed (${res.status})`);
105
+ setServerAvatar(role, json?.data || json);
106
+ }
28
107
 
29
- const ai = document.getElementById('agentAvatarInput') as HTMLInputElement | null;
30
- const ui = document.getElementById('userAvatarInput') as HTMLInputElement | null;
31
- if (ai) ai.value = agentAvatar;
32
- if (ui) ui.value = userAvatar;
108
+ async function resetAvatarImage(role: AvatarRole): Promise<void> {
109
+ const res = await authorizedFetch(`/api/avatar/${role}/image`, { method: 'DELETE' });
110
+ const json = await res.json().catch(() => null);
111
+ if (!res.ok) throw new Error(json?.error || `avatar reset failed (${res.status})`);
112
+ setServerAvatar(role, json?.data || json);
113
+ }
33
114
 
34
- document.getElementById('avatarSave')?.addEventListener('click', () => {
35
- const a = document.getElementById('agentAvatarInput') as HTMLInputElement | null;
36
- const u = document.getElementById('userAvatarInput') as HTMLInputElement | null;
37
- if (a) setAgentAvatar(a.value);
38
- if (u) setUserAvatar(u.value);
115
+ function bindRoleControls(role: AvatarRole): void {
116
+ const uploadBtnId = role === 'agent' ? 'agentAvatarUploadBtn' : 'userAvatarUploadBtn';
117
+ const resetBtnId = role === 'agent' ? 'agentAvatarResetBtn' : 'userAvatarResetBtn';
118
+ const fileInputId = role === 'agent' ? 'agentAvatarFile' : 'userAvatarFile';
119
+
120
+ document.getElementById(uploadBtnId)?.addEventListener('click', () => {
121
+ (document.getElementById(fileInputId) as HTMLInputElement | null)?.click();
122
+ });
123
+
124
+ document.getElementById(resetBtnId)?.addEventListener('click', async () => {
125
+ try {
126
+ await resetAvatarImage(role);
127
+ } catch (error) {
128
+ console.warn('[avatar:reset]', (error as Error).message);
129
+ }
39
130
  });
40
131
 
41
- for (const id of ['agentAvatarInput', 'userAvatarInput']) {
42
- document.getElementById(id)?.addEventListener('keydown', (e: Event) => {
43
- if ((e as KeyboardEvent).key === 'Enter') {
44
- (e as KeyboardEvent).preventDefault();
45
- document.getElementById('avatarSave')?.click();
46
- (e.target as HTMLInputElement).blur();
47
- }
48
- });
132
+ document.getElementById(fileInputId)?.addEventListener('change', async (event: Event) => {
133
+ const input = event.target as HTMLInputElement;
134
+ const file = input.files?.[0];
135
+ if (!file) return;
136
+ try {
137
+ await uploadAvatar(role, file);
138
+ } catch (error) {
139
+ console.warn('[avatar:upload]', (error as Error).message);
140
+ } finally {
141
+ input.value = '';
142
+ }
143
+ });
144
+ }
145
+
146
+ export function getAgentAvatar(): string { return stateFor('agent').emoji; }
147
+ export function getUserAvatar(): string { return stateFor('user').emoji; }
148
+ export function getAgentAvatarMarkup(): string { return avatarMarkup('agent'); }
149
+ export function getUserAvatarMarkup(): string { return avatarMarkup('user'); }
150
+
151
+ export function setAgentAvatar(emoji: string): void {
152
+ const next = (emoji || '').trim() || DEFAULT_AGENT;
153
+ stateFor('agent').emoji = next;
154
+ localStorage.setItem(storageKey('agent'), next);
155
+ syncPreview('agent');
156
+ if (!stateFor('agent').imageUrl) applyAvatar('agent');
157
+ }
158
+
159
+ export function setUserAvatar(emoji: string): void {
160
+ const next = (emoji || '').trim() || DEFAULT_USER;
161
+ stateFor('user').emoji = next;
162
+ localStorage.setItem(storageKey('user'), next);
163
+ syncPreview('user');
164
+ if (!stateFor('user').imageUrl) applyAvatar('user');
165
+ }
166
+
167
+ export async function initAvatar(): Promise<void> {
168
+ stateFor('agent').emoji = localStorage.getItem(AGENT_KEY) || DEFAULT_AGENT;
169
+ stateFor('user').emoji = localStorage.getItem(USER_KEY) || DEFAULT_USER;
170
+ syncPreview('agent');
171
+ syncPreview('user');
172
+
173
+ if (!initialized) {
174
+ initialized = true;
175
+ bindRoleControls('agent');
176
+ bindRoleControls('user');
49
177
  }
178
+
179
+ await loadServerAvatars();
180
+ applyAvatar('agent');
181
+ applyAvatar('user');
50
182
  }
@@ -2,6 +2,7 @@
2
2
  import { escapeHtml } from '../render.js';
3
3
  import { api, apiJson } from '../api.js';
4
4
  import { ICONS } from '../icons.js';
5
+ import { t } from '../locale.js';
5
6
 
6
7
  interface MemoryFile {
7
8
  name: string;
@@ -79,7 +80,7 @@ function syncSidebarBadge(status: AdvancedMemoryStatus | null, basicCount: numbe
79
80
  const sideBtn = $('memorySidebarBtn');
80
81
  if (!sideBtn) return;
81
82
  if (status?.enabled && status?.hasSoul === false) {
82
- sideBtn.innerHTML = `${ICONS.brain} Memory · <span style="color:var(--accent)">업데이트 필요</span>`;
83
+ sideBtn.innerHTML = `${ICONS.brain} Memory · <span style="color:var(--accent)">${t('updateNeeded')}</span>`;
83
84
  return;
84
85
  }
85
86
  const state = status?.indexState === 'ready'
@@ -101,7 +102,7 @@ function renderStatusBanner(status: AdvancedMemoryStatus | null) {
101
102
  banner.style.display = '';
102
103
  if (status.hasSoul === false) {
103
104
  banner.innerHTML = `<span>Memory structure upgrade available.</span>
104
- <button id="advUpgradeSoulBtn" class="btn-sm" style="margin-left:8px">메모리 업데이트하기</button>`;
105
+ <button id="advUpgradeSoulBtn" class="btn-sm" style="margin-left:8px">${t('memoryUpdateBtn')}</button>`;
105
106
  return;
106
107
  }
107
108
  if (status.state === 'not_initialized') {
@@ -324,12 +325,23 @@ export async function rerunAdvancedBootstrap(): Promise<void> {
324
325
 
325
326
  export async function upgradeSoulMemory(): Promise<void> {
326
327
  setAdvBusy(true);
327
- setAdvBanner('메모리 구조를 업데이트하는 중...', true);
328
- await apiJson<{ message?: string }>('/api/memory/reindex', 'POST', {});
328
+ setAdvBanner(t('memoryUpdating'), true);
329
+ const result = await apiJson<{
330
+ activated: boolean;
331
+ created: boolean;
332
+ preview: string;
333
+ }>('/api/jaw-memory/soul/activate', 'POST', {});
329
334
  setAdvBusy(false);
330
- setAdvBanner('✓ Memory structure upgraded. Soul identity created.');
335
+ if (result?.created) {
336
+ setAdvBanner('✓ Soul identity created.');
337
+ } else {
338
+ setAdvBanner('✓ Soul already active.');
339
+ }
331
340
  await openMemoryModal();
332
341
  switchMemTab('status');
342
+ const freshStatus = await apiJson<any>('/api/memory/status');
343
+ syncSidebarBadge(freshStatus, 0);
344
+ renderStatusBanner(freshStatus);
333
345
  }
334
346
 
335
347
  export async function reindexAdvancedMemory(): Promise<void> {