nothumanallowed 4.1.0 → 6.0.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.
@@ -151,6 +151,29 @@ input:focus,textarea:focus{border-color:var(--green3)}
151
151
  /* ---- SPINNER ---- */
152
152
  .spinner{width:24px;height:24px;border:2px solid var(--border);border-top-color:var(--green);border-radius:50%;animation:spin .6s linear infinite;margin:0 auto 12px}
153
153
  @keyframes spin{to{transform:rotate(360deg)}}
154
+
155
+ /* ---- TOASTS (real-time notifications) ---- */
156
+ .toast-container{position:fixed;top:16px;right:16px;z-index:500;display:flex;flex-direction:column;gap:8px;pointer-events:none}
157
+ .toast{background:var(--bg3);border:1px solid var(--green);border-radius:8px;padding:12px 16px;max-width:320px;animation:toastIn .3s;pointer-events:auto;cursor:pointer;box-shadow:0 4px 20px rgba(0,0,0,0.5)}
158
+ .toast--email{border-color:var(--cyan)}
159
+ .toast--meeting{border-color:var(--amber)}
160
+ .toast--security{border-color:var(--red)}
161
+ .toast--plan{border-color:var(--green)}
162
+ .toast__title{font-size:11px;font-weight:700;margin-bottom:3px}
163
+ .toast--email .toast__title{color:var(--cyan)}
164
+ .toast--meeting .toast__title{color:var(--amber)}
165
+ .toast--security .toast__title{color:var(--red)}
166
+ .toast--plan .toast__title{color:var(--green)}
167
+ .toast__body{font-size:11px;color:var(--text);line-height:1.4}
168
+ .toast--fadeout{animation:toastOut .3s forwards}
169
+ @keyframes toastIn{from{transform:translateX(100%);opacity:0}to{transform:none;opacity:1}}
170
+ @keyframes toastOut{from{opacity:1}to{opacity:0;transform:translateX(40px)}}
171
+
172
+ /* ---- VOICE MIC BUTTON (in chat bar) ---- */
173
+ .chat__mic{background:var(--bg3);color:var(--green);border:1px solid var(--border2);width:40px;height:40px;border-radius:var(--r);display:flex;align-items:center;justify-content:center;font-size:18px;cursor:pointer;flex-shrink:0;transition:all .2s}
174
+ .chat__mic:hover{border-color:var(--green3);background:var(--greendim)}
175
+ .chat__mic--recording{border-color:var(--red);color:var(--red);background:rgba(255,23,68,0.1);animation:micPulse 1.5s ease-in-out infinite}
176
+ @keyframes micPulse{0%,100%{box-shadow:none}50%{box-shadow:0 0 0 6px rgba(255,23,68,0.2)}}
154
177
  `;
155
178
 
156
179
  // JS as clean block
@@ -256,7 +279,7 @@ function renderDash(el){
256
279
  var chatReady=false;
257
280
  function renderChat(el){
258
281
  if(!chatReady||!document.getElementById('chatMessages')){
259
- el.innerHTML='<div class="chat"><div class="chat__messages" id="chatMessages"></div><div class="chat__bar"><textarea class="chat__input" id="chatInput" placeholder="Ask anything..." rows="1"></textarea><button class="chat__send" id="chatSend">Send</button></div></div>';
282
+ 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">&#127908;</button><textarea class="chat__input" id="chatInput" placeholder="Ask anything..." rows="1"></textarea><button class="chat__send" id="chatSend">Send</button></div></div>';
260
283
  chatReady=true;
261
284
  document.getElementById('chatSend').onclick=sendChat;
262
285
  document.getElementById('chatInput').onkeydown=function(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendChat()}};
@@ -644,6 +667,165 @@ function askAgent(){
644
667
  });
645
668
  }
646
669
 
670
+ // ---- TOASTS (real-time notification overlay) ----
671
+ function showToast(type, title, body, durationMs) {
672
+ var container = document.getElementById('toastContainer');
673
+ if (!container) return;
674
+ var toast = document.createElement('div');
675
+ toast.className = 'toast toast--' + type;
676
+ toast.innerHTML = '<div class="toast__title">' + esc(title) + '</div><div class="toast__body">' + esc(body) + '</div>';
677
+ toast.onclick = function() {
678
+ toast.classList.add('toast--fadeout');
679
+ setTimeout(function() { toast.remove(); }, 300);
680
+ // Navigate to relevant view
681
+ if (type === 'email') switchView('emails');
682
+ else if (type === 'meeting') switchView('calendar');
683
+ else if (type === 'plan') switchView('plan');
684
+ };
685
+ container.appendChild(toast);
686
+ // Auto-remove after duration
687
+ setTimeout(function() {
688
+ if (toast.parentNode) {
689
+ toast.classList.add('toast--fadeout');
690
+ setTimeout(function() { toast.remove(); }, 300);
691
+ }
692
+ }, durationMs || 8000);
693
+ }
694
+
695
+ // ---- WEBSOCKET (connect to daemon for real-time events) ----
696
+ var ws = null;
697
+ var wsReconnectTimer = null;
698
+ function connectWebSocket() {
699
+ try {
700
+ ws = new WebSocket('ws://127.0.0.1:3848');
701
+ } catch(e) { return; }
702
+
703
+ ws.onopen = function() {
704
+ var indicator = document.getElementById('wsIndicator');
705
+ if (indicator) indicator.style.color = 'var(--green)';
706
+ };
707
+
708
+ ws.onmessage = function(event) {
709
+ try {
710
+ var msg = JSON.parse(event.data);
711
+ handleDaemonEvent(msg);
712
+ } catch(e) {}
713
+ };
714
+
715
+ ws.onclose = function() {
716
+ var indicator = document.getElementById('wsIndicator');
717
+ if (indicator) indicator.style.color = 'var(--dim)';
718
+ ws = null;
719
+ // Reconnect after 10 seconds
720
+ if (!wsReconnectTimer) {
721
+ wsReconnectTimer = setTimeout(function() {
722
+ wsReconnectTimer = null;
723
+ connectWebSocket();
724
+ }, 10000);
725
+ }
726
+ };
727
+
728
+ ws.onerror = function() {
729
+ try { ws.close(); } catch(e) {}
730
+ };
731
+ }
732
+
733
+ function handleDaemonEvent(msg) {
734
+ switch(msg.type) {
735
+ case 'new_email':
736
+ showToast('email', 'New Email', 'From: ' + (msg.data.from || 'unknown') + '\\n' + (msg.data.subject || ''), 10000);
737
+ // Auto-refresh emails
738
+ loadDash().then(function() {
739
+ if (currentView === 'dashboard' || currentView === 'emails') render();
740
+ }).catch(function(){});
741
+ break;
742
+
743
+ case 'meeting_alert':
744
+ showToast('meeting', 'Meeting in ' + msg.data.minutesUntil + ' min', msg.data.summary + (msg.data.location ? '\\n@ ' + msg.data.location : ''), 15000);
745
+ // Auto-refresh calendar
746
+ loadDash().then(function() {
747
+ if (currentView === 'dashboard' || currentView === 'calendar') render();
748
+ }).catch(function(){});
749
+ break;
750
+
751
+ case 'security_alert':
752
+ showToast('security', 'Security Alert', 'Suspicious: ' + (msg.data.from || '') + ' — ' + (msg.data.subject || ''), 20000);
753
+ break;
754
+
755
+ case 'plan_ready':
756
+ showToast('plan', 'Daily Plan Ready', 'Your plan for ' + msg.data.date + ' has been generated.', 10000);
757
+ if (currentView === 'plan') renderPlan(document.getElementById('content'));
758
+ break;
759
+ }
760
+ }
761
+
762
+ // ---- VOICE INPUT (in chat view) ----
763
+ var voiceRecognition = null;
764
+ var voiceRecording = false;
765
+
766
+ function toggleVoiceInput() {
767
+ if (voiceRecording) {
768
+ stopVoiceInput();
769
+ } else {
770
+ startVoiceInput();
771
+ }
772
+ }
773
+
774
+ function startVoiceInput() {
775
+ var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
776
+ if (!SpeechRecognition) {
777
+ showToast('security', 'Not Supported', 'Speech Recognition requires Chrome or Edge.', 5000);
778
+ return;
779
+ }
780
+
781
+ voiceRecording = true;
782
+ var mic = document.getElementById('chatMic');
783
+ if (mic) mic.classList.add('chat__mic--recording');
784
+
785
+ voiceRecognition = new SpeechRecognition();
786
+ voiceRecognition.continuous = false;
787
+ voiceRecognition.interimResults = false;
788
+ voiceRecognition.lang = navigator.language || 'en-US';
789
+
790
+ voiceRecognition.onresult = function(event) {
791
+ var transcript = '';
792
+ for (var i = 0; i < event.results.length; i++) {
793
+ transcript += event.results[i][0].transcript;
794
+ }
795
+ if (transcript.trim()) {
796
+ var inp = document.getElementById('chatInput');
797
+ if (inp) {
798
+ inp.value = transcript.trim();
799
+ sendChat();
800
+ }
801
+ }
802
+ stopVoiceInput();
803
+ };
804
+
805
+ voiceRecognition.onerror = function(e) {
806
+ if (e.error !== 'no-speech') {
807
+ showToast('security', 'Voice Error', e.error, 4000);
808
+ }
809
+ stopVoiceInput();
810
+ };
811
+
812
+ voiceRecognition.onend = function() {
813
+ stopVoiceInput();
814
+ };
815
+
816
+ voiceRecognition.start();
817
+ }
818
+
819
+ function stopVoiceInput() {
820
+ voiceRecording = false;
821
+ var mic = document.getElementById('chatMic');
822
+ if (mic) mic.classList.remove('chat__mic--recording');
823
+ if (voiceRecognition) {
824
+ try { voiceRecognition.stop(); } catch(e) {}
825
+ voiceRecognition = null;
826
+ }
827
+ }
828
+
647
829
  // ---- INIT ----
648
830
  function init(){
649
831
  var el=document.getElementById('content');
@@ -651,6 +833,8 @@ function init(){
651
833
  loadDash().then(function(){render()}).catch(function(){render()});
652
834
  loadAgents().catch(function(){});
653
835
  setInterval(function(){loadDash().then(function(){if(currentView==='dashboard')render()}).catch(function(){})},120000);
836
+ // Connect to daemon WebSocket for real-time notifications
837
+ connectWebSocket();
654
838
  }
655
839
  init();
656
840
  `;
@@ -671,6 +855,7 @@ init();
671
855
  </head>
672
856
  <body>
673
857
 
858
+ <div class="toast-container" id="toastContainer"></div>
674
859
  <div class="sidebar__overlay" id="overlay" onclick="closeSidebar()"></div>
675
860
 
676
861
  <div class="app">
@@ -700,6 +885,7 @@ init();
700
885
  <div class="header">
701
886
  <button class="header__burger" onclick="toggleSidebar()">&#9776;</button>
702
887
  <span class="header__title" id="headerTitle">Dashboard</span>
888
+ <span id="wsIndicator" style="color:var(--dim);font-size:8px;margin-right:4px" title="Daemon WebSocket">&#9679;</span>
703
889
  <span class="header__clock" id="clock"></span>
704
890
  </div>
705
891