logs.html 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. <!DOCTYPE html><html><head>
  2. <meta charset='UTF-8'>
  3. <meta name='viewport' content='width=device-width,initial-scale=1'>
  4. <title>System Logs</title>
  5. <style>
  6. *{box-sizing:border-box;margin:0;padding:0}
  7. body{font-family:-apple-system,system-ui,sans-serif;background:#0d1117;color:#c9d1d9;min-height:100vh;display:flex;flex-direction:column}
  8. .toolbar{background:#161b22;padding:10px 16px;display:flex;align-items:center;gap:12px;border-bottom:1px solid #30363d}
  9. .toolbar h1{font-size:16px;font-weight:600;flex:1}
  10. .toolbar a{color:#58a6ff;text-decoration:none;font-size:13px}
  11. .dot{width:10px;height:10px;border-radius:50%;background:#f85149}
  12. .dot.ok{background:#3fb950}
  13. .btn-sm{padding:6px 12px;border:1px solid #30363d;border-radius:6px;background:#21262d;color:#c9d1d9;font-size:12px;cursor:pointer}
  14. .btn-sm:hover{background:#30363d}
  15. #log{flex:1;overflow-y:auto;padding:12px 16px;font-family:'SF Mono',Consolas,monospace;font-size:12px;line-height:1.6;white-space:pre-wrap;word-break:break-all}
  16. .a30{color:#484f58}.a31{color:#f85149}.a32{color:#3fb950}.a33{color:#d29922}.a34{color:#58a6ff}.a35{color:#bc8cff}.a36{color:#39c5cf}.a37{color:#c9d1d9}
  17. .a90{color:#6e7681}.a91{color:#ff7b72}.a92{color:#56d364}.a93{color:#e3b341}.a94{color:#79c0ff}.a95{color:#d2a8ff}.a96{color:#56d4dd}.a97{color:#f0f6fc}
  18. </style></head><body>
  19. <div class='toolbar'>
  20. <a href='/'>&larr; Back</a>
  21. <h1>System Logs</h1>
  22. <span id='dot' class='dot'></span>
  23. <button class='btn-sm' onclick='clear_log()'>Clear</button>
  24. <button class='btn-sm' id='scroll-btn' onclick='toggle_scroll()'>Auto-scroll: ON</button>
  25. </div>
  26. <div id='log'><div id='anchor'></div></div>
  27. <script>
  28. var log=document.getElementById('log'),dot=document.getElementById('dot'),anchor=document.getElementById('anchor'),autoScroll=true,maxLines=2000;
  29. function toggle_scroll(){autoScroll=!autoScroll;document.getElementById('scroll-btn').textContent='Auto-scroll: '+(autoScroll?'ON':'OFF');}
  30. function clear_log(){log.innerHTML='';anchor=document.createElement('div');anchor.id='anchor';log.appendChild(anchor);}
  31. var ansiRe=/\x1b\[([0-9;]*)m/g;
  32. var ansiCls={'30':1,'31':1,'32':1,'33':1,'34':1,'35':1,'36':1,'37':1,'90':1,'91':1,'92':1,'93':1,'94':1,'95':1,'96':1,'97':1};
  33. var letterCls={'E':'a31','W':'a33','I':'a32','D':'a30','V':'a30'};
  34. function colorize(text){
  35. var frag=document.createDocumentFragment();
  36. text.split('\n').forEach(function(line){
  37. if(!line)return;
  38. var wrap=document.createElement('span');
  39. var hasAnsi=ansiRe.test(line);
  40. ansiRe.lastIndex=0;
  41. if(hasAnsi){
  42. var last=0,cls=null,m;
  43. while((m=ansiRe.exec(line))!==null){
  44. if(m.index>last){
  45. var s=document.createElement('span');
  46. if(cls)s.className=cls;
  47. s.textContent=line.substring(last,m.index);
  48. wrap.appendChild(s);
  49. }
  50. var p=m[1].split(';');cls=null;
  51. for(var i=0;i<p.length;i++){if(ansiCls[p[i]])cls='a'+p[i];}
  52. last=ansiRe.lastIndex;
  53. }
  54. if(last<line.length){
  55. var s=document.createElement('span');
  56. if(cls)s.className=cls;
  57. s.textContent=line.substring(last);
  58. wrap.appendChild(s);
  59. }
  60. }else{
  61. var ch=line.charAt(0);
  62. if(letterCls[ch])wrap.className=letterCls[ch];
  63. wrap.textContent=line;
  64. }
  65. wrap.appendChild(document.createTextNode('\n'));
  66. frag.appendChild(wrap);
  67. });
  68. log.insertBefore(frag,anchor);
  69. while(log.childNodes.length>maxLines+1)log.removeChild(log.firstChild);
  70. if(autoScroll)anchor.scrollIntoView();
  71. }
  72. function connect(){
  73. var ws=new WebSocket('ws://'+location.host+'/ws/logs');
  74. ws.onopen=function(){dot.classList.add('ok');};
  75. ws.onclose=function(){dot.classList.remove('ok');setTimeout(connect,2000);};
  76. ws.onmessage=function(e){colorize(e.data);};
  77. }
  78. connect();
  79. </script></body></html>