nothumanallowed 9.5.2 → 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -305
- package/bin/nha.mjs +34 -3
- package/package.json +2 -2
- package/src/cli.mjs +105 -153
- package/src/commands/ask.mjs +18 -206
- package/src/commands/chat.mjs +64 -482
- package/src/commands/ui.mjs +41 -837
- package/src/config.mjs +0 -2
- package/src/constants.mjs +1 -1
- package/src/services/google-oauth.mjs +21 -12
- package/src/services/llm.mjs +0 -138
- package/src/services/ops-daemon.mjs +236 -0
- package/src/services/screen-capture.mjs +160 -0
- package/src/services/tool-executor.mjs +88 -335
- package/src/services/web-ui.mjs +126 -423
- package/src/services/browser-engine.mjs +0 -1240
- package/src/services/conversations.mjs +0 -277
- package/src/services/web-tools.mjs +0 -430
package/src/services/web-ui.mjs
CHANGED
|
@@ -46,7 +46,10 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
46
46
|
|
|
47
47
|
/* ---- LAYOUT: mobile-first ---- */
|
|
48
48
|
.app{display:flex;flex-direction:column;height:100vh;height:100dvh}
|
|
49
|
-
|
|
49
|
+
.header{display:flex;align-items:center;gap:12px;padding:10px 16px;border-bottom:1px solid var(--border);background:var(--bg);position:relative;z-index:60;flex-shrink:0}
|
|
50
|
+
.header__burger{background:none;color:var(--green);font-size:22px;padding:4px 8px;line-height:1}
|
|
51
|
+
.header__title{font-size:14px;color:var(--bright);font-weight:700;flex:1}
|
|
52
|
+
.header__clock{font-size:10px;color:var(--dim)}
|
|
50
53
|
|
|
51
54
|
.sidebar{display:none;position:fixed;top:0;left:0;width:260px;height:100vh;height:100dvh;background:var(--bg2);border-right:1px solid var(--border);z-index:200;flex-direction:column;overflow-y:auto;box-shadow:4px 0 20px rgba(0,0,0,0.8)}
|
|
52
55
|
.sidebar--open{display:flex}
|
|
@@ -84,9 +87,8 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
84
87
|
.section-title{font-size:12px;color:var(--cyan);text-transform:uppercase;letter-spacing:1px;margin-bottom:10px}
|
|
85
88
|
|
|
86
89
|
/* ---- CHAT ---- */
|
|
87
|
-
.
|
|
88
|
-
.chat{
|
|
89
|
-
@media(min-width:901px){.content--chat{padding:0!important}}
|
|
90
|
+
.chat{display:flex;flex-direction:column;height:calc(100vh - 52px);height:calc(100dvh - 52px)}
|
|
91
|
+
@media(min-width:901px){.chat{height:calc(100vh - 52px)}}
|
|
90
92
|
.chat__messages{flex:1;overflow-y:auto;padding-bottom:12px;-webkit-overflow-scrolling:touch}
|
|
91
93
|
.chat__empty{text-align:center;padding:60px 16px;color:var(--dim)}
|
|
92
94
|
.chat__empty-title{font-size:28px;color:var(--green);margin-bottom:12px}
|
|
@@ -96,30 +98,10 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
96
98
|
.msg--assistant .msg__bubble{background:var(--greendim);border:1px solid var(--green3);border-radius:8px 8px 8px 2px;padding:10px 14px;max-width:85%;color:var(--text);white-space:pre-wrap;word-wrap:break-word}
|
|
97
99
|
.msg__label{font-size:10px;color:var(--dim);margin-bottom:2px}
|
|
98
100
|
.msg--thinking{color:var(--dim);font-style:italic}
|
|
99
|
-
.
|
|
100
|
-
.tool-indicator--browser{border-color:#9c27b0;color:#ce93d8}
|
|
101
|
-
.tool-indicator--web{border-color:var(--cyan);color:var(--cyan)}
|
|
102
|
-
.tool-indicator--email{border-color:var(--green3);color:var(--green)}
|
|
103
|
-
.screenshot-preview{max-width:100%;border-radius:var(--r);margin:8px 0;border:1px solid var(--border)}
|
|
104
|
-
.browser-viewer{position:fixed;top:12px;left:12px;width:440px;background:var(--bg2);border:2px solid #9c27b0;border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,0.6);z-index:300;overflow:hidden;display:none;transition:all .3s ease}
|
|
105
|
-
.browser-viewer--open{display:block}
|
|
106
|
-
.browser-viewer__header{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#1a1a2e;border-bottom:1px solid #9c27b0;font-size:10px;color:#ce93d8}
|
|
107
|
-
.browser-viewer__dot{width:6px;height:6px;border-radius:50%;background:#9c27b0;animation:bvpulse 1.5s infinite}
|
|
108
|
-
@keyframes bvpulse{0%,100%{opacity:1}50%{opacity:.3}}
|
|
109
|
-
.browser-viewer__title{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
110
|
-
.browser-viewer__close{background:none;border:none;color:#666;cursor:pointer;font-size:14px;padding:0 4px}
|
|
111
|
-
.browser-viewer__close:hover{color:#fff}
|
|
112
|
-
.browser-viewer__frame{width:100%;aspect-ratio:16/9;background:#000;display:flex;align-items:center;justify-content:center}
|
|
113
|
-
.browser-viewer__frame img{width:100%;height:100%;object-fit:contain}
|
|
114
|
-
.browser-viewer__status{padding:4px 10px;font-size:9px;color:var(--dim);border-top:1px solid var(--border)}
|
|
115
|
-
@media(max-width:600px){.browser-viewer{width:calc(100vw - 24px);top:8px;left:8px}}
|
|
116
|
-
@media(min-width:901px){.browser-viewer{left:232px}}
|
|
117
|
-
.chat__bar{display:flex;gap:8px;padding:10px 0 12px 0;border-top:1px solid var(--border);flex-shrink:0}
|
|
101
|
+
.chat__bar{display:flex;gap:8px;padding:10px 0 0 0;border-top:1px solid var(--border);flex-shrink:0}
|
|
118
102
|
.chat__input{flex:1;resize:none;min-height:40px;max-height:100px;padding:10px 14px}
|
|
119
103
|
.chat__send{background:var(--green3);color:var(--bg);padding:10px 16px;border-radius:var(--r);font-weight:700;font-size:12px}
|
|
120
104
|
.chat__send:disabled{opacity:.4}
|
|
121
|
-
.chat__stop{background:var(--red);color:var(--bright);padding:10px 16px;border-radius:var(--r);font-weight:700;font-size:12px;display:none}
|
|
122
|
-
.chat__stop--visible{display:block}
|
|
123
105
|
|
|
124
106
|
/* ---- TASKS ---- */
|
|
125
107
|
.task-bar{display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap}
|
|
@@ -216,52 +198,11 @@ input:focus,textarea:focus{border-color:var(--green3)}
|
|
|
216
198
|
const JS = `
|
|
217
199
|
var API = '';
|
|
218
200
|
var currentView = 'dashboard';
|
|
219
|
-
var chatHistory = [];
|
|
220
|
-
var activeConvId = null;
|
|
221
|
-
var convList = [];
|
|
201
|
+
var chatHistory = (function(){try{var s=localStorage.getItem('nha_chat_history');return s?JSON.parse(s):[];}catch(e){return [];}})();
|
|
222
202
|
var dash = {emails:[],events:[],tasks:[],plan:null,status:null};
|
|
223
|
-
var dashLoaded = {emails:false,events:false,tasks:false,contacts:false,notes:false,drive:false,github:false,notion:false,slack:false};
|
|
224
|
-
var chatStreaming = false;
|
|
225
|
-
var chatAbortController = null;
|
|
226
|
-
|
|
227
|
-
function endStreaming(){
|
|
228
|
-
chatStreaming=false;chatAbortController=null;
|
|
229
|
-
var stopBtn=document.getElementById('chatStop');if(stopBtn)stopBtn.classList.remove('chat__stop--visible');
|
|
230
|
-
var sendBtn=document.getElementById('chatSend');if(sendBtn)sendBtn.style.display='';
|
|
231
|
-
}
|
|
232
|
-
function stopChat(){
|
|
233
|
-
if(chatAbortController){try{chatAbortController.abort()}catch(e){}}
|
|
234
|
-
endStreaming();
|
|
235
|
-
if(chatHistory.length>0){
|
|
236
|
-
var last=chatHistory[chatHistory.length-1];
|
|
237
|
-
if(last.role==='assistant'&&(!last.content||last.content===''))last.content='[Stopped]';
|
|
238
|
-
}
|
|
239
|
-
renderMessages();
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// ---- BROWSER VIEWER (live preview of headless Chrome) ----
|
|
243
|
-
function showBrowserViewer(title,status){
|
|
244
|
-
var v=document.getElementById('browserViewer');if(!v)return;
|
|
245
|
-
v.classList.add('browser-viewer--open');
|
|
246
|
-
var t=document.getElementById('bvTitle');if(t)t.textContent=title||'Browser';
|
|
247
|
-
var s=document.getElementById('bvStatus');if(s)s.textContent=status||'Loading...';
|
|
248
|
-
}
|
|
249
|
-
function updateBrowserFrame(base64,format){
|
|
250
|
-
var f=document.getElementById('bvFrame');if(!f)return;
|
|
251
|
-
f.innerHTML='<img src="data:image/'+(format||'jpeg')+';base64,'+base64+'" alt="Browser view">';
|
|
252
|
-
}
|
|
253
|
-
function updateBrowserStatus(status){
|
|
254
|
-
var s=document.getElementById('bvStatus');if(s)s.textContent=status;
|
|
255
|
-
}
|
|
256
|
-
function closeBrowserViewer(){
|
|
257
|
-
var v=document.getElementById('browserViewer');if(v)v.classList.remove('browser-viewer--open');
|
|
258
|
-
}
|
|
259
203
|
|
|
260
|
-
function
|
|
261
|
-
function
|
|
262
|
-
function createNewConv(){return apiPost('/api/conversations',{}).then(function(r){if(r&&r.conversation){activeConvId=r.conversation.id;chatHistory=[];renderMessages();loadConvList();}})}
|
|
263
|
-
function deleteConv(id){return fetch(API+'/api/conversations/'+id,{method:'DELETE'}).then(function(){loadConvList();if(id===activeConvId)createNewConv();})}
|
|
264
|
-
function clearChatHistory(){createNewConv()}
|
|
204
|
+
function saveChatToStorage(){try{localStorage.setItem('nha_chat_history',JSON.stringify(chatHistory.slice(-40)));}catch(e){}}
|
|
205
|
+
function clearChatHistory(){chatHistory=[];saveChatToStorage();renderMessages();}
|
|
265
206
|
var agentsList = [];
|
|
266
207
|
var selectedAgent = null;
|
|
267
208
|
|
|
@@ -278,11 +219,7 @@ function switchView(v) {
|
|
|
278
219
|
if(el.dataset.view===v){el.classList.add('nav-item--active')}else{el.classList.remove('nav-item--active')}
|
|
279
220
|
});
|
|
280
221
|
var titles = {dashboard:'Dashboard',chat:'Chat',plan:'Daily Plan',tasks:'Tasks',emails:'Emails',calendar:'Calendar',drive:'Drive',contacts:'Contacts',notes:'Notes',onedrive:'OneDrive',mstodo:'Microsoft To Do',agents:'Agents',settings:'Settings'};
|
|
281
|
-
|
|
282
|
-
if(spt)spt.textContent=titles[v]||v;
|
|
283
|
-
// Toggle content--chat class for proper chat layout (no overflow, flex column)
|
|
284
|
-
var ct=document.getElementById('content');
|
|
285
|
-
if(ct){if(v==='chat'){ct.classList.add('content--chat')}else{ct.classList.remove('content--chat')}}
|
|
222
|
+
document.getElementById('headerTitle').textContent = titles[v]||v;
|
|
286
223
|
closeSidebar();
|
|
287
224
|
render();
|
|
288
225
|
}
|
|
@@ -314,11 +251,10 @@ function apiPatch(p){return fetch(API+p,{method:'PATCH'}).then(function(r){retur
|
|
|
314
251
|
|
|
315
252
|
// ---- LOAD DATA ----
|
|
316
253
|
function loadDash(){
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return apiGet('/api/emails?page=0&pageSize=25').then(function(r){dash.emails=(r&&r.emails)||[];dash._emailHasMore=r&&r.hasMore;dashLoaded.emails=true;emailPage=0;updateBadges();render()});
|
|
254
|
+
return Promise.all([apiGet('/api/status'),apiGet('/api/emails'),apiGet('/api/calendar'),apiGet('/api/tasks')]).then(function(r){
|
|
255
|
+
dash.status=r[0];dash.emails=(r[1]&&r[1].emails)||[];dash.events=(r[2]&&r[2].events)||[];dash.tasks=(r[3]&&r[3].tasks)||[];
|
|
256
|
+
updateBadges();
|
|
257
|
+
});
|
|
322
258
|
}
|
|
323
259
|
function loadAgents(){return apiGet('/api/agents').then(function(r){agentsList=(r&&r.agents)||[]})}
|
|
324
260
|
function updateBadges(){
|
|
@@ -332,7 +268,6 @@ function updateBadges(){
|
|
|
332
268
|
// ---- HELPERS ----
|
|
333
269
|
function esc(s){return s?String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'):''}
|
|
334
270
|
function fmtTime(iso){if(!iso)return '';try{return new Date(iso).toLocaleTimeString('en',{hour:'2-digit',minute:'2-digit',hour12:true})}catch(e){return iso}}
|
|
335
|
-
function loadingHTML(label){return '<div style="text-align:center;padding:40px"><div class="spinner"></div><div style="color:var(--dim);margin-top:8px;font-size:12px">Loading '+esc(label)+'...</div></div>'}
|
|
336
271
|
|
|
337
272
|
// ---- RENDER ----
|
|
338
273
|
function render(){
|
|
@@ -361,14 +296,13 @@ function render(){
|
|
|
361
296
|
|
|
362
297
|
// ---- DASHBOARD ----
|
|
363
298
|
function renderDash(el){
|
|
364
|
-
if(!dashLoaded.tasks&&!dashLoaded.events&&!dashLoaded.emails){el.innerHTML=loadingHTML('dashboard');return}
|
|
365
299
|
var t=dash.tasks,e=dash.emails,ev=dash.events;
|
|
366
300
|
var done=t.filter(function(x){return x.status==='done'}).length;
|
|
367
301
|
var pend=t.length-done;
|
|
368
302
|
var pct=t.length>0?Math.round(done/t.length*100):0;
|
|
369
303
|
var h='<div class="dash-grid">'+
|
|
370
304
|
'<div class="card"><div class="card__title">Tasks</div><div class="card__value">'+pend+'</div><div class="card__sub">'+done+'/'+t.length+' done ('+pct+'%)</div></div>'+
|
|
371
|
-
'<div class="card"><div class="card__title">Emails</div><div class="card__value">'+
|
|
305
|
+
'<div class="card"><div class="card__title">Emails</div><div class="card__value">'+e.length+'</div><div class="card__sub">'+(e.length>0?esc(e[0].from):'Inbox zero')+'</div></div>'+
|
|
372
306
|
'<div class="card"><div class="card__title">Events</div><div class="card__value">'+ev.length+'</div><div class="card__sub">'+(ev.length>0?esc(ev[0].summary):'No events')+'</div></div>'+
|
|
373
307
|
'<div class="card"><div class="card__title">Agents</div><div class="card__value">38</div><div class="card__sub">Ready</div></div>'+
|
|
374
308
|
'</div>';
|
|
@@ -382,225 +316,67 @@ function renderDash(el){
|
|
|
382
316
|
var chatReady=false;
|
|
383
317
|
function renderChat(el){
|
|
384
318
|
if(!chatReady||!document.getElementById('chatMessages')){
|
|
385
|
-
el.innerHTML='<div style="
|
|
386
|
-
'<div id="convSidebar" style="width:220px;border-right:1px solid var(--border);overflow-y:auto;flex-shrink:0;background:var(--bg);display:'+(localStorage.getItem('nha_conv_sidebar')==='hidden'?'none':'')+'">'+
|
|
387
|
-
'<div style="padding:8px"><button onclick="createNewConv()" style="width:100%;padding:8px;border-radius:var(--r);border:1px solid var(--green);background:transparent;color:var(--green);cursor:pointer;font-size:11px">+ New Chat</button></div>'+
|
|
388
|
-
'<div id="convList"></div>'+
|
|
389
|
-
'</div>'+
|
|
390
|
-
'<div style="flex:1;display:flex;flex-direction:column;min-width:0">'+
|
|
391
|
-
'<div style="padding:6px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px">'+
|
|
392
|
-
'<button onclick="toggleConvSidebar()" style="background:none;border:none;cursor:pointer;font-size:14px;color:var(--dim);padding:2px 6px" title="Toggle conversations">💬</button>'+
|
|
393
|
-
'<span id="convTitle" style="flex:1;font-size:12px;color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">New Chat</span>'+
|
|
394
|
-
'<button onclick="createNewConv()" style="background:none;border:1px solid var(--green);color:var(--green);padding:4px 10px;border-radius:var(--r);cursor:pointer;font-size:10px">+ New</button>'+
|
|
395
|
-
'<button onclick="exportConvMd()" style="background:none;border:1px solid var(--border);color:var(--dim);padding:4px 8px;border-radius:var(--r);cursor:pointer;font-size:10px" title="Export Markdown">Export</button>'+
|
|
396
|
-
'</div>'+
|
|
397
|
-
'<div class="chat"><div class="chat__messages" id="chatMessages"></div>'+
|
|
398
|
-
'<div id="chatAttachInfo" style="display:none;padding:4px 12px;font-size:11px;color:var(--cyan);background:var(--bg2);border-top:1px solid var(--border)"><span id="chatAttachName"></span> <button onclick="clearChatAttach()" style="background:none;border:none;color:#f44;cursor:pointer;font-size:14px;font-weight:700">×</button></div>'+
|
|
399
|
-
'<div class="chat__bar"><button class="chat__mic" id="chatMic" onclick="toggleVoiceInput()" title="Voice input">🎤</button><button onclick="document.getElementById(\\x27chatFileInput\\x27).click()" style="background:none;border:none;cursor:pointer;font-size:16px;padding:4px" title="Attach file">📎</button><button onclick="document.getElementById(\\x27chatImageInput\\x27).click()" style="background:none;border:none;cursor:pointer;font-size:16px;padding:4px" title="Attach image">📷</button><input type="file" id="chatFileInput" style="display:none" onchange="handleChatFile(this)"><input type="file" id="chatImageInput" accept="image/*" style="display:none" onchange="handleChatImage(this)"><textarea class="chat__input" id="chatInput" placeholder="Ask anything... (or attach file/image first)" rows="1"></textarea><button class="chat__send" id="chatSend">Send</button><button class="chat__stop" id="chatStop" onclick="stopChat()">Stop</button></div>'+
|
|
400
|
-
'</div>'+
|
|
401
|
-
'</div>'+
|
|
402
|
-
'</div>';
|
|
319
|
+
el.innerHTML='<div class="chat"><div class="chat__messages" id="chatMessages"></div><div class="chat__bar"><button class="chat__mic" id="chatMic" onclick="toggleVoiceInput()" title="Voice input">🎤</button><textarea class="chat__input" id="chatInput" placeholder="Ask anything..." rows="1"></textarea><button class="chat__send" id="chatSend">Send</button><button onclick="clearChatHistory()" style="background:none;color:var(--dim);font-size:10px;padding:4px 8px" title="Clear chat history">Clear</button></div></div>';
|
|
403
320
|
chatReady=true;
|
|
404
321
|
document.getElementById('chatSend').onclick=sendChat;
|
|
405
322
|
document.getElementById('chatInput').onkeydown=function(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendChat()}};
|
|
406
|
-
|
|
407
|
-
if(!activeConvId&&convList.length>0){loadConv(convList[0].id)}
|
|
408
|
-
else if(!activeConvId){createNewConv()}
|
|
409
|
-
else{loadConv(activeConvId)}
|
|
410
|
-
});
|
|
323
|
+
renderMessages();
|
|
411
324
|
setTimeout(function(){var i=document.getElementById('chatInput');if(i)i.focus()},100);
|
|
412
325
|
}
|
|
413
326
|
}
|
|
414
|
-
function toggleConvSidebar(){var s=document.getElementById('convSidebar');if(!s)return;var hide=s.style.display!=='none';s.style.display=hide?'none':'';try{localStorage.setItem('nha_conv_sidebar',hide?'hidden':'visible')}catch(e){}}
|
|
415
|
-
function renderConvSidebar(){
|
|
416
|
-
var el=document.getElementById('convList');if(!el)return;
|
|
417
|
-
var h='';convList.forEach(function(c){
|
|
418
|
-
var active=c.id===activeConvId;
|
|
419
|
-
var turns=Math.floor(c.messageCount/2);
|
|
420
|
-
h+='<div onclick="loadConv(\\x27'+c.id+'\\x27)" style="padding:8px 12px;cursor:pointer;border-left:3px solid '+(active?'var(--green)':'transparent')+';background:'+(active?'var(--bg2)':'transparent')+'" onmouseover="this.style.background=\\x27var(--bg2)\\x27" onmouseout="this.style.background='+(active?"\\x27var(--bg2)\\x27":"\\x27transparent\\x27")+'">'+
|
|
421
|
-
'<div style="font-size:11px;color:var(--fg);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+esc(c.title)+'</div>'+
|
|
422
|
-
'<div style="font-size:9px;color:var(--dim);display:flex;gap:6px;margin-top:2px"><span>'+turns+' turns</span>'+(active?'':'<span onclick="event.stopPropagation();deleteConv(\\x27'+c.id+'\\x27)" style="color:var(--red);cursor:pointer">del</span>')+'</div>'+
|
|
423
|
-
'</div>';
|
|
424
|
-
});
|
|
425
|
-
el.innerHTML=h;
|
|
426
|
-
var t=document.getElementById('convTitle');
|
|
427
|
-
if(t){var ac=convList.find(function(c){return c.id===activeConvId});t.textContent=ac?ac.title:'New Chat';}
|
|
428
|
-
}
|
|
429
|
-
function exportConvMd(){if(!activeConvId)return;window.open(API+'/api/conversations/'+activeConvId+'/export?format=md','_blank');}
|
|
430
327
|
function renderMessages(){
|
|
431
328
|
var el=document.getElementById('chatMessages');if(!el)return;
|
|
432
329
|
if(chatHistory.length===0){
|
|
433
|
-
el.innerHTML='<div class="chat__empty"><div class="chat__empty-title">NHA Chat</div><div>Personal Operations Assistant
|
|
330
|
+
el.innerHTML='<div class="chat__empty"><div class="chat__empty-title">NHA Chat</div><div>Personal Operations Assistant</div><div class="chat__empty-hint">Try: Show my unread emails / What is on my calendar? / Add a task</div></div>';
|
|
434
331
|
return;
|
|
435
332
|
}
|
|
436
333
|
var h='';chatHistory.forEach(function(m){
|
|
437
|
-
var
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
334
|
+
var content = m.content || '';
|
|
335
|
+
// Handle canvas render markers
|
|
336
|
+
var canvasMatch = content.match(/\\[CANVAS_RENDER\\](.*?)\\[\\/CANVAS_RENDER\\]/s);
|
|
337
|
+
if (canvasMatch) {
|
|
338
|
+
try { var cd = JSON.parse(canvasMatch[1]); showCanvas(cd.html, cd.title); } catch(e){}
|
|
339
|
+
content = content.replace(/\\[CANVAS_RENDER\\].*?\\[\\/CANVAS_RENDER\\]/s, '').trim();
|
|
340
|
+
}
|
|
341
|
+
if (content.indexOf('[CANVAS_CLEAR]') !== -1) {
|
|
342
|
+
closeCanvas();
|
|
343
|
+
content = content.replace(/\\[CANVAS_CLEAR\\].*?\\[\\/CANVAS_CLEAR\\]/s, '').trim();
|
|
344
|
+
}
|
|
345
|
+
// Handle screenshot markers
|
|
346
|
+
var screenshotMatch = content.match(/\\[SCREENSHOT\\](.*?)\\[\\/SCREENSHOT\\]/);
|
|
347
|
+
if (screenshotMatch) {
|
|
348
|
+
var imgPath = screenshotMatch[1];
|
|
349
|
+
content = content.replace(/\\[SCREENSHOT\\].*?\\[\\/SCREENSHOT\\]/, '');
|
|
350
|
+
content = '<img src="/api/screenshots/' + encodeURIComponent(imgPath.split('/').pop()) + '" style="max-width:100%;border-radius:8px;margin:8px 0" />' + content;
|
|
351
|
+
}
|
|
352
|
+
var bubbleContent = m.role === 'assistant' ? content : esc(content);
|
|
353
|
+
h+='<div class="msg msg--'+esc(m.role)+'"><div class="msg__label">'+esc(m.role==='user'?'You':'NHA')+'</div><div class="msg__bubble">'+bubbleContent+'</div></div>';
|
|
447
354
|
});
|
|
448
355
|
el.innerHTML=h;el.scrollTop=el.scrollHeight;
|
|
449
356
|
}
|
|
450
|
-
var chatAttachedFile=null;
|
|
451
|
-
var chatAttachedImage=null;
|
|
452
|
-
|
|
453
|
-
function handleChatFile(input){
|
|
454
|
-
var file=input.files&&input.files[0];if(!file)return;
|
|
455
|
-
var isPDF=file.name.toLowerCase().endsWith('.pdf')||file.type==='application/pdf';
|
|
456
|
-
if(isPDF){
|
|
457
|
-
// PDF: read as base64 and send as document to LLM
|
|
458
|
-
var reader=new FileReader();
|
|
459
|
-
reader.onload=function(e){
|
|
460
|
-
var base64=e.target.result.split(',')[1];
|
|
461
|
-
chatAttachedFile={name:file.name,size:file.size,content:null,base64:base64,mimeType:'application/pdf',isPDF:true};
|
|
462
|
-
chatAttachedImage=null;
|
|
463
|
-
document.getElementById('chatAttachInfo').style.display='';
|
|
464
|
-
document.getElementById('chatAttachName').textContent='📎 '+file.name+' ('+Math.round(file.size/1024)+' KB)';
|
|
465
|
-
};
|
|
466
|
-
reader.readAsDataURL(file);
|
|
467
|
-
}else{
|
|
468
|
-
var reader=new FileReader();
|
|
469
|
-
reader.onload=function(e){
|
|
470
|
-
chatAttachedFile={name:file.name,size:file.size,content:e.target.result};
|
|
471
|
-
chatAttachedImage=null;
|
|
472
|
-
document.getElementById('chatAttachInfo').style.display='';
|
|
473
|
-
document.getElementById('chatAttachName').textContent='📎 '+file.name+' ('+Math.round(file.size/1024)+' KB)';
|
|
474
|
-
};
|
|
475
|
-
reader.readAsText(file);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function handleChatImage(input){
|
|
480
|
-
var file=input.files&&input.files[0];if(!file)return;
|
|
481
|
-
var reader=new FileReader();
|
|
482
|
-
reader.onload=function(e){
|
|
483
|
-
var base64=e.target.result.split(',')[1];
|
|
484
|
-
chatAttachedImage={name:file.name,size:file.size,base64:base64,mimeType:file.type||'image/jpeg'};
|
|
485
|
-
chatAttachedFile=null;
|
|
486
|
-
document.getElementById('chatAttachInfo').style.display='';
|
|
487
|
-
document.getElementById('chatAttachName').textContent='📷 '+file.name+' ('+Math.round(file.size/1024)+' KB)';
|
|
488
|
-
};
|
|
489
|
-
reader.readAsDataURL(file);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function clearChatAttach(){
|
|
493
|
-
chatAttachedFile=null;chatAttachedImage=null;
|
|
494
|
-
document.getElementById('chatAttachInfo').style.display='none';
|
|
495
|
-
document.getElementById('chatFileInput').value='';
|
|
496
|
-
document.getElementById('chatImageInput').value='';
|
|
497
|
-
}
|
|
498
|
-
|
|
499
357
|
function sendChat(){
|
|
500
358
|
var inp=document.getElementById('chatInput');if(!inp)return;
|
|
501
|
-
var msg=inp.value.trim();
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
359
|
+
var msg=inp.value.trim();if(!msg)return;
|
|
360
|
+
chatHistory.push({role:'user',content:msg});
|
|
361
|
+
inp.value='';saveChatToStorage();renderMessages();
|
|
362
|
+
chatHistory.push({role:'assistant',content:'Thinking...'});renderMessages();
|
|
363
|
+
apiPost('/api/chat',{message:msg,history:chatHistory.slice(0,-1)}).then(function(r){
|
|
364
|
+
chatHistory.pop();
|
|
365
|
+
if(r&&r.response){chatHistory.push({role:'assistant',content:r.response})}
|
|
366
|
+
else if(r&&r.error){chatHistory.push({role:'assistant',content:'Error: '+r.error})}
|
|
367
|
+
else{chatHistory.push({role:'assistant',content:'Error: no response from server'})}
|
|
368
|
+
saveChatToStorage();renderMessages();
|
|
369
|
+
// Refresh ALL data after any tool execution
|
|
370
|
+
if(r&&((r.actions&&r.actions.length>0)||(r.toolResults&&r.toolResults.length>0))){
|
|
371
|
+
calEventsCache={};
|
|
372
|
+
contactsData=null;
|
|
373
|
+
notesData=null;
|
|
374
|
+
driveData=null;
|
|
375
|
+
onedriveData=null;
|
|
376
|
+
mstodoData=null;
|
|
377
|
+
loadDash().then(function(){render()}).catch(function(){});
|
|
520
378
|
}
|
|
521
|
-
|
|
522
|
-
clearChatAttach();
|
|
523
|
-
apiPost('/api/chat',payload).then(function(r){
|
|
524
|
-
chatHistory.pop();
|
|
525
|
-
if(r&&r.response){chatHistory.push({role:'assistant',content:r.response})}
|
|
526
|
-
else if(r&&r.error){chatHistory.push({role:'assistant',content:'Error: '+r.error})}
|
|
527
|
-
else{chatHistory.push({role:'assistant',content:'Error: no response'})}
|
|
528
|
-
renderMessages();loadConvList();
|
|
529
|
-
});
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
clearChatAttach();
|
|
533
|
-
|
|
534
|
-
// Streaming SSE
|
|
535
|
-
chatStreaming=true;
|
|
536
|
-
chatAbortController=new AbortController();
|
|
537
|
-
// Show Stop button, hide Send button
|
|
538
|
-
var stopBtn=document.getElementById('chatStop');if(stopBtn)stopBtn.classList.add('chat__stop--visible');
|
|
539
|
-
var sendBtn=document.getElementById('chatSend');if(sendBtn)sendBtn.style.display='none';
|
|
540
|
-
chatHistory.push({role:'assistant',content:''});
|
|
541
|
-
renderMessages();
|
|
542
|
-
var streamIdx=chatHistory.length-1;
|
|
543
|
-
var allHistory=chatHistory.slice(0,-1).map(function(m){return{role:m.role,content:(m.content||'').replace(/!\\[Screenshot\\]\\(data:image\\/[^)]+\\)/g,'[Screenshot taken]')};});
|
|
544
|
-
var payload={message:msg,history:allHistory,conversationId:activeConvId};
|
|
545
|
-
|
|
546
|
-
fetch(API+'/api/chat/stream',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload),signal:chatAbortController.signal}).then(function(response){
|
|
547
|
-
if(!response.ok||!response.body){chatHistory[streamIdx].content='Error: connection failed';endStreaming();renderMessages();return;}
|
|
548
|
-
var reader=response.body.getReader();var decoder=new TextDecoder();var buffer='';var currentEvent='';
|
|
549
|
-
function pump(){
|
|
550
|
-
reader.read().then(function(result){
|
|
551
|
-
if(result.done){endStreaming();renderMessages();loadConvList();return;}
|
|
552
|
-
buffer+=decoder.decode(result.value,{stream:true});
|
|
553
|
-
var lines=buffer.split('\\n');buffer=lines.pop()||'';
|
|
554
|
-
for(var i=0;i<lines.length;i++){
|
|
555
|
-
var line=lines[i];
|
|
556
|
-
if(line.startsWith('event: ')){currentEvent=line.slice(7).trim();}
|
|
557
|
-
else if(line.startsWith('data: ')){
|
|
558
|
-
try{
|
|
559
|
-
var data=JSON.parse(line.slice(6));
|
|
560
|
-
if(currentEvent==='token'&&data.content){
|
|
561
|
-
chatHistory[streamIdx].content+=data.content;
|
|
562
|
-
var el=document.getElementById('chatMessages');
|
|
563
|
-
if(el){var msgs=el.querySelectorAll('.msg');var last=msgs[msgs.length-1];if(last){var bub=last.querySelector('.msg__bubble');if(bub)bub.textContent=chatHistory[streamIdx].content;}el.scrollTop=el.scrollHeight;}
|
|
564
|
-
}
|
|
565
|
-
if(currentEvent==='tool'){
|
|
566
|
-
var toolLabels={browser_open:'Opening page',browser_screenshot:'Taking screenshot',browser_click:'Clicking element',browser_type:'Typing text',browser_extract:'Extracting content',browser_js:'Running JavaScript',browser_wait:'Waiting for element',browser_scroll:'Scrolling page',browser_key:'Pressing key',browser_close:'Closing browser',web_search:'Searching the web',fetch_url:'Fetching URL',gmail_list:'Searching emails',gmail_read:'Reading email',gmail_send:'Sending email',calendar_today:'Loading calendar',calendar_create:'Creating event'};
|
|
567
|
-
var label=toolLabels[data.action]||data.action;
|
|
568
|
-
var indicator=data.status==='executing'?'\\u23f3 '+label+'...':'\\u2705 '+label;
|
|
569
|
-
if(data.status==='error')indicator='\\u274c '+label+' failed';
|
|
570
|
-
// Show browser viewer for browser and web_search actions
|
|
571
|
-
var isBrowserAction=data.action&&(data.action.startsWith('browser_')||data.action==='web_search');
|
|
572
|
-
if(isBrowserAction&&data.status==='executing'){showBrowserViewer(label,'Executing...');}
|
|
573
|
-
if(isBrowserAction&&data.status==='done'){updateBrowserStatus('\\u2705 '+label);}
|
|
574
|
-
if(isBrowserAction&&data.status==='error'){updateBrowserStatus('\\u274c '+label);}
|
|
575
|
-
if(data.action==='browser_close'&&data.status==='done'){setTimeout(closeBrowserViewer,2000);}
|
|
576
|
-
// Strip JSON action blocks from streamed content (they are internal tool calls, not for the user)
|
|
577
|
-
if(data.status==='executing'){chatHistory[streamIdx].content=chatHistory[streamIdx].content.replace(new RegExp('\\x60\\x60\\x60json[\\\\s\\\\S]*?\\x60\\x60\\x60','g'),'').trim()+'\\n';}
|
|
578
|
-
chatHistory[streamIdx].content+=indicator+'\\n';
|
|
579
|
-
renderMessages();
|
|
580
|
-
}
|
|
581
|
-
if(currentEvent==='screenshot'&&data.base64){
|
|
582
|
-
// Only update the browser viewer — the actual image in chat is handled by the 'done' event via screenshotFiles
|
|
583
|
-
showBrowserViewer('Screenshot','Captured');
|
|
584
|
-
updateBrowserFrame(data.base64,data.format||'jpeg');
|
|
585
|
-
updateBrowserStatus('Screenshot captured');
|
|
586
|
-
}
|
|
587
|
-
if(currentEvent==='browser_frame'&&data.base64){
|
|
588
|
-
// Live frame update — also ensure viewer is open
|
|
589
|
-
showBrowserViewer(data.url||'Browser','Live');
|
|
590
|
-
updateBrowserFrame(data.base64,data.format||'jpeg');
|
|
591
|
-
if(data.url)updateBrowserStatus(data.url);
|
|
592
|
-
}
|
|
593
|
-
if(currentEvent==='tool_synthesis'){chatHistory[streamIdx].content='';renderMessages();}
|
|
594
|
-
if(currentEvent==='done'){endStreaming();if(data.content)chatHistory[streamIdx].content=data.content;var ssf=data.screenshotFiles||[];for(var fi=0;fi<ssf.length;fi++){chatHistory[streamIdx].content+='\\n\\n';}renderMessages();loadConvList();if(ssf.length>0)setTimeout(closeBrowserViewer,5000);}
|
|
595
|
-
if(currentEvent==='error'){endStreaming();chatHistory[streamIdx].content='Error: '+(data.message||'Unknown');renderMessages();}
|
|
596
|
-
}catch(e){}
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
pump();
|
|
600
|
-
}).catch(function(e){endStreaming();if(e.name!=='AbortError'){chatHistory[streamIdx].content='Error: '+e.message;renderMessages();}});
|
|
601
|
-
}
|
|
602
|
-
pump();
|
|
603
|
-
}).catch(function(e){endStreaming();if(e.name!=='AbortError'){chatHistory[streamIdx].content='Error: '+e.message;renderMessages();}});
|
|
379
|
+
});
|
|
604
380
|
}
|
|
605
381
|
|
|
606
382
|
// ---- TASKS ----
|
|
@@ -670,64 +446,17 @@ function refreshPlan(){
|
|
|
670
446
|
|
|
671
447
|
// ---- EMAILS ----
|
|
672
448
|
function renderEmails(el){
|
|
673
|
-
if(!dashLoaded.emails){el.innerHTML=loadingHTML('emails');return}
|
|
674
449
|
var e=dash.emails;
|
|
675
|
-
if(e.length===0){el.innerHTML='<div class="card" style="text-align:center;color:var(--dim);padding:30px">
|
|
676
|
-
var
|
|
677
|
-
var h='<div style="display:flex;gap:8px;margin-bottom:10px;align-items:center">';
|
|
678
|
-
h+='<span style="font-size:12px;color:var(--dim)">'+e.length+' emails'+(unreadCount>0?' ('+unreadCount+' unread)':'')+'</span>';
|
|
679
|
-
if(unreadCount>0)h+='<button class="btn btn--secondary" style="font-size:10px;padding:4px 10px" onclick="markAllEmailsRead()">Mark all read</button>';
|
|
680
|
-
h+='</div>';
|
|
681
|
-
e.forEach(function(x){
|
|
450
|
+
if(e.length===0){el.innerHTML='<div class="card" style="text-align:center;color:var(--dim);padding:30px">No unread emails</div>';return}
|
|
451
|
+
var h='';e.forEach(function(x){
|
|
682
452
|
var unreadStyle=x.isUnread?'border-left:3px solid var(--green);font-weight:700':'border-left:3px solid transparent;opacity:0.7';
|
|
683
453
|
h+='<div class="card email" style="cursor:pointer;'+unreadStyle+'" onclick="openEmail(\\x27'+esc(x.id)+'\\x27)"><div class="email__header"><span class="email__from">'+esc(x.from)+'</span><span class="email__date">'+esc(x.date)+(x.isUnread?' <span style="color:var(--green);font-size:9px">NEW</span>':'')+'</span></div><div class="email__subject">'+esc(x.subject)+'</div><div class="email__snippet" style="font-weight:400">'+esc((x.snippet||'').slice(0,150))+'</div></div>';
|
|
684
454
|
});
|
|
685
|
-
// Load More button
|
|
686
|
-
if(dash._emailHasMore!==false){
|
|
687
|
-
h+='<button id="loadMoreEmails" onclick="loadMoreEmails()" style="width:100%;padding:12px;margin-top:8px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);color:var(--cyan);font-family:var(--font);font-size:12px;cursor:pointer;font-weight:700">Load More Emails</button>';
|
|
688
|
-
}
|
|
689
455
|
el.innerHTML=h;
|
|
690
456
|
}
|
|
691
|
-
var emailPage=0;
|
|
692
|
-
function loadMoreEmails(){
|
|
693
|
-
var btn=document.getElementById('loadMoreEmails');
|
|
694
|
-
if(btn){btn.textContent='Loading...';btn.disabled=true;}
|
|
695
|
-
emailPage++;
|
|
696
|
-
apiGet('/api/emails?page='+emailPage+'&pageSize=25').then(function(r){
|
|
697
|
-
if(r&&r.emails){
|
|
698
|
-
for(var i=0;i<r.emails.length;i++){
|
|
699
|
-
if(!dash.emails.find(function(e){return e.id===r.emails[i].id})){
|
|
700
|
-
dash.emails.push(r.emails[i]);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
dash._emailHasMore=r.hasMore;
|
|
704
|
-
updateBadges();
|
|
705
|
-
render();
|
|
706
|
-
}
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
function markAllEmailsRead(){
|
|
710
|
-
apiPost('/api/email/mark-all-read',{}).then(function(r){
|
|
711
|
-
if(r&&r.ok){
|
|
712
|
-
dash.emails.forEach(function(e){e.isUnread=false});
|
|
713
|
-
updateBadges();
|
|
714
|
-
renderEmails(document.getElementById('content'));
|
|
715
|
-
showToast('success','All Read','Marked '+( r.count||0)+' emails as read');
|
|
716
|
-
}else{
|
|
717
|
-
showToast('error','Error',r&&r.error||'Failed');
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
457
|
var openEmailId=null;
|
|
722
458
|
function openEmail(id){
|
|
723
459
|
openEmailId=id;
|
|
724
|
-
// Mark as read locally + on server
|
|
725
|
-
var emailObj=dash.emails.find(function(e){return e.id===id});
|
|
726
|
-
if(emailObj&&emailObj.isUnread){
|
|
727
|
-
emailObj.isUnread=false;
|
|
728
|
-
updateBadges();
|
|
729
|
-
apiPost('/api/email/mark-read',{messageId:id}).catch(function(){});
|
|
730
|
-
}
|
|
731
460
|
var el=document.getElementById('content');
|
|
732
461
|
el.innerHTML='<div style="text-align:center;padding:40px"><div class="spinner"></div><div style="color:var(--dim)">Loading email...</div></div>';
|
|
733
462
|
apiPost('/api/email/read',{messageId:id}).then(function(r){
|
|
@@ -1376,68 +1105,22 @@ function renderAgents(el){
|
|
|
1376
1105
|
|
|
1377
1106
|
var filtered=agentFilter?agentsList.filter(function(a){return a.category===agentFilter}):agentsList;
|
|
1378
1107
|
|
|
1379
|
-
h+='<div style="margin-bottom:10px"><button class="btn btn--primary" style="font-size:11px" onclick="showCreateAgentForm()">+ Create Agent</button></div>';
|
|
1380
1108
|
h+='<div class="agents-grid">';
|
|
1381
1109
|
filtered.forEach(function(a){
|
|
1382
1110
|
var name=a.name||a.agentName;
|
|
1383
1111
|
var display=a.displayName||name;
|
|
1384
1112
|
var icon=AGENT_ICONS[name.toLowerCase()]||'\\u{1F916}';
|
|
1385
1113
|
var desc=AGENT_DESCRIPTIONS[name.toLowerCase()]||a.tagline||a.description||'';
|
|
1386
|
-
|
|
1387
|
-
h+='<div class="card agent-card" style="position:relative">'+
|
|
1388
|
-
'<div style="flex:1;cursor:pointer" onclick="openAgent(\\''+esc(name)+'\\',\\''+esc(display)+'\\')">'+
|
|
1389
|
-
'<div style="display:flex;align-items:center;gap:8px">'+
|
|
1114
|
+
h+='<div class="card agent-card" onclick="openAgent(\\''+esc(name)+'\\',\\''+esc(display)+'\\')">'+
|
|
1390
1115
|
'<div class="agent-card__icon">'+icon+'</div>'+
|
|
1391
1116
|
'<div class="agent-card__body"><div class="agent-card__name">'+esc(display)+'</div>'+
|
|
1392
1117
|
'<div class="agent-card__tagline">'+esc(desc)+'</div></div>'+
|
|
1393
|
-
'</div></div>'+
|
|
1394
|
-
'<div style="display:flex;gap:4px;flex-shrink:0">'+
|
|
1395
|
-
'<button onclick="editAgent(\\''+esc(name)+'\\')" style="background:none;border:none;cursor:pointer;font-size:12px;padding:2px" title="Edit">\\u{270F}\\u{FE0F}</button>'+
|
|
1396
|
-
'<button onclick="deleteAgent(\\''+esc(name)+'\\')" style="background:none;border:none;cursor:pointer;font-size:12px;padding:2px;color:#f44" title="Delete">\\u{1F5D1}</button>'+
|
|
1397
|
-
'</div>'+
|
|
1398
1118
|
'</div>';
|
|
1399
1119
|
});
|
|
1400
1120
|
h+='</div>';
|
|
1401
1121
|
el.innerHTML=h;
|
|
1402
1122
|
}
|
|
1403
1123
|
var agentFilter=null;
|
|
1404
|
-
|
|
1405
|
-
function showCreateAgentForm(){
|
|
1406
|
-
var name=prompt('Agent name (lowercase, no spaces):');
|
|
1407
|
-
if(!name)return;
|
|
1408
|
-
name=name.toLowerCase().replace(/[^a-z0-9_-]/g,'');
|
|
1409
|
-
if(!name)return;
|
|
1410
|
-
var tagline=prompt('Tagline (short description):');
|
|
1411
|
-
if(!tagline)return;
|
|
1412
|
-
var sysPrompt=prompt('System prompt (agent personality & instructions):');
|
|
1413
|
-
if(!sysPrompt)return;
|
|
1414
|
-
apiPost('/api/agents',{name:name,tagline:tagline,systemPrompt:sysPrompt}).then(function(r){
|
|
1415
|
-
if(r&&r.ok){showToast('success','Agent Created',name.toUpperCase()+' is ready to use');loadAgents().then(function(){renderAgents(document.getElementById('content'))});}
|
|
1416
|
-
else{alert('Error: '+(r&&r.error||'Unknown'));}
|
|
1417
|
-
});
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
function editAgent(name){
|
|
1421
|
-
fetch('/api/agents/'+name).then(function(r){return r.json()}).then(function(data){
|
|
1422
|
-
var newTagline=prompt('Tagline:',data.tagline||'');
|
|
1423
|
-
if(newTagline===null)return;
|
|
1424
|
-
var newPrompt=prompt('System prompt:',data.systemPrompt||'');
|
|
1425
|
-
if(newPrompt===null)return;
|
|
1426
|
-
fetch('/api/agents/'+name,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({tagline:newTagline,systemPrompt:newPrompt,category:data.category||'custom'})}).then(function(r){return r.json()}).then(function(r){
|
|
1427
|
-
if(r&&r.ok){showToast('success','Agent Updated',name.toUpperCase()+' updated');loadAgents().then(function(){renderAgents(document.getElementById('content'))});}
|
|
1428
|
-
else{alert('Error: '+(r&&r.error||'Unknown'));}
|
|
1429
|
-
});
|
|
1430
|
-
});
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
function deleteAgent(name){
|
|
1434
|
-
if(!confirm('Delete agent "'+name+'"? This cannot be undone.'))return;
|
|
1435
|
-
fetch('/api/agents/'+name,{method:'DELETE'}).then(function(r){return r.json()}).then(function(r){
|
|
1436
|
-
if(r&&r.ok){showToast('success','Agent Deleted',name+' removed');loadAgents().then(function(){renderAgents(document.getElementById('content'))});}
|
|
1437
|
-
else{alert('Error: '+(r&&r.error||'Unknown'));}
|
|
1438
|
-
});
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
1124
|
function openAgent(name,display){
|
|
1442
1125
|
selectedAgent=name;
|
|
1443
1126
|
attachedFileContent=null;attachedFileName=null;
|
|
@@ -1520,26 +1203,9 @@ function renderSettings(el) {
|
|
|
1520
1203
|
['summary-time', 'Summary Time', '18:00'],
|
|
1521
1204
|
['meeting-alert', 'Meeting Alert (minutes)', '30'],
|
|
1522
1205
|
]) +
|
|
1523
|
-
'<div class="card" style="margin-top:16px"><div class="card__title">Google Account</div>' +
|
|
1524
|
-
'<div style="font-size:11px;color:var(--dim);margin-bottom:8px">Connect Gmail, Calendar, Drive, Contacts, and Tasks. Opens a browser window for Google sign-in.</div>' +
|
|
1525
|
-
(settingsData.hasGoogle ? '<div style="color:var(--green);font-size:12px;margin-bottom:8px">\\u2705 Connected</div>' : '') +
|
|
1526
|
-
'<button onclick="connectGoogle()" style="background:var(--green3);color:var(--bg);padding:8px 20px;border-radius:var(--r);font-weight:700;font-size:12px;cursor:pointer;border:none">' + (settingsData.hasGoogle ? 'Reconnect Google' : 'Connect Google') + '</button>' +
|
|
1527
|
-
'<div id="googleStatus" style="margin-top:8px;font-size:10px;color:var(--dim)"></div>' +
|
|
1528
|
-
'</div>' +
|
|
1529
1206
|
'</div>';
|
|
1530
1207
|
}
|
|
1531
1208
|
|
|
1532
|
-
function connectGoogle() {
|
|
1533
|
-
var s = document.getElementById('googleStatus');
|
|
1534
|
-
if (s) s.textContent = 'Starting Google sign-in...';
|
|
1535
|
-
apiPost('/api/google/auth', {}).then(function(r) {
|
|
1536
|
-
if (s) s.textContent = r.message || 'Check the browser window that opened.';
|
|
1537
|
-
if (s) s.style.color = 'var(--green)';
|
|
1538
|
-
}).catch(function(e) {
|
|
1539
|
-
if (s) { s.textContent = 'Error: ' + e.message; s.style.color = 'var(--red)'; }
|
|
1540
|
-
});
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
1209
|
function settingsSection(id, title, desc, fields) {
|
|
1544
1210
|
var h = '<form class="card" style="margin-bottom:16px" id="settings-' + id + '" onsubmit="event.preventDefault();saveSettingsSection(\\x27' + id + '\\x27)">' +
|
|
1545
1211
|
'<div class="card__title" style="color:var(--green);font-size:14px;margin-bottom:4px">' + esc(title) + '</div>' +
|
|
@@ -1760,6 +1426,61 @@ function handleDaemonEvent(msg) {
|
|
|
1760
1426
|
showToast('plan', 'Daily Plan Ready', 'Your plan for ' + msg.data.date + ' has been generated.', 10000);
|
|
1761
1427
|
if (currentView === 'plan') renderPlan(document.getElementById('content'));
|
|
1762
1428
|
break;
|
|
1429
|
+
|
|
1430
|
+
case 'cron_result':
|
|
1431
|
+
showToast('cron', 'Scheduled Task', msg.data.prompt + '\\n' + (msg.data.result || '').slice(0, 100), 8000);
|
|
1432
|
+
break;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// ---- CANVAS PANEL ----
|
|
1437
|
+
var canvasVisible = false;
|
|
1438
|
+
function showCanvas(html, title) {
|
|
1439
|
+
var panel = document.getElementById('canvasPanel');
|
|
1440
|
+
if (!panel) {
|
|
1441
|
+
panel = document.createElement('div');
|
|
1442
|
+
panel.id = 'canvasPanel';
|
|
1443
|
+
panel.style.cssText = 'position:fixed;top:60px;right:12px;width:480px;max-height:calc(100vh - 80px);background:#0d0d0d;border:1px solid var(--green);border-radius:12px;z-index:1000;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 0 30px rgba(0,255,65,0.1)';
|
|
1444
|
+
document.body.appendChild(panel);
|
|
1445
|
+
}
|
|
1446
|
+
var header = '<div style="display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border-bottom:1px solid var(--green);background:rgba(0,255,65,0.05)">' +
|
|
1447
|
+
'<span style="font-family:var(--mono);color:var(--green);font-size:12px">' + (title || 'Canvas') + '</span>' +
|
|
1448
|
+
'<div><button onclick="toggleCanvasSize()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px;margin-right:8px" title="Resize">⤢</button>' +
|
|
1449
|
+
'<button onclick="closeCanvas()" style="background:none;border:none;color:var(--dim);cursor:pointer;font-size:14px" title="Close">×</button></div></div>';
|
|
1450
|
+
var iframe = '<iframe id="canvasFrame" sandbox="allow-scripts allow-same-origin" style="flex:1;border:none;background:#fff;min-height:300px;width:100%"></iframe>';
|
|
1451
|
+
panel.innerHTML = header + iframe;
|
|
1452
|
+
panel.style.display = 'flex';
|
|
1453
|
+
canvasVisible = true;
|
|
1454
|
+
|
|
1455
|
+
// Write HTML to iframe
|
|
1456
|
+
var frame = document.getElementById('canvasFrame');
|
|
1457
|
+
if (frame) {
|
|
1458
|
+
var doc = frame.contentDocument || frame.contentWindow.document;
|
|
1459
|
+
doc.open();
|
|
1460
|
+
doc.write(html);
|
|
1461
|
+
doc.close();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
function closeCanvas() {
|
|
1466
|
+
var panel = document.getElementById('canvasPanel');
|
|
1467
|
+
if (panel) { panel.style.display = 'none'; canvasVisible = false; }
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
function toggleCanvasSize() {
|
|
1471
|
+
var panel = document.getElementById('canvasPanel');
|
|
1472
|
+
if (!panel) return;
|
|
1473
|
+
if (panel.style.width === '480px') {
|
|
1474
|
+
panel.style.width = '80vw';
|
|
1475
|
+
panel.style.height = '80vh';
|
|
1476
|
+
panel.style.top = '10vh';
|
|
1477
|
+
panel.style.right = '10vw';
|
|
1478
|
+
} else {
|
|
1479
|
+
panel.style.width = '480px';
|
|
1480
|
+
panel.style.height = '';
|
|
1481
|
+
panel.style.maxHeight = 'calc(100vh - 80px)';
|
|
1482
|
+
panel.style.top = '60px';
|
|
1483
|
+
panel.style.right = '12px';
|
|
1763
1484
|
}
|
|
1764
1485
|
}
|
|
1765
1486
|
|
|
@@ -1865,13 +1586,8 @@ init();
|
|
|
1865
1586
|
<div class="app">
|
|
1866
1587
|
<nav class="sidebar" id="sidebar">
|
|
1867
1588
|
<div class="sidebar__brand">
|
|
1868
|
-
<div
|
|
1869
|
-
|
|
1870
|
-
<span id="wsIndicator" style="color:var(--dim);font-size:8px" title="Daemon WebSocket">●</span>
|
|
1871
|
-
<span style="font-size:9px;color:var(--dim)">v${VERSION}</span>
|
|
1872
|
-
</div>
|
|
1873
|
-
<div id="sidebarPageTitle" style="font-size:11px;color:var(--bright);margin-top:4px;font-weight:600">Dashboard</div>
|
|
1874
|
-
<div class="sidebar__brand-sub" id="clock"></div>
|
|
1589
|
+
<div class="sidebar__brand-name">NHA</div>
|
|
1590
|
+
<div class="sidebar__brand-sub">Operations Console</div>
|
|
1875
1591
|
</div>
|
|
1876
1592
|
<div class="sidebar__section">
|
|
1877
1593
|
<div class="sidebar__label">Overview</div>
|
|
@@ -1908,30 +1624,17 @@ init();
|
|
|
1908
1624
|
<div class="sidebar__label">Config</div>
|
|
1909
1625
|
<div class="nav-item" data-view="settings" onclick="switchView('settings')"><span class="nav-item__icon">⚙</span> Settings</div>
|
|
1910
1626
|
</div>
|
|
1911
|
-
<div
|
|
1912
|
-
<div class="sidebar__label">Help</div>
|
|
1913
|
-
<a href="https://nothumanallowed.com/docs" target="_blank" class="nav-item" style="text-decoration:none"><span class="nav-item__icon">📖</span> Documentation</a>
|
|
1914
|
-
<a href="https://nothumanallowed.com/docs/agents" target="_blank" class="nav-item" style="text-decoration:none"><span class="nav-item__icon">🤖</span> Agents Guide</a>
|
|
1915
|
-
<a href="https://nothumanallowed.com/docs/mobile" target="_blank" class="nav-item" style="text-decoration:none"><span class="nav-item__icon">📱</span> Mobile App</a>
|
|
1916
|
-
</div>
|
|
1917
|
-
<div style="padding:12px 16px;margin-top:auto;border-top:1px solid var(--border);font-size:10px;color:var(--dim)">nothumanallowed.com</div>
|
|
1627
|
+
<div style="padding:12px 16px;margin-top:auto;border-top:1px solid var(--border);font-size:10px;color:var(--dim)">NHA v${VERSION}</div>
|
|
1918
1628
|
</nav>
|
|
1919
1629
|
|
|
1920
|
-
<
|
|
1630
|
+
<div class="header">
|
|
1631
|
+
<button class="header__burger" onclick="toggleSidebar()">☰</button>
|
|
1632
|
+
<span class="header__title" id="headerTitle">Dashboard</span>
|
|
1633
|
+
<span id="wsIndicator" style="color:var(--dim);font-size:8px;margin-right:4px" title="Daemon WebSocket">●</span>
|
|
1634
|
+
<span class="header__clock" id="clock"></span>
|
|
1635
|
+
</div>
|
|
1921
1636
|
|
|
1922
1637
|
<div class="content" id="content"></div>
|
|
1923
|
-
|
|
1924
|
-
<div class="browser-viewer" id="browserViewer">
|
|
1925
|
-
<div class="browser-viewer__header">
|
|
1926
|
-
<span class="browser-viewer__dot"></span>
|
|
1927
|
-
<span class="browser-viewer__title" id="bvTitle">Browser</span>
|
|
1928
|
-
<button class="browser-viewer__close" onclick="closeBrowserViewer()">×</button>
|
|
1929
|
-
</div>
|
|
1930
|
-
<div class="browser-viewer__frame" id="bvFrame">
|
|
1931
|
-
<span style="color:var(--dim);font-size:11px">Waiting...</span>
|
|
1932
|
-
</div>
|
|
1933
|
-
<div class="browser-viewer__status" id="bvStatus">Idle</div>
|
|
1934
|
-
</div>
|
|
1935
1638
|
</div>
|
|
1936
1639
|
|
|
1937
1640
|
<div class="modal-overlay" id="agentModal">
|