nothumanallowed 3.5.0 → 3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "NotHumanAllowed — 38 AI agents for security, code, DevOps, data & daily ops. Ask agents directly, plan your day with 5 specialist agents, manage tasks, connect Gmail + Calendar.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -132,7 +132,7 @@ input:focus,textarea:focus{border-color:var(--green3)}
132
132
  /* ---- MODAL ---- */
133
133
  .modal-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:300;align-items:center;justify-content:center}
134
134
  .modal-overlay--open{display:flex}
135
- .modal{background:var(--bg2);border:1px solid var(--border2);border-radius:8px;width:90%;max-width:500px;max-height:80vh;display:flex;flex-direction:column}
135
+ .modal{background:var(--bg2);border:1px solid var(--border2);border-radius:8px;width:92%;max-width:540px;max-height:90vh;display:flex;flex-direction:column}
136
136
  .modal__header{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid var(--border)}
137
137
  .modal__header h2{font-size:16px;color:var(--green)}
138
138
  .modal__close{background:none;color:var(--dim);font-size:24px;padding:0 4px}
@@ -342,66 +342,134 @@ function renderEmails(el){
342
342
  el.innerHTML=h;
343
343
  }
344
344
 
345
- // ---- CALENDAR ----
346
- var calOffset = 0;
347
- function renderCalendar(el) {
348
- var today = new Date();
349
- var target = new Date(today);
350
- target.setDate(today.getDate() + calOffset);
351
- var yyyy = target.getFullYear();
352
- var mm = String(target.getMonth() + 1).padStart(2, '0');
353
- var dd = String(target.getDate()).padStart(2, '0');
354
- var dateStr = yyyy + '-' + mm + '-' + dd;
355
- var dayName = target.toLocaleDateString('en', { weekday: 'long', month: 'long', day: 'numeric' });
356
- var isToday = calOffset === 0;
357
-
358
- var h = '<div style="display:flex;align-items:center;gap:8px;margin-bottom:16px;flex-wrap:wrap">' +
359
- '<button class="btn btn--secondary" onclick="calOffset--;renderCalendar(document.getElementById(\\x27content\\x27))">&larr; Prev</button>' +
360
- '<div style="flex:1;text-align:center"><div style="font-size:16px;font-weight:700;color:var(--bright)">' + esc(dayName) + '</div>' +
361
- '<div style="font-size:11px;color:var(--dim)">' + dateStr + (isToday ? ' (today)' : '') + '</div></div>' +
362
- '<button class="btn btn--secondary" onclick="calOffset++;renderCalendar(document.getElementById(\\x27content\\x27))">Next &rarr;</button>' +
363
- '</div>';
364
-
365
- if (!isToday) {
366
- h += '<div style="text-align:center;margin-bottom:12px"><button class="btn btn--secondary" onclick="calOffset=0;renderCalendar(document.getElementById(\\x27content\\x27))">Go to Today</button></div>';
345
+ // ---- CALENDAR (monthly grid + day detail modal) ----
346
+ var calYear, calMonth;
347
+ var calEventsCache = {};
348
+ (function(){var d=new Date();calYear=d.getFullYear();calMonth=d.getMonth()})();
349
+
350
+ function calKey(y,m,d){return y+'-'+String(m+1).padStart(2,'0')+'-'+String(d).padStart(2,'0')}
351
+ function isToday(y,m,d){var t=new Date();return t.getFullYear()===y&&t.getMonth()===m&&t.getDate()===d}
352
+
353
+ function renderCalendar(el){
354
+ var firstDay=new Date(calYear,calMonth,1).getDay();
355
+ var daysInMonth=new Date(calYear,calMonth+1,0).getDate();
356
+ var monthName=new Date(calYear,calMonth,1).toLocaleDateString('en',{month:'long',year:'numeric'});
357
+ // Adjust so Monday=0
358
+ var startDay=(firstDay+6)%7;
359
+
360
+ var h='<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px">'+
361
+ '<button class="btn btn--secondary" onclick="calPrev()">&larr;</button>'+
362
+ '<div style="flex:1;text-align:center;font-size:16px;font-weight:700;color:var(--bright)">'+esc(monthName)+'</div>'+
363
+ '<button class="btn btn--secondary" onclick="calNext()">&rarr;</button>'+
364
+ '</div>';
365
+
366
+ // Day headers
367
+ h+='<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:2px;margin-bottom:4px">';
368
+ ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'].forEach(function(d){
369
+ h+='<div style="text-align:center;font-size:10px;color:var(--dim);padding:4px">'+d+'</div>';
370
+ });
371
+ h+='</div>';
372
+
373
+ // Calendar grid
374
+ h+='<div style="display:grid;grid-template-columns:repeat(7,1fr);gap:2px">';
375
+ // Empty cells before first day
376
+ for(var i=0;i<startDay;i++){h+='<div style="min-height:48px;background:var(--bg);border-radius:4px"></div>'}
377
+ // Day cells
378
+ for(var d=1;d<=daysInMonth;d++){
379
+ var key=calKey(calYear,calMonth,d);
380
+ var today=isToday(calYear,calMonth,d);
381
+ var evts=calEventsCache[key]||[];
382
+ var hasDot=evts.length>0;
383
+ h+='<div onclick="openDayDetail(\\x27'+key+'\\x27)" style="min-height:48px;background:'+(today?'var(--greendim)':'var(--bg2)')+';border:1px solid '+(today?'var(--green3)':'var(--border)')+';border-radius:4px;padding:4px;cursor:pointer;position:relative">';
384
+ h+='<div style="font-size:12px;font-weight:'+(today?'700':'400')+';color:'+(today?'var(--green)':'var(--text)')+'">'+d+'</div>';
385
+ if(hasDot){
386
+ h+='<div style="font-size:8px;color:var(--amber);margin-top:2px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis">'+esc(evts[0].summary)+'</div>';
387
+ if(evts.length>1)h+='<div style="font-size:8px;color:var(--dim)">+'+String(evts.length-1)+' more</div>';
388
+ }
389
+ h+='</div>';
367
390
  }
391
+ h+='</div>';
392
+
393
+ // Loading indicator
394
+ h+='<div id="calLoading" style="text-align:center;padding:8px;color:var(--dim);font-size:10px">Loading events...</div>';
368
395
 
369
- h += '<div id="calEvents"><div style="text-align:center;padding:30px"><div class="spinner"></div><div style="color:var(--dim);margin-top:8px">Loading events...</div></div></div>';
370
- el.innerHTML = h;
396
+ el.innerHTML=h;
397
+ loadMonthEvents();
398
+ }
371
399
 
372
- apiGet('/api/calendar?date=' + dateStr).then(function(r) {
373
- var ev = (r && r.events) || [];
374
- var ce = document.getElementById('calEvents');
375
- if (!ce) return;
376
- if (ev.length === 0) {
377
- ce.innerHTML = '<div class="card" style="text-align:center;color:var(--dim);padding:30px">No events on ' + esc(dayName) + '</div>';
378
- return;
400
+ function loadMonthEvents(){
401
+ var daysInMonth=new Date(calYear,calMonth+1,0).getDate();
402
+ var promises=[];
403
+ for(var d=1;d<=daysInMonth;d++){
404
+ var key=calKey(calYear,calMonth,d);
405
+ if(!calEventsCache[key]){
406
+ (function(k,day){
407
+ promises.push(apiGet('/api/calendar?date='+k).then(function(r){
408
+ calEventsCache[k]=(r&&r.events)||[];
409
+ }));
410
+ })(key,d);
379
411
  }
380
- var out = '<div style="color:var(--cyan);font-size:11px;margin-bottom:8px">' + ev.length + ' event' + (ev.length > 1 ? 's' : '') + '</div>';
381
- ev.forEach(function(x) {
382
- var timeStr = x.isAllDay ? 'All day' : fmtTime(x.start) + '' + fmtTime(x.end);
383
- out += '<div class="card" style="padding:14px">' +
384
- '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">' +
385
- '<span style="color:var(--amber);font-weight:700;font-size:13px">' + esc(timeStr) + '</span>' +
386
- '</div>' +
387
- '<div style="color:var(--bright);font-size:14px;font-weight:600;margin-bottom:4px">' + esc(x.summary) + '</div>';
388
- if (x.location) {
389
- out += '<div style="color:var(--dim);font-size:11px">📍 ' + esc(x.location) + '</div>';
412
+ }
413
+ if(promises.length===0){
414
+ var li=document.getElementById('calLoading');if(li)li.style.display='none';
415
+ return;
416
+ }
417
+ Promise.all(promises).then(function(){
418
+ var li=document.getElementById('calLoading');if(li)li.style.display='none';
419
+ // Re-render just the grid cells with events
420
+ renderCalendar(document.getElementById('content'));
421
+ });
422
+ }
423
+
424
+ function calPrev(){calMonth--;if(calMonth<0){calMonth=11;calYear--}renderCalendar(document.getElementById('content'))}
425
+ function calNext(){calMonth++;if(calMonth>11){calMonth=0;calYear++}renderCalendar(document.getElementById('content'))}
426
+
427
+ function openDayDetail(dateStr){
428
+ var evts=calEventsCache[dateStr]||[];
429
+ var dayLabel=new Date(dateStr+'T12:00:00').toLocaleDateString('en',{weekday:'long',month:'long',day:'numeric',year:'numeric'});
430
+
431
+ var h='<h2 style="color:var(--green);margin-bottom:4px">'+esc(dayLabel)+'</h2>';
432
+ h+='<div style="color:var(--dim);font-size:11px;margin-bottom:12px">'+dateStr+'</div>';
433
+
434
+ if(evts.length===0){
435
+ h+='<div style="color:var(--dim);padding:20px;text-align:center">No events on this day</div>';
436
+ } else {
437
+ evts.forEach(function(x){
438
+ var timeStr=x.isAllDay?'All day':fmtTime(x.start)+' - '+fmtTime(x.end);
439
+ h+='<div style="border:1px solid var(--border);border-radius:6px;padding:12px;margin-bottom:10px;background:var(--bg3)">';
440
+ h+='<div style="color:var(--amber);font-weight:700;font-size:13px;margin-bottom:4px">'+esc(timeStr)+'</div>';
441
+ h+='<div style="color:var(--bright);font-size:15px;font-weight:700;margin-bottom:6px">'+esc(x.summary)+'</div>';
442
+ if(x.location)h+='<div style="color:var(--cyan);font-size:12px;margin-bottom:4px">Location: '+esc(x.location)+'</div>';
443
+ if(x.organizer)h+='<div style="color:var(--dim);font-size:11px;margin-bottom:4px">Organizer: '+esc(x.organizer)+'</div>';
444
+ if(x.attendees&&x.attendees.length>0){
445
+ h+='<div style="color:var(--dim);font-size:11px;margin-bottom:4px">Attendees:</div>';
446
+ x.attendees.forEach(function(a){
447
+ var status=a.responseStatus==='accepted'?'var(--green)':a.responseStatus==='declined'?'var(--red)':'var(--dim)';
448
+ h+='<div style="font-size:11px;color:'+status+';padding-left:8px">'+esc(a.name||a.email)+' ('+esc(a.responseStatus)+')</div>';
449
+ });
390
450
  }
391
- if (x.attendees && x.attendees.length > 0) {
392
- var names = x.attendees.map(function(a) { return a.name || a.email }).join(', ');
393
- out += '<div style="color:var(--dim);font-size:11px;margin-top:4px">👥 ' + esc(names) + '</div>';
451
+ if(x.description){
452
+ h+='<div style="border-top:1px solid var(--border);margin-top:8px;padding-top:8px;color:var(--text);font-size:12px;white-space:pre-wrap;word-wrap:break-word">'+esc(x.description)+'</div>';
394
453
  }
395
- if (x.description) {
396
- out += '<div style="color:var(--dim);font-size:11px;margin-top:4px;border-top:1px solid var(--border);padding-top:6px">' + esc(x.description.slice(0, 200)) + '</div>';
454
+ if(x.hangoutLink){
455
+ h+='<div style="margin-top:8px"><a href="'+esc(x.hangoutLink)+'" target="_blank" style="color:var(--cyan);font-size:12px;font-weight:700">Join Video Call</a></div>';
397
456
  }
398
- if (x.hangoutLink) {
399
- out += '<div style="margin-top:6px"><a href="' + esc(x.hangoutLink) + '" target="_blank" style="color:var(--cyan);font-size:11px">🔗 Join video call</a></div>';
457
+ if(x.htmlLink){
458
+ h+='<div style="margin-top:4px"><a href="'+esc(x.htmlLink)+'" target="_blank" style="color:var(--dim);font-size:10px">Open in Google Calendar</a></div>';
400
459
  }
401
- out += '</div>';
460
+ h+='</div>';
402
461
  });
403
- ce.innerHTML = out;
404
- });
462
+ }
463
+
464
+ // Use the agent modal for day detail
465
+ document.getElementById('modalName').textContent=dayLabel;
466
+ document.getElementById('modalPrompt').style.display='none';
467
+ document.getElementById('modalResponse').style.display='block';
468
+ document.getElementById('modalResponse').innerHTML=h;
469
+ document.getElementById('agentModal').classList.add('modal-overlay--open');
470
+ // Hide ask button
471
+ var sendBtn=document.getElementById('agentModal').querySelector('.btn--primary');
472
+ if(sendBtn)sendBtn.style.display='none';
405
473
  }
406
474
 
407
475
  // ---- AGENTS ----
@@ -418,8 +486,12 @@ function openAgent(name,display){
418
486
  selectedAgent=name;
419
487
  document.getElementById('modalName').textContent=display||name;
420
488
  document.getElementById('modalPrompt').value='';
489
+ document.getElementById('modalPrompt').style.display='';
421
490
  document.getElementById('modalResponse').style.display='none';
422
491
  document.getElementById('modalResponse').textContent='';
492
+ document.getElementById('modalResponse').innerHTML='';
493
+ var sendBtn=document.getElementById('agentModal').querySelector('.btn--primary');
494
+ if(sendBtn)sendBtn.style.display='';
423
495
  document.getElementById('agentModal').classList.add('modal-overlay--open');
424
496
  }
425
497
  function closeModal(){