| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>JARVIS β Neural Intelligence Interface v2</title> |
| <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700;900&family=Rajdhani:wght@300;400;500;600&family=Share+Tech+Mono&display=swap" rel="stylesheet"> |
| |
| <script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script> |
| <script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script> |
| <script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script> |
| <style> |
| :root { |
| --void:#00000a;--deep:#020510;--panel:rgba(0,15,40,0.75);--glass:rgba(0,180,255,0.04); |
| --cyan:#00d4ff;--cyan2:#00aaff;--gold:#f0a500;--gold2:#ffcc44; |
| --green:#00ff88;--red:#ff3366;--white:#e8f4ff;--dim:rgba(0,212,255,0.4); |
| --border:rgba(0,212,255,0.15);--border2:rgba(0,212,255,0.08); |
| --purple:#cc66ff;--orange:#ff6644; |
| --glow-c:0 0 20px rgba(0,212,255,.5),0 0 60px rgba(0,212,255,.2); |
| --glow-g:0 0 20px rgba(240,165,0,.5),0 0 60px rgba(240,165,0,.2); |
| --font-d:'Orbitron',monospace;--font-b:'Rajdhani',sans-serif;--font-m:'Share Tech Mono',monospace; |
| } |
| *{margin:0;padding:0;box-sizing:border-box;} |
| body{background:var(--void);color:var(--white);font-family:var(--font-b);min-height:100vh;overflow-x:hidden;cursor:crosshair;} |
| body::after{content:'';position:fixed;inset:0;z-index:999;pointer-events:none; |
| background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,0,0,.025) 2px,rgba(0,0,0,.025) 4px);} |
| #starfield{position:fixed;inset:0;z-index:0;pointer-events:none;} |
| |
| |
| #app{position:relative;z-index:1;display:grid;grid-template-rows:60px 1fr;grid-template-columns:260px 1fr 260px;height:100vh;} |
| |
| |
| header{grid-column:1/-1;display:flex;align-items:center;justify-content:space-between;padding:0 20px; |
| border-bottom:1px solid var(--border);background:rgba(0,5,20,.95);backdrop-filter:blur(20px);z-index:100;position:relative;} |
| header::after{content:'';position:absolute;bottom:-2px;left:0;right:0;height:1px; |
| background:linear-gradient(90deg,transparent,var(--cyan),var(--gold),var(--cyan),transparent); |
| animation:hglow 3s ease-in-out infinite;} |
| @keyframes hglow{0%,100%{opacity:.4}50%{opacity:1}} |
| .logo{font-family:var(--font-d);font-size:20px;font-weight:900;letter-spacing:8px; |
| background:linear-gradient(135deg,var(--cyan),var(--gold));-webkit-background-clip:text; |
| -webkit-text-fill-color:transparent;background-clip:text;filter:drop-shadow(0 0 10px rgba(0,212,255,.5));} |
| .hcenter{display:flex;align-items:center;gap:24px;} |
| .sdot{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 10px var(--green);animation:pulse 2s ease-in-out infinite;} |
| @keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.8)}} |
| .stext{font-family:var(--font-m);font-size:10px;color:var(--green);letter-spacing:2px;text-transform:uppercase;} |
| .htime{font-family:var(--font-m);font-size:12px;color:var(--dim);letter-spacing:1px;} |
| .hright{display:flex;align-items:center;gap:10px;} |
| .hbtn{font-family:var(--font-d);font-size:8px;letter-spacing:2px;padding:5px 12px;border:1px solid var(--border); |
| background:var(--glass);color:var(--dim);cursor:pointer;transition:all .3s;text-transform:uppercase;} |
| .hbtn:hover,.hbtn.on{border-color:var(--cyan);color:var(--cyan);background:rgba(0,212,255,.08);box-shadow:var(--glow-c);} |
| .hbtn.danger{border-color:rgba(255,51,102,.3);color:rgba(255,51,102,.6);} |
| .hbtn.danger:hover{border-color:var(--red);color:var(--red);} |
| |
| |
| .pleft,.pright{border-right:1px solid var(--border2);background:rgba(0,8,25,.65);backdrop-filter:blur(10px); |
| padding:16px 14px;display:flex;flex-direction:column;gap:14px;overflow-y:auto;} |
| .pright{border-right:none;border-left:1px solid var(--border2);} |
| |
| |
| .w{border:1px solid var(--border2);background:var(--panel);padding:12px;position:relative;overflow:hidden;} |
| .w::before{content:'';position:absolute;top:0;left:0;right:0;height:1px; |
| background:linear-gradient(90deg,transparent,var(--cyan),transparent);opacity:.35;} |
| .wtitle{font-family:var(--font-d);font-size:7.5px;letter-spacing:3px;color:var(--dim);text-transform:uppercase; |
| margin-bottom:10px;display:flex;align-items:center;gap:7px;} |
| .wtitle::before{content:'';width:3px;height:9px;background:var(--cyan);box-shadow:0 0 6px var(--cyan);} |
| .wtitle.gold::before{background:var(--gold);box-shadow:0 0 6px var(--gold);} |
| .wtitle.red::before{background:var(--red);box-shadow:0 0 6px var(--red);} |
| .wtitle.purple::before{background:var(--purple);box-shadow:0 0 6px var(--purple);} |
| |
| |
| .mrow{display:flex;justify-content:space-between;align-items:center;padding:5px 0;border-bottom:1px solid var(--border2);} |
| .mrow:last-child{border:none;} |
| .mlabel{font-family:var(--font-m);font-size:9px;color:rgba(232,244,255,.4);letter-spacing:1px;} |
| .mval{font-family:var(--font-m);font-size:11px;color:var(--cyan);} |
| .mval.g{color:var(--green)}.mval.gold{color:var(--gold)}.mval.r{color:var(--red)}.mval.p{color:var(--purple)} |
| |
| |
| .brow{display:flex;align-items:center;gap:7px;margin-bottom:5px;} |
| .blabel{font-family:var(--font-m);font-size:8px;color:rgba(232,244,255,.4);width:55px;text-align:right;flex-shrink:0;} |
| .btrack{flex:1;height:3px;background:rgba(0,212,255,.08);position:relative;overflow:hidden;} |
| .bfill{height:100%;background:linear-gradient(90deg,var(--cyan2),var(--cyan));box-shadow:0 0 6px var(--cyan); |
| animation:bin 1.5s ease-out forwards;transform-origin:left;} |
| @keyframes bin{from{transform:scaleX(0)}to{transform:scaleX(1)}} |
| .bval{font-family:var(--font-m);font-size:8px;color:var(--dim);width:26px;flex-shrink:0;} |
| |
| |
| .litem{padding:5px 0;border-bottom:1px solid var(--border2);display:flex;flex-direction:column;gap:2px;} |
| .litem:last-child{border:none;} |
| .ltime{font-family:var(--font-m);font-size:8px;color:var(--gold);opacity:.7;} |
| .ltext{font-size:10px;color:rgba(232,244,255,.55);line-height:1.4;} |
| |
| |
| .citem{display:flex;align-items:center;gap:9px;padding:6px 0;border-bottom:1px solid var(--border2);cursor:pointer;transition:all .2s;} |
| .citem:hover{padding-left:4px;background:rgba(0,212,255,.03);} |
| .cavatar{width:26px;height:26px;border:1px solid var(--border);background:rgba(0,212,255,.1); |
| display:flex;align-items:center;justify-content:center;font-family:var(--font-d);font-size:9px;color:var(--cyan);flex-shrink:0;} |
| .cname{font-size:11px;color:var(--white);font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} |
| .crole{font-size:9px;color:rgba(232,244,255,.35);} |
| .csdot{width:5px;height:5px;border-radius:50%;flex-shrink:0;} |
| .online{background:var(--green);box-shadow:0 0 5px var(--green);} |
| .away{background:var(--gold);box-shadow:0 0 5px var(--gold);} |
| .offline{background:rgba(255,255,255,.15);} |
| |
| |
| .vgrid{display:grid;grid-template-columns:1fr 1fr;gap:7px;} |
| .vcard{border:1px solid var(--border2);padding:8px;background:rgba(0,10,30,.5);text-align:center;} |
| .vnum{font-family:var(--font-d);font-size:18px;font-weight:700;line-height:1;} |
| .vnum.c{color:var(--cyan);text-shadow:0 0 10px rgba(0,212,255,.5);} |
| .vnum.g{color:var(--gold);text-shadow:0 0 10px rgba(240,165,0,.5);} |
| .vnum.gr{color:var(--green);text-shadow:0 0 10px rgba(0,255,136,.5);} |
| .vnum.o{color:#ff8844;text-shadow:0 0 10px rgba(255,136,68,.5);} |
| .vlbl{font-family:var(--font-m);font-size:7px;color:rgba(232,244,255,.3);letter-spacing:2px;margin-top:3px;text-transform:uppercase;} |
| |
| |
| .drow{display:grid;grid-template-columns:1fr auto auto;gap:7px;align-items:center;padding:5px 0;border-bottom:1px solid var(--border2);font-size:10px;} |
| .drow:last-child{border:none;} |
| .dname{color:var(--white);font-weight:500;} |
| .dcalls{font-family:var(--font-m);color:var(--cyan);font-size:11px;} |
| .dbadge{font-family:var(--font-m);font-size:7px;padding:2px 5px;letter-spacing:1px;} |
| .hot{background:rgba(255,51,102,.12);color:var(--red);border:1px solid rgba(255,51,102,.25);} |
| .ok{background:rgba(0,255,136,.08);color:var(--green);border:1px solid rgba(0,255,136,.18);} |
| .low{background:rgba(240,165,0,.08);color:var(--gold);border:1px solid rgba(240,165,0,.18);} |
| |
| |
| .maincenter{display:flex;flex-direction:column;align-items:center;padding:16px;gap:16px;overflow-y:auto;position:relative;} |
| |
| |
| .orbsec{display:flex;flex-direction:column;align-items:center;gap:12px;width:100%;} |
| .orbwrap{position:relative;width:200px;height:200px;display:flex;align-items:center;justify-content:center;cursor:pointer;} |
| .orbring{position:absolute;border-radius:50%;border:1px solid rgba(0,212,255,.12);animation:rr 12s linear infinite;} |
| .orbring:nth-child(1){width:192px;height:192px;animation-duration:20s;} |
| .orbring:nth-child(2){width:174px;height:174px;border-color:rgba(0,212,255,.18);animation-duration:14s;animation-direction:reverse;} |
| .orbring:nth-child(3){width:155px;height:155px;border-color:rgba(240,165,0,.1);animation-duration:9s;} |
| .orbring::after{content:'';position:absolute;width:5px;height:5px;border-radius:50%;background:var(--cyan); |
| box-shadow:0 0 8px var(--cyan);top:-2.5px;left:50%;transform:translateX(-50%);} |
| .orbring:nth-child(2)::after{background:var(--gold);box-shadow:0 0 8px var(--gold);} |
| .orbring:nth-child(3)::after{width:3px;height:3px;top:-1.5px;} |
| @keyframes rr{from{transform:rotate(0)}to{transform:rotate(360deg)}} |
| .orbcore{position:relative;width:136px;height:136px;border-radius:50%;z-index:10;} |
| .orbsphere{width:100%;height:100%;border-radius:50%; |
| background:radial-gradient(circle at 35% 35%,rgba(0,212,255,.9),rgba(0,80,160,.8) 50%,rgba(0,10,40,.95) 80%); |
| box-shadow:0 0 35px rgba(0,212,255,.4),0 0 70px rgba(0,212,255,.15),inset 0 0 25px rgba(0,0,0,.5); |
| position:relative;overflow:hidden;animation:idle 4s ease-in-out infinite;} |
| @keyframes idle{0%,100%{box-shadow:0 0 35px rgba(0,212,255,.4),0 0 70px rgba(0,212,255,.15),inset 0 0 25px rgba(0,0,0,.5)} |
| 50%{box-shadow:0 0 55px rgba(0,212,255,.6),0 0 110px rgba(0,212,255,.25),inset 0 0 25px rgba(0,0,0,.5)}} |
| .orbsphere.speaking{animation:spk .12s ease-in-out infinite alternate; |
| background:radial-gradient(circle at 35% 35%,rgba(0,255,150,.9),rgba(0,180,100,.8) 50%,rgba(0,20,10,.95) 80%);} |
| @keyframes spk{0%{transform:scale(1);box-shadow:0 0 35px rgba(0,255,150,.5)}100%{transform:scale(1.07);box-shadow:0 0 65px rgba(0,255,150,.9),0 0 130px rgba(0,255,150,.3)}} |
| .orbsphere.listening{animation:lst .8s ease-in-out infinite alternate; |
| background:radial-gradient(circle at 35% 35%,rgba(255,180,0,.9),rgba(200,100,0,.8) 50%,rgba(20,5,0,.95) 80%);} |
| @keyframes lst{0%{transform:scale(.97);box-shadow:0 0 28px rgba(240,165,0,.5)}100%{transform:scale(1.04);box-shadow:0 0 56px rgba(240,165,0,.8),0 0 110px rgba(240,165,0,.3)}} |
| .orbsphere.thinking{animation:thk 1.4s ease-in-out infinite; |
| background:radial-gradient(circle at 35% 35%,rgba(150,0,255,.9),rgba(80,0,180,.8) 50%,rgba(10,0,30,.95) 80%);} |
| @keyframes thk{0%{transform:rotate(0) scale(1);box-shadow:0 0 35px rgba(150,0,255,.5)} |
| 25%{transform:rotate(5deg) scale(1.04)}75%{transform:rotate(-5deg) scale(.97)} |
| 100%{transform:rotate(0) scale(1);box-shadow:0 0 75px rgba(150,0,255,.7)}} |
| .orbsphere::before{content:'';position:absolute;width:55%;height:28%;top:14%;left:20%; |
| background:radial-gradient(ellipse,rgba(255,255,255,.15),transparent 70%);border-radius:50%;transform:rotate(-20deg);} |
| .orbwave{position:absolute;bottom:25px;left:16px;right:16px;height:32px;opacity:.7;pointer-events:none;} |
| .orbslabel{font-family:var(--font-d);font-size:8px;letter-spacing:4px;color:var(--dim);text-align:center;text-transform:uppercase;transition:color .3s;} |
| .orbctrl{display:flex;gap:10px;flex-wrap:wrap;justify-content:center;} |
| .obtn{font-family:var(--font-d);font-size:7px;letter-spacing:2px;padding:7px 13px;border:1px solid var(--border); |
| background:var(--glass);color:var(--dim);cursor:pointer;transition:all .3s;text-transform:uppercase;} |
| .obtn:hover{border-color:var(--cyan);color:var(--cyan);box-shadow:var(--glow-c);} |
| .obtn.as{border-color:var(--green);color:var(--green);box-shadow:0 0 18px rgba(0,255,136,.4);} |
| .obtn.al{border-color:var(--gold);color:var(--gold);box-shadow:var(--glow-g);} |
| .obtn.at{border-color:#9900ff;color:var(--purple);box-shadow:0 0 18px rgba(150,0,255,.4);} |
| |
| |
| .tbox{width:100%;max-width:580px;border:1px solid var(--border2);background:rgba(0,5,20,.85);padding:12px 16px;min-height:60px;position:relative;} |
| .tbox::before{content:'// NEURAL OUTPUT';position:absolute;top:-8px;left:14px;font-family:var(--font-m); |
| font-size:8px;color:var(--dim);background:var(--deep);padding:0 7px;letter-spacing:2px;} |
| #ttext{font-family:var(--font-m);font-size:12px;color:var(--white);line-height:1.6;opacity:.9;} |
| .cur{display:inline-block;width:7px;height:12px;background:var(--cyan);margin-left:2px;animation:blink 1s step-end infinite;vertical-align:middle;} |
| @keyframes blink{0%,100%{opacity:1}50%{opacity:0}} |
| |
| |
| .cmdbar{width:100%;max-width:580px;display:flex;border:1px solid var(--border);background:rgba(0,5,20,.9);} |
| .cmdinput{flex:1;background:transparent;border:none;outline:none;padding:10px 14px;font-family:var(--font-m);font-size:12px;color:var(--white);letter-spacing:.5px;} |
| .cmdinput::placeholder{color:rgba(0,212,255,.2);} |
| .cmdgo{padding:0 18px;background:rgba(0,212,255,.08);border:none;border-left:1px solid var(--border); |
| color:var(--cyan);font-family:var(--font-d);font-size:8px;letter-spacing:2px;cursor:pointer;transition:all .3s;text-transform:uppercase;} |
| .cmdgo:hover{background:rgba(0,212,255,.18);box-shadow:var(--glow-c);} |
| |
| |
| .brainsec{width:100%;max-width:580px;border:1px solid var(--border2);background:rgba(0,5,20,.85);} |
| .brainhead{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;border-bottom:1px solid var(--border2);} |
| .braintitle{font-family:var(--font-d);font-size:7.5px;letter-spacing:3px;color:var(--dim);text-transform:uppercase;display:flex;align-items:center;gap:7px;} |
| .braintitle::before{content:'';width:3px;height:9px;background:var(--gold);box-shadow:0 0 5px var(--gold);} |
| .zoomBtn{font-family:var(--font-d);font-size:7px;letter-spacing:2px;padding:4px 9px;border:1px solid var(--border); |
| background:transparent;color:var(--dim);cursor:pointer;transition:all .3s;} |
| .zoomBtn:hover{border-color:var(--gold);color:var(--gold);box-shadow:var(--glow-g);} |
| #brainMini{display:block;width:100%;height:180px;cursor:crosshair;} |
| |
| |
| .avatarsec{width:100%;max-width:580px;border:1px solid var(--border2);background:rgba(0,5,20,.85);position:relative;} |
| .avatarhead{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;border-bottom:1px solid var(--border2);} |
| .avatartitle{font-family:var(--font-d);font-size:7.5px;letter-spacing:3px;color:var(--purple);text-transform:uppercase; |
| display:flex;align-items:center;gap:7px;} |
| .avatartitle::before{content:'';width:3px;height:9px;background:var(--purple);box-shadow:0 0 5px var(--purple);} |
| .avatar-body{display:flex;gap:0;position:relative;} |
| #avatarCanvas{flex:1;height:380px;cursor:crosshair;} |
| .avatar-rightpanel{width:180px;flex-shrink:0;border-left:1px solid var(--border2);padding:12px; |
| display:flex;flex-direction:column;gap:8px;overflow-y:auto;background:rgba(0,5,20,.5);} |
| .modpill{padding:7px 10px;border:1px solid var(--border2);cursor:pointer;transition:all .3s;position:relative;overflow:hidden;} |
| .modpill::after{content:'';position:absolute;inset:0;background:linear-gradient(135deg,rgba(0,212,255,.06),transparent);opacity:0;transition:opacity .3s;} |
| .modpill:hover::after,.modpill.active::after{opacity:1;} |
| .modpill:hover,.modpill.active{border-color:var(--cyan);} |
| .modpill.active{background:rgba(0,212,255,.06);} |
| .modpill-name{font-family:var(--font-d);font-size:8px;letter-spacing:2px;color:rgba(232,244,255,.7);text-transform:uppercase;} |
| .modpill-status{font-family:var(--font-m);font-size:9px;margin-top:3px;} |
| .modpill-bar{height:2px;margin-top:6px;background:rgba(255,255,255,.06);} |
| .modpill-barfill{height:100%;transition:width 1s ease;} |
| .modpill-pct{font-family:var(--font-m);font-size:8px;color:var(--dim);margin-top:2px;text-align:right;} |
| .avatar-footer{padding:10px 13px;border-top:1px solid var(--border2);display:flex;gap:8px;flex-wrap:wrap;} |
| .avtag{font-family:var(--font-m);font-size:8px;padding:3px 9px;border:1px solid var(--border2);color:rgba(232,244,255,.35);letter-spacing:1px;} |
| .avtag.lit{border-color:rgba(0,212,255,.3);color:var(--cyan);} |
| #avatarStatus{font-family:var(--font-m);font-size:9px;color:var(--dim);letter-spacing:1px;padding:4px 13px;border-top:1px solid var(--border2);} |
| |
| |
| .camsec{width:100%;max-width:580px;border:1px solid var(--border2);background:rgba(0,5,20,.85);} |
| .camhead{display:flex;align-items:center;justify-content:space-between;padding:9px 13px;border-bottom:1px solid var(--border2);} |
| .camtitle{font-family:var(--font-d);font-size:7.5px;letter-spacing:3px;color:rgba(255,51,102,.6);text-transform:uppercase; |
| display:flex;align-items:center;gap:7px;} |
| .camtitle::before{content:'';width:3px;height:9px;background:var(--red);box-shadow:0 0 5px var(--red);} |
| .camview{position:relative;width:100%;height:240px;background:#000;overflow:hidden;display:flex;align-items:center;justify-content:center;} |
| #camfeed{width:100%;height:100%;object-fit:cover;display:none;transform:scaleX(-1);} |
| #gestcv{position:absolute;inset:0;pointer-events:none;display:none;} |
| .corner{position:absolute;width:20px;height:20px;pointer-events:none;z-index:10;} |
| .tl{top:7px;left:7px;border-top:2px solid var(--cyan);border-left:2px solid var(--cyan);} |
| .tr{top:7px;right:7px;border-top:2px solid var(--cyan);border-right:2px solid var(--cyan);} |
| .bl{bottom:7px;left:7px;border-bottom:2px solid var(--cyan);border-left:2px solid var(--cyan);} |
| .br{bottom:7px;right:7px;border-bottom:2px solid var(--cyan);border-right:2px solid var(--cyan);} |
| .camoff{display:flex;flex-direction:column;align-items:center;gap:12px;} |
| .camicon{width:52px;height:52px;border:2px solid var(--border);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:22px;filter:grayscale(1) opacity(.25);} |
| .camlabel{font-family:var(--font-d);font-size:8px;letter-spacing:2px;color:rgba(232,244,255,.18);text-align:center;} |
| .gestlabel{position:absolute;bottom:10px;left:50%;transform:translateX(-50%);font-family:var(--font-d);font-size:9px; |
| letter-spacing:2px;color:var(--gold);background:rgba(0,0,0,.75);padding:3px 14px;border:1px solid rgba(240,165,0,.25); |
| opacity:0;transition:opacity .3s;z-index:10;white-space:nowrap;} |
| .camctrl{padding:10px 13px;display:flex;gap:8px;flex-wrap:wrap;border-top:1px solid var(--border2);} |
| .gtag{font-family:var(--font-m);font-size:8px;padding:3px 9px;border:1px solid var(--border2);color:rgba(232,244,255,.35);letter-spacing:1px;display:flex;align-items:center;gap:5px;} |
| .gtag.rdy{border-color:rgba(0,255,136,.25);color:var(--green);} |
| |
| |
| #bmodal{position:fixed;inset:0;z-index:1000;background:rgba(0,0,10,.96);display:none;align-items:center;justify-content:center;backdrop-filter:blur(10px);} |
| #bmodal.open{display:flex;} |
| .bmodalinner{position:relative;width:90vw;height:90vh;border:1px solid var(--border);background:rgba(0,5,25,.98);} |
| .bmodalhead{display:flex;justify-content:space-between;align-items:center;padding:12px 18px;border-bottom:1px solid var(--border2);} |
| .bmodaltitle{font-family:var(--font-d);font-size:9px;letter-spacing:4px;color:var(--gold);text-transform:uppercase;} |
| .mclose{font-family:var(--font-d);font-size:8px;letter-spacing:2px;padding:4px 10px;border:1px solid rgba(255,51,102,.3); |
| background:transparent;color:rgba(255,51,102,.6);cursor:pointer;transition:all .3s;} |
| .mclose:hover{border-color:var(--red);color:var(--red);} |
| #brainBig{width:100%;height:calc(100% - 44px);} |
| .blegend{position:absolute;right:18px;top:60px;display:flex;flex-direction:column;gap:7px;} |
| .bleg{display:flex;align-items:center;gap:7px;font-family:var(--font-m);font-size:8px;color:rgba(232,244,255,.45);letter-spacing:1px;} |
| .bdot{width:7px;height:7px;border-radius:50%;} |
| |
| |
| #notif{position:fixed;top:72px;right:18px;z-index:900;display:flex;flex-direction:column;gap:7px;pointer-events:none;} |
| .nitem{background:rgba(0,5,25,.97);border:1px solid var(--border);border-left:3px solid var(--cyan); |
| padding:9px 14px;font-family:var(--font-m);font-size:10px;color:var(--white);min-width:220px;letter-spacing:.5px; |
| animation:nin .3s ease-out,nout .3s ease-in 2.7s forwards;} |
| .nitem.ng{border-left-color:var(--green);} |
| .nitem.no{border-left-color:var(--gold);} |
| @keyframes nin{from{opacity:0;transform:translateX(18px)}to{opacity:1;transform:translateX(0)}} |
| @keyframes nout{from{opacity:1}to{opacity:0}} |
| |
| |
| #expchart{width:100%;height:72px;} |
| |
| ::-webkit-scrollbar{width:3px;} |
| ::-webkit-scrollbar-track{background:transparent;} |
| ::-webkit-scrollbar-thumb{background:rgba(0,212,255,.18);} |
| </style> |
| </head> |
| <body> |
| <canvas id="starfield"></canvas> |
| <div id="notif"></div> |
|
|
| |
| <div id="bmodal"> |
| <div class="bmodalinner"> |
| <div class="bmodalhead"> |
| <div class="bmodaltitle">// JARVIS NEURAL ARCHITECTURE β FULL CORTEX MAP</div> |
| <button class="mclose" onclick="closeBrain()">CLOSE β</button> |
| </div> |
| <canvas id="brainBig"></canvas> |
| <div class="blegend"> |
| <div class="bleg"><div class="bdot" style="background:#00d4ff;box-shadow:0 0 5px #00d4ff"></div>VOICE ENGINE</div> |
| <div class="bleg"><div class="bdot" style="background:#f0a500;box-shadow:0 0 5px #f0a500"></div>MEMORY CORE</div> |
| <div class="bleg"><div class="bdot" style="background:#00ff88;box-shadow:0 0 5px #00ff88"></div>INTENT ROUTER</div> |
| <div class="bleg"><div class="bdot" style="background:#ff3366;box-shadow:0 0 5px #ff3366"></div>COMM MODULE</div> |
| <div class="bleg"><div class="bdot" style="background:#cc66ff;box-shadow:0 0 5px #cc66ff"></div>CRM BRIDGE</div> |
| <div class="bleg"><div class="bdot" style="background:#ff6644;box-shadow:0 0 5px #ff6644"></div>ANALYTICS</div> |
| <div class="bleg"><div class="bdot" style="background:#ffffff;box-shadow:0 0 5px #ffffff"></div>CORE LLM</div> |
| </div> |
| </div> |
| </div> |
|
|
| <div id="app"> |
| |
| <header> |
| <div class="logo">JARVIS</div> |
| <div class="hcenter"> |
| <div class="sdot" id="sdot"></div> |
| <div class="stext" id="stext">SYSTEM ONLINE</div> |
| <div class="htime" id="htime">--:--:--</div> |
| </div> |
| <div class="hright"> |
| <button class="hbtn" id="micBtn" onclick="toggleMic()">MIC ON</button> |
| <button class="hbtn" id="camBtn" onclick="toggleCamera()">CAM OFF</button> |
| <button class="hbtn danger">STANDBY</button> |
| </div> |
| </header> |
|
|
| |
| <div class="pleft"> |
| <div class="w"> |
| <div class="wtitle">System Vitals</div> |
| <div class="vgrid"> |
| <div class="vcard"><div class="vnum c" id="cpuv">14%</div><div class="vlbl">CPU</div></div> |
| <div class="vcard"><div class="vnum g" id="memv">4.3G</div><div class="vlbl">MEMORY</div></div> |
| <div class="vcard"><div class="vnum gr" id="latv">38ms</div><div class="vlbl">LATENCY</div></div> |
| <div class="vcard"><div class="vnum o" id="tmpv">62Β°C</div><div class="vlbl">TEMP</div></div> |
| </div> |
| </div> |
| <div class="w"> |
| <div class="wtitle">Module Load</div> |
| <div class="brow"><div class="blabel">VOICE</div><div class="btrack"><div class="bfill" style="width:92%"></div></div><div class="bval" style="color:var(--green)">92%</div></div> |
| <div class="brow"><div class="blabel">MEMORY</div><div class="btrack"><div class="bfill" style="width:78%;background:linear-gradient(90deg,var(--gold),#ffcc44)"></div></div><div class="bval" style="color:var(--gold)">78%</div></div> |
| <div class="brow"><div class="blabel">CRM</div><div class="btrack"><div class="bfill" style="width:64%"></div></div><div class="bval">64%</div></div> |
| <div class="brow"><div class="blabel">GESTURE</div><div class="btrack"><div class="bfill" id="gestbar" style="width:0%;background:linear-gradient(90deg,#ff3366,#ff6666)"></div></div><div class="bval" id="gestval" style="color:rgba(255,51,102,.5)">OFF</div></div> |
| <div class="brow"><div class="blabel">ANALYTICS</div><div class="btrack"><div class="bfill" style="width:88%"></div></div><div class="bval" style="color:var(--green)">88%</div></div> |
| </div> |
| <div class="w"> |
| <div class="wtitle gold">Expense Β· This Week</div> |
| <canvas id="expchart"></canvas> |
| <div class="mrow"><div class="mlabel">TOTAL</div><div class="mval gold">βΉ18,650</div></div> |
| <div class="mrow"><div class="mlabel">TRANSACTIONS</div><div class="mval">12</div></div> |
| <div class="mrow"><div class="mlabel">TOP CATEGORY</div><div class="mval gr">TRAVEL</div></div> |
| <div class="mrow"><div class="mlabel">BUDGET LEFT</div><div class="mval r">βΉ1,350</div></div> |
| </div> |
| <div class="w"> |
| <div class="wtitle">Activity Log</div> |
| <div class="litem"><div class="ltime">09:42:17</div><div class="ltext">Call count β Ravi Motors: 14 calls today</div></div> |
| <div class="litem"><div class="ltime">09:38:04</div><div class="ltext">Expense logged βΉ2,400 Β· Fuel Β· Travel</div></div> |
| <div class="litem"><div class="ltime">09:31:50</div><div class="ltext">Contact lookup β Pradeep Sharma Β· Shree Motors</div></div> |
| <div class="litem"><div class="ltime">09:15:22</div><div class="ltext">Call summary: Anil (7 mins) Β· 3 action items</div></div> |
| <div class="litem"><div class="ltime">08:59:11</div><div class="ltext">WhatsApp sent to Team Managers Β· 6 recipients</div></div> |
| </div> |
| </div> |
|
|
| |
| <div class="maincenter"> |
|
|
| |
| <div class="orbsec"> |
| <div class="orbwrap" id="orbwrap" onclick="cycleOrb()"> |
| <div class="orbring"></div><div class="orbring"></div><div class="orbring"></div> |
| <div class="orbcore"> |
| <div class="orbsphere" id="orb"> |
| <canvas class="orbwave" id="orbwave"></canvas> |
| </div> |
| </div> |
| </div> |
| <div class="orbslabel" id="orblabel">IDLE β CLICK TO ACTIVATE</div> |
| <div class="orbctrl"> |
| <button class="obtn" id="bspk" onclick="setOrb('speaking')">β SPEAKING</button> |
| <button class="obtn" id="blst" onclick="setOrb('listening')">β LISTENING</button> |
| <button class="obtn" id="bthk" onclick="setOrb('thinking')">⬑ THINKING</button> |
| <button class="obtn" onclick="setOrb('idle')">β IDLE</button> |
| </div> |
| </div> |
|
|
| |
| <div class="tbox"> |
| <div id="ttext">Initializing neural interface... All systems nominal. Ready for voice commands.<span class="cur"></span></div> |
| </div> |
|
|
| |
| <div class="cmdbar"> |
| <input class="cmdinput" id="cmdinput" placeholder="// type command or speak..." onkeydown="handleKey(event)"> |
| <button class="cmdgo" onclick="submitCmd()">EXECUTE</button> |
| </div> |
|
|
| |
| <div class="avatarsec"> |
| <div class="avatarhead"> |
| <div class="avatartitle">Neural Avatar β JARVIS Embodiment</div> |
| <div style="display:flex;gap:8px;"> |
| <button class="zoomBtn" style="border-color:rgba(204,102,255,.3);color:var(--purple);" onclick="cycleAvatarMode()">CYCLE VIEW</button> |
| </div> |
| </div> |
| <div class="avatar-body"> |
| <canvas id="avatarCanvas"></canvas> |
| <div class="avatar-rightpanel" id="modpanel"></div> |
| </div> |
| <div id="avatarStatus">// AVATAR IDLE β ALL SYSTEMS NOMINAL</div> |
| <div class="avatar-footer" id="avfooter"></div> |
| </div> |
|
|
| |
| <div class="brainsec"> |
| <div class="brainhead"> |
| <div class="braintitle">Neural Brain Map</div> |
| <button class="zoomBtn" onclick="openBrain()">β ZOOM β FULL CORTEX</button> |
| </div> |
| <canvas id="brainMini"></canvas> |
| </div> |
|
|
| |
| <div class="camsec"> |
| <div class="camhead"> |
| <div class="camtitle">Vision & Gesture Control</div> |
| <button class="hbtn" id="camtoggle" onclick="toggleCamera()">ENABLE CAMERA</button> |
| </div> |
| <div class="camview" id="camview"> |
| <video id="camfeed" autoplay muted playsinline></video> |
| <canvas id="gestcv"></canvas> |
| <div class="corner tl"></div><div class="corner tr"></div> |
| <div class="corner bl"></div><div class="corner br"></div> |
| <div class="gestlabel" id="gestlabel">PALM DETECTED</div> |
| <div class="camoff" id="camoff"> |
| <div class="camicon">π·</div> |
| <div class="camlabel">CAMERA DISABLED<br>ENABLE GESTURE CONTROL</div> |
| </div> |
| </div> |
| <div class="camctrl"> |
| <div class="gtag">β OPEN PALM β PAUSE</div> |
| <div class="gtag">π POINT β SELECT</div> |
| <div class="gtag">β FIST β CLOSE</div> |
| <div class="gtag">π€ PINCH β ZOOM</div> |
| <div class="gtag rdy" id="visready">β VISION: STANDBY</div> |
| </div> |
| </div> |
|
|
| </div> |
|
|
| |
| <div class="pright"> |
| <div class="w"> |
| <div class="wtitle">OttoPilot Β· Live Dealers</div> |
| <div class="drow"><div class="dname">Ravi Motors</div><div class="dcalls">14</div><div class="dbadge hot">HOT</div></div> |
| <div class="drow"><div class="dname">Shree Auto</div><div class="dcalls">09</div><div class="dbadge ok">GOOD</div></div> |
| <div class="drow"><div class="dname">Krishna Cars</div><div class="dcalls">06</div><div class="dbadge ok">GOOD</div></div> |
| <div class="drow"><div class="dname">Pradeep Auto</div><div class="dcalls">03</div><div class="dbadge low">LOW</div></div> |
| <div class="drow"><div class="dname">Anil Motors</div><div class="dcalls">01</div><div class="dbadge low">LOW</div></div> |
| <div style="margin-top:8px"> |
| <div class="mrow"><div class="mlabel">TOTAL CALLS</div><div class="mval c">33</div></div> |
| <div class="mrow"><div class="mlabel">CONNECTED</div><div class="mval gr">24</div></div> |
| <div class="mrow"><div class="mlabel">PENDING LEADS</div><div class="mval gold">18</div></div> |
| </div> |
| </div> |
| <div class="w"> |
| <div class="wtitle">Recent Contacts</div> |
| <div class="citem"><div class="cavatar">RP</div><div style="flex:1;overflow:hidden"><div class="cname">Ravi Patil</div><div class="crole">GM Β· Ravi Motors</div></div><div class="csdot online"></div></div> |
| <div class="citem"><div class="cavatar">AS</div><div style="flex:1;overflow:hidden"><div class="cname">Ajay Sharma</div><div class="crole">Exec Β· Shree Auto</div></div><div class="csdot away"></div></div> |
| <div class="citem"><div class="cavatar">PK</div><div style="flex:1;overflow:hidden"><div class="cname">Pradeep Kumar</div><div class="crole">Owner Β· Krishna Cars</div></div><div class="csdot offline"></div></div> |
| <div class="citem"><div class="cavatar">VJ</div><div style="flex:1;overflow:hidden"><div class="cname">Vijay Joshi</div><div class="crole">Mgr Β· Anil Motors</div></div><div class="csdot online"></div></div> |
| </div> |
| <div class="w"> |
| <div class="wtitle gold">Today's Schedule</div> |
| <div class="litem"><div class="ltime">10:00 AM</div><div class="ltext">Dealer review call β All GMs</div></div> |
| <div class="litem"><div class="ltime">12:30 PM</div><div class="ltext">Lunch β Rohit & Team</div></div> |
| <div class="litem"><div class="ltime">03:00 PM</div><div class="ltext">OttoPilot training walkthrough</div></div> |
| <div class="litem"><div class="ltime">05:30 PM</div><div class="ltext">Q2 target review β Finance</div></div> |
| </div> |
| <div class="w"> |
| <div class="wtitle purple">Memory Bank</div> |
| <div class="mrow"><div class="mlabel">CONVERSATIONS</div><div class="mval c">247</div></div> |
| <div class="mrow"><div class="mlabel">CALL SUMMARIES</div><div class="mval gold">84</div></div> |
| <div class="mrow"><div class="mlabel">EXPENSE RECORDS</div><div class="mval">312</div></div> |
| <div class="mrow"><div class="mlabel">CONTACTS INDEXED</div><div class="mval gr">156</div></div> |
| <div class="mrow"><div class="mlabel">NEURAL VECTORS</div><div class="mval p">18.4K</div></div> |
| <div class="mrow"><div class="mlabel">UPTIME</div><div class="mval gr">14d 6h</div></div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| |
| |
| (() => { |
| const c = document.getElementById('starfield'), ctx = c.getContext('2d'); |
| const resize = () => { c.width = innerWidth; c.height = innerHeight; }; |
| resize(); addEventListener('resize', resize); |
| const stars = Array.from({ length: 180 }, () => ({ x: Math.random(), y: Math.random(), s: Math.random() * 1.4 + .2, o: Math.random(), sp: Math.random() * .0002 + .0001 })); |
| const draw = () => { |
| ctx.clearRect(0, 0, c.width, c.height); |
| stars.forEach(s => { |
| s.o += Math.sin(Date.now() * s.sp) * .004; |
| ctx.beginPath(); ctx.arc(s.x * c.width, s.y * c.height, s.s, 0, Math.PI * 2); |
| ctx.fillStyle = `rgba(180, 220, 255, ${Math.max(0, Math.min(1, s.o))})`; ctx.fill(); |
| }); |
| requestAnimationFrame(draw); |
| }; draw(); |
| })(); |
| |
| |
| |
| |
| const API_BASE = ""; |
| |
| async function refreshData() { |
| try { |
| |
| const vitRes = await fetch(`${API_BASE}/system/vitals`); |
| const vitals = await vitRes.json(); |
| document.getElementById('cpuv').textContent = Math.floor(vitals.cpu) + '%'; |
| document.getElementById('memv').textContent = (vitals.memory / 10).toFixed(1) + 'G'; |
| document.getElementById('latv').textContent = Math.floor(Math.random() * 15 + 20) + 'ms'; |
| document.getElementById('tmpv').textContent = Math.floor(Math.random() * 5 + 55) + 'Β°C'; |
| |
| |
| const expRes = await fetch(`${API_BASE}/system/expenses`); |
| const exp = await expRes.json(); |
| const expParent = document.getElementById('expchart').parentElement; |
| expParent.querySelector('.mval.gold').textContent = exp.total; |
| expParent.querySelector('.mrow:nth-child(4) .mval').textContent = exp.count; |
| expParent.querySelector('.mrow:nth-child(5) .mval').textContent = exp.top_category; |
| |
| |
| const dealRes = await fetch(`${API_BASE}/system/dealers`); |
| const dealers = await dealRes.json(); |
| const dealList = document.querySelector('.pright .w:first-child'); |
| const existingRows = dealList.querySelectorAll('.drow'); |
| existingRows.forEach(r => r.remove()); |
| dealers.dealers.forEach(d => { |
| const row = document.createElement('div'); |
| row.className = 'drow'; |
| row.innerHTML = `<div class="dname">${d.name}</div><div class="dcalls">${d.calls}</div><div class="dbadge ${d.status.toLowerCase() === 'hot' ? 'hot' : (d.status.toLowerCase() === 'good' ? 'ok' : 'low')}">${d.status}</div>`; |
| dealList.insertBefore(row, dealList.querySelector('div[style*="margin-top"]')); |
| }); |
| dealList.querySelector('.mval.c').textContent = dealers.total_calls; |
| dealList.querySelector('.mval.gr').textContent = dealers.connected; |
| |
| |
| const statsRes = await fetch(`${API_BASE}/system/stats/count`); |
| const stats = await statsRes.json(); |
| const memW = document.querySelector('.pright .w:last-child'); |
| if (memW) { |
| const conversations = memW.querySelector('.mrow:nth-child(2) .mval'); |
| if (conversations) conversations.textContent = stats.conversations; |
| const expenses = memW.querySelector('.mrow:nth-child(4) .mval'); |
| if (expenses) expenses.textContent = stats.expenses; |
| const contacts = memW.querySelector('.mrow:nth-child(5) .mval'); |
| if (contacts) contacts.textContent = stats.contacts; |
| const vectors = memW.querySelector('.mrow:nth-child(6) .mval'); |
| if (vectors) vectors.textContent = stats.vectors; |
| const uptime = memW.querySelector('.mrow:nth-child(7) .mval'); |
| if (uptime) uptime.textContent = stats.uptime; |
| } |
| |
| |
| const contactRes = await fetch(`${API_BASE}/system/contacts`); |
| const contacts = await contactRes.json(); |
| const cList = document.querySelector('.pright .w:nth-child(2)'); |
| cList.querySelectorAll('.citem').forEach(e => e.remove()); |
| contacts.permanent.concat(contacts.recent).slice(0, 4).forEach(c => { |
| const item = document.createElement('div'); |
| item.className = 'citem'; |
| const initials = c.name.split(' ').map(n => n[0]).join('').toUpperCase(); |
| item.innerHTML = `<div class="cavatar">${initials}</div><div style="flex:1;overflow:hidden"><div class="cname">${c.name}</div><div class="crole">${c.role}</div></div><div class="csdot ${c.status}"></div>`; |
| cList.appendChild(item); |
| }); |
| } catch (e) { console.error("API Fetch Error:", e); } |
| } |
| |
| setInterval(refreshData, 5000); |
| refreshData(); |
| setInterval(() => { document.getElementById('htime').textContent = new Date().toTimeString().slice(0, 8) + ' IST'; }, 1000); |
| |
| |
| |
| |
| let orbState = 'idle'; |
| const orbStates = { |
| idle: { cls: '', label: 'IDLE β CLICK TO ACTIVATE', status: 'SYSTEM ONLINE' }, |
| speaking: { cls: 'speaking', label: 'SPEAKING β NEURAL OUTPUT ACTIVE', status: 'SPEAKING' }, |
| listening: { cls: 'listening', label: 'LISTENING β VOICE INPUT ACTIVE', status: 'LISTENING' }, |
| thinking: { cls: 'thinking', label: 'PROCESSING β NEURAL INFERENCE', status: 'THINKING' } |
| }; |
| |
| function setOrb(s) { |
| orbState = s; |
| const orb = document.getElementById('orb'); |
| if (orb) orb.className = 'orbsphere ' + (orbStates[s].cls || ''); |
| const orblabel = document.getElementById('orblabel'); |
| if (orblabel) orblabel.textContent = orbStates[s].label; |
| const stext = document.getElementById('stext'); |
| if (stext) stext.textContent = orbStates[s].status; |
| ['bspk', 'blst', 'bthk'].forEach(id => { |
| const btn = document.getElementById(id); |
| if (btn) btn.className = 'obtn'; |
| }); |
| if (s === 'speaking') { const b = document.getElementById('bspk'); if (b) b.className = 'obtn as'; } |
| if (s === 'listening') { const b = document.getElementById('blst'); if (b) b.className = 'obtn al'; } |
| if (s === 'thinking') { const b = document.getElementById('bthk'); if (b) b.className = 'obtn at'; } |
| updateAvatar(s); |
| notif('JARVIS: Mode β ' + s.toUpperCase(), s === 'speaking' ? 'ng' : ''); |
| } |
| |
| function cycleOrb() { const s = ['idle', 'listening', 'thinking', 'speaking']; setOrb(s[(s.indexOf(orbState) + 1) % s.length]); } |
| |
| |
| (() => { |
| const c = document.getElementById('orbwave'), ctx = c.getContext('2d'); |
| c.width = 100; c.height = 30; let t = 0; |
| const draw = () => { |
| ctx.clearRect(0, 0, 100, 30); |
| ctx.strokeStyle = orbState === 'speaking' ? '#00ff88' : orbState === 'listening' ? '#f0a500' : orbState === 'thinking' ? '#cc66ff' : '#00d4ff'; |
| ctx.lineWidth = 1.5; ctx.globalAlpha = .6; ctx.beginPath(); |
| for (let x = 0; x < 100; x++) { |
| const amp = orbState === 'idle' ? 2 : orbState === 'thinking' ? 5 : orbState === 'speaking' ? 9 * (0.5 + 0.5 * Math.sin(t * 3 + x * .1)) : 6; |
| const y = 15 + amp * Math.sin(t * 4 + x * .15) * Math.sin(x * .08); x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y); |
| } |
| ctx.stroke(); t += .06; requestAnimationFrame(draw); |
| }; draw(); |
| })(); |
| |
| |
| |
| |
| function handleKey(e) { if (e.key === 'Enter') submitCmd(); } |
| async function submitCmd() { |
| const v = document.getElementById('cmdinput').value.trim(); if (!v) return; |
| setOrb('thinking'); document.getElementById('cmdinput').value = ''; |
| document.getElementById('ttext').innerHTML = 'Processing: "' + v + '"<span class="cur"></span>'; |
| try { |
| const res = await fetch(`${API_BASE}/system/command`, { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ command: v }) |
| }); |
| const data = await res.json(); |
| setOrb('speaking'); |
| document.getElementById('ttext').innerHTML = data.response + '<span class="cur"></span>'; |
| setTimeout(() => setOrb('idle'), 5000); |
| } catch (e) { |
| notif("Command failed", "no"); |
| setOrb('idle'); |
| } |
| } |
| |
| async function toggleStandby() { |
| const current = document.getElementById('stext').textContent === 'STANDBY'; |
| const newStatus = !current; |
| try { |
| await fetch(`${API_BASE}/system/safety-lock`, { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ status: newStatus }) |
| }); |
| notif(newStatus ? "System entering standby..." : "System resuming...", newStatus ? "no" : "ng"); |
| document.getElementById('stext').textContent = newStatus ? 'STANDBY' : 'SYSTEM ONLINE'; |
| } catch (e) { notif("Lock toggle failed", "no"); } |
| } |
| document.querySelector('.danger').onclick = toggleStandby; |
| |
| |
| |
| |
| (() => { |
| const c = document.getElementById('expchart'), ctx = c.getContext('2d'); |
| const dpr = devicePixelRatio || 1, W = c.parentElement.offsetWidth - 24; |
| c.style.width = W + 'px'; c.width = W * dpr; c.height = 72 * dpr; ctx.scale(dpr, dpr); |
| const vals = [3200, 1800, 4100, 2650, 3900, 1400, 1600], days = ['M', 'T', 'W', 'T', 'F', 'S', 'S'], mx = Math.max(...vals); |
| vals.forEach((v, i) => { |
| const x = i * (W / 7) + (W / 7 - 16) / 2, bh = (v / mx) * 50, y = 62 - bh; |
| const gr = ctx.createLinearGradient(0, y, 0, 62); gr.addColorStop(0, 'rgba(0,212,255,.8)'); gr.addColorStop(1, 'rgba(0,80,160,.2)'); |
| ctx.fillStyle = gr; ctx.fillRect(x, y, 16, bh); |
| ctx.fillStyle = 'rgba(232,244,255,.3)'; ctx.font = '7px Share Tech Mono'; ctx.textAlign = 'center'; ctx.fillText(days[i], x + 8, 70); |
| }); |
| })(); |
| |
| |
| |
| |
| function drawBrain(ctx, W, H, sc) { |
| const cx = W / 2, cy = H / 2, t = Date.now() * .001; |
| const nodes = [ |
| { x: cx, y: cy - 65 * sc, r: 17 * sc, c: '#00d4ff', label: 'VOICE', fired: true }, |
| { x: cx - 85 * sc, y: cy - 22 * sc, r: 21 * sc, c: '#f0a500', label: 'MEMORY', fired: true }, |
| { x: cx + 85 * sc, y: cy - 22 * sc, r: 19 * sc, c: '#00ff88', label: 'INTENT', fired: true }, |
| { x: cx - 55 * sc, y: cy + 55 * sc, r: 15 * sc, c: '#ff3366', label: 'COMM', fired: true }, |
| { x: cx + 55 * sc, y: cy + 55 * sc, r: 17 * sc, c: '#cc66ff', label: 'CRM', fired: true }, |
| { x: cx, y: cy + 75 * sc, r: 13 * sc, c: '#ff6644', label: 'ANALYTICS', fired: true }, |
| { x: cx - 135 * sc, y: cy + 5 * sc, r: 11 * sc, c: '#00aaff', label: 'STT', fired: true }, |
| { x: cx + 135 * sc, y: cy + 5 * sc, r: 11 * sc, c: '#44ffaa', label: 'TTS', fired: true }, |
| { x: cx, y: cy + 8 * sc, r: 25 * sc, c: '#ffffff', label: 'CORE LLM', fired: true }, |
| ]; |
| const edges = [[0, 8], [1, 8], [2, 8], [3, 8], [4, 8], [5, 8], [6, 0], [7, 0], [0, 2], [1, 3], [2, 4], [3, 5], [4, 5], [6, 8], [7, 8], [1, 2]]; |
| ctx.clearRect(0, 0, W, H); |
| edges.forEach(([a, b]) => { |
| const na = nodes[a], nb = nodes[b]; |
| const gr = ctx.createLinearGradient(na.x, na.y, nb.x, nb.y); |
| gr.addColorStop(0, na.c + '55'); gr.addColorStop(1, nb.c + '55'); |
| ctx.strokeStyle = gr; ctx.lineWidth = .8; ctx.beginPath(); ctx.moveTo(na.x, na.y); ctx.lineTo(nb.x, nb.y); ctx.stroke(); |
| const p = (t * .35 + (a + b) * .07) % 1; |
| const px = na.x + (nb.x - na.x) * p, py = na.y + (nb.y - na.y) * p; |
| ctx.beginPath(); ctx.arc(px, py, 1.8, 0, Math.PI * 2); ctx.fillStyle = '#ffffff99'; ctx.fill(); |
| }); |
| nodes.forEach((n, i) => { |
| const pulse = 1 + .08 * Math.sin(t * 2 + i), r = n.r * pulse; |
| const gw = ctx.createRadialGradient(n.x, n.y, 0, n.x, n.y, r * 2.2); |
| gw.addColorStop(0, n.c + '40'); gw.addColorStop(1, 'transparent'); |
| ctx.fillStyle = gw; ctx.beginPath(); ctx.arc(n.x, n.y, r * 2.2, 0, Math.PI * 2); ctx.fill(); |
| const gr = ctx.createRadialGradient(n.x - r * .3, n.y - r * .3, 0, n.x, n.y, r); |
| gr.addColorStop(0, n.c + 'ff'); gr.addColorStop(.6, n.c + 'aa'); gr.addColorStop(1, n.c + '33'); |
| ctx.fillStyle = gr; ctx.beginPath(); ctx.arc(n.x, n.y, r, 0, Math.PI * 2); ctx.fill(); |
| ctx.fillStyle = 'rgba(232,244,255,.65)'; ctx.font = `${Math.max(6, 7 * sc)}px Share Tech Mono`; ctx.textAlign = 'center'; |
| ctx.fillText(n.label, n.x, n.y + r + 9 * sc); |
| }); |
| } |
| (() => { |
| const c = document.getElementById('brainMini'), ctx = c.getContext('2d'); |
| const dpr = devicePixelRatio || 1; |
| const resize = () => { const W = c.parentElement.offsetWidth; c.style.width = W + 'px'; c.style.height = '180px'; c.width = W * dpr; c.height = 180 * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); }; |
| resize(); addEventListener('resize', resize); |
| const loop = () => { drawBrain(ctx, c.offsetWidth, c.offsetHeight, .68); requestAnimationFrame(loop); }; loop(); |
| })(); |
| |
| let brainAnim; |
| function openBrain() { |
| document.getElementById('bmodal').classList.add('open'); |
| const c = document.getElementById('brainBig'), ctx = c.getContext('2d'); |
| const dpr = devicePixelRatio || 1; |
| const resize = () => { const W = c.parentElement.offsetWidth, H = c.parentElement.offsetHeight - 44; c.style.width = W + 'px'; c.style.height = H + 'px'; c.width = W * dpr; c.height = H * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); }; |
| resize(); |
| const loop = () => { drawBrain(ctx, c.offsetWidth, c.offsetHeight, 1.7); brainAnim = requestAnimationFrame(loop); }; loop(); |
| } |
| function closeBrain() { document.getElementById('bmodal').classList.remove('open'); cancelAnimationFrame(brainAnim); } |
| |
| |
| |
| |
| const MODULES = [ |
| { id: 'voice', name: 'VOICE ENGINE', color: '#00d4ff', pct: 92, region: 'head', desc: 'STT + TTS active' }, |
| { id: 'memory', name: 'MEMORY CORE', color: '#f0a500', pct: 78, region: 'brain', desc: '142K vectors loaded' }, |
| { id: 'intent', name: 'INTENT ROUTER', color: '#00ff88', pct: 88, region: 'chest', desc: 'LLM routing online' }, |
| { id: 'crm', name: 'CRM BRIDGE', color: '#cc66ff', pct: 64, region: 'rhand', desc: 'OttoPilot connected' }, |
| { id: 'comm', name: 'COMM MODULE', color: '#ff3366', pct: 71, region: 'lhand', desc: 'WhatsApp + Email ready' }, |
| { id: 'gesture', name: 'GESTURE CTRL', color: '#ff6644', pct: 0, region: 'lhand', desc: 'Camera offline' }, |
| { id: 'analytics', name: 'ANALYTICS', color: '#ffcc44', pct: 88, region: 'core', desc: 'Reports engine online' }, |
| { id: 'profile', name: 'PROFILE SWITCH', color: '#44ffaa', pct: 55, region: 'legs', desc: 'Secondary profile ready' }, |
| ]; |
| |
| let avatarMode = 0; let activeModule = null; let avatarAnim; let particleList = []; |
| function buildModPanel() { |
| const p = document.getElementById('modpanel'); if (!p) return; p.innerHTML = ''; |
| MODULES.forEach(m => { |
| const el = document.createElement('div'); el.className = 'modpill' + (activeModule === m.id ? ' active' : ''); el.id = 'pill_' + m.id; el.onclick = () => selectModule(m.id); |
| el.innerHTML = `<div class="modpill-name" style="color:${m.color}">${m.name}</div><div class="modpill-status" style="color:${m.color}88">${m.desc}</div><div class="modpill-bar"><div class="modpill-barfill" style="width:${m.pct}%;background:${m.color};box-shadow:0 0 6px ${m.color}"></div></div><div class="modpill-pct">${m.pct > 0 ? m.pct + '%' : 'OFFLINE'}</div>`; |
| p.appendChild(el); |
| }); |
| const f = document.getElementById('avfooter'); if (!f) return; f.innerHTML = ''; |
| ['HOLOGRAPHIC', 'SKELETON', 'ORGANS', 'ENERGY'].forEach((v, i) => { |
| const el = document.createElement('div'); el.className = 'avtag' + (avatarMode === i ? ' lit' : ''); el.textContent = v; el.onclick = () => { avatarMode = i; buildModPanel(); }; f.appendChild(el); |
| }); |
| } |
| buildModPanel(); |
| |
| function selectModule(id) { |
| activeModule = activeModule === id ? null : id; buildModPanel(); |
| const m = MODULES.find(x => x.id === id); if (m) notif('MODULE: ' + m.name + ' β ' + m.desc, m.pct > 70 ? 'ng' : 'no'); |
| const avatarStatus = document.getElementById('avatarStatus'); |
| if (avatarStatus) avatarStatus.textContent = m ? '// SELECTED: ' + m.name + ' (' + m.pct + '%) β ' + m.desc : '// AVATAR IDLE β ALL SYSTEMS NOMINAL'; |
| } |
| function updateAvatar(state) { |
| const pcts = { idle: [92, 78, 88, 64, 71, 0, 88, 55], speaking: [98, 80, 95, 64, 71, 0, 88, 55], listening: [95, 85, 88, 64, 75, 0, 88, 55], thinking: [88, 95, 99, 80, 71, 0, 92, 55] }; |
| const p = pcts[state] || pcts.idle; MODULES.forEach((m, i) => { m.pct = p[i]; }); buildModPanel(); |
| } |
| function cycleAvatarMode() { avatarMode = (avatarMode + 1) % 4; buildModPanel(); notif('Avatar view: ' + ['HOLOGRAPHIC', 'SKELETON', 'ORGANS', 'ENERGY'][avatarMode], ''); } |
| |
| |
| (() => { |
| const c = document.getElementById('avatarCanvas'); if (!c) return; const ctx = c.getContext('2d'); const dpr = devicePixelRatio || 1; |
| const resize = () => { const W = c.parentElement.offsetWidth - 180; c.style.width = W + 'px'; c.style.height = '380px'; c.width = W * dpr; c.height = 380 * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); }; |
| resize(); const regions = { head: { dy: -140, r: 28 }, brain: { dy: -130, r: 20 }, chest: { dy: -55, r: 40 }, core: { dy: -55, r: 40 }, lhand: { dx: -65, dy: -20, r: 20 }, rhand: { dx: 65, dy: -20, r: 20 }, legs: { dy: 80, r: 35 } }; |
| c.addEventListener('click', (e) => { |
| const rect = c.getBoundingClientRect(); const mx = (e.clientX - rect.left), my = (e.clientY - rect.top), cx = c.offsetWidth / 2, cy = 220; |
| MODULES.forEach(m => { const reg = regions[m.region] || { dy: 0, r: 30 }, rx = cx + (reg.dx || 0), ry = cy + (reg.dy || 0); if (Math.hypot(mx - rx, my - ry) < reg.r + 10) selectModule(m.id); }); |
| }); |
| function spawnParticle(x, y, color) { for (let i = 0; i < 6; i++)particleList.push({ x, y, vx: (Math.random() - .5) * 3, vy: (Math.random() - .5) * 3, life: 1, color }); } |
| function drawAvatar(W, H) { |
| ctx.clearRect(0, 0, W, H); const t = Date.now() * .001, cx = W / 2, cy = 220; |
| ctx.strokeStyle = 'rgba(0,212,255,0.04)'; ctx.lineWidth = .5; |
| for (let x = 0; x < W; x += 30) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke(); } |
| if (avatarMode === 0 || avatarMode === 1) { |
| ctx.shadowBlur = avatarMode === 0 ? 16 : 4; ctx.shadowColor = avatarMode === 0 ? '#00d4ff' : 'rgba(0,212,255,.3)'; ctx.strokeStyle = avatarMode === 0 ? 'rgba(0,212,255,.55)' : 'rgba(0,212,255,.2)'; ctx.lineWidth = avatarMode === 0 ? 1.5 : 1; |
| ctx.beginPath(); ctx.arc(cx, cy - 140, 28, 0, Math.PI * 2); ctx.stroke(); ctx.beginPath(); ctx.moveTo(cx - 30, cy - 100); ctx.lineTo(cx - 38, cy - 60); ctx.lineTo(cx - 35, cy + 10); ctx.lineTo(cx + 35, cy + 10); ctx.lineTo(cx + 38, cy - 60); ctx.lineTo(cx + 30, cy - 100); ctx.closePath(); ctx.stroke(); |
| ctx.shadowBlur = 0; |
| } |
| const scanY = cy - 160 + ((t * 50) % 320); const sg = ctx.createLinearGradient(0, scanY - 8, 0, scanY + 8); sg.addColorStop(0, 'transparent'); sg.addColorStop(.5, 'rgba(0,212,255,.08)'); sg.addColorStop(1, 'transparent'); ctx.fillStyle = sg; ctx.fillRect(0, scanY - 8, W, 16); |
| particleList = particleList.filter(p => { p.x += p.vx; p.y += p.vy; p.life -= .03; if (p.life <= 0) return false; ctx.beginPath(); ctx.arc(p.x, p.y, 2 * p.life, 0, Math.PI * 2); ctx.fillStyle = p.color + (Math.floor(p.life * 255).toString(16).padStart(2, '0')); ctx.fill(); return true; }); |
| } |
| const loop = () => { drawAvatar(c.offsetWidth, c.offsetHeight); avatarAnim = requestAnimationFrame(loop); }; loop(); |
| })(); |
| |
| |
| |
| |
| let camOn = false; |
| const videoElement = document.getElementById('camfeed'); |
| const canvasElement = document.getElementById('gestcv'); |
| const canvasCtx = canvasElement.getContext('2d'); |
| let hands = null; |
| let camera = null; |
| |
| function onResults(results) { |
| canvasCtx.save(); |
| canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height); |
| if (results.multiHandLandmarks) { |
| for (const landmarks of results.multiHandLandmarks) { |
| if (window.drawConnectors) drawConnectors(canvasCtx, landmarks, HAND_CONNECTIONS, { color: '#00d4ff', lineWidth: 2 }); |
| if (window.drawLandmarks) drawLandmarks(canvasCtx, landmarks, { color: '#f0a500', lineWidth: 1, radius: 2 }); |
| |
| |
| const isFist = landmarks[8].y > landmarks[6].y && landmarks[12].y > landmarks[10].y && landmarks[16].y > landmarks[14].y; |
| const isOpen = landmarks[8].y < landmarks[6].y && landmarks[12].y < landmarks[10].y; |
| const isPinch = Math.hypot(landmarks[8].x - landmarks[4].x, landmarks[8].y - landmarks[4].y) < 0.05; |
| |
| const gl = document.getElementById('gestlabel'); |
| if (gl) { |
| if (isPinch) { gl.textContent = "PINCH β ZOOM"; gl.style.opacity = '1'; } |
| else if (isFist) { gl.textContent = "FIST β SELECT"; gl.style.opacity = '1'; } |
| else if (isOpen) { gl.textContent = "OPEN PALM β IDLE"; gl.style.opacity = '1'; } |
| } |
| } |
| } |
| canvasCtx.restore(); |
| } |
| |
| async function toggleCamera() { |
| if (!camOn) { |
| try { |
| if (!hands && window.Hands) { |
| hands = new Hands({ locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}` }); |
| hands.setOptions({ maxNumHands: 1, modelComplexity: 1, minDetectionConfidence: 0.5, minTrackingConfidence: 0.5 }); |
| hands.onResults(onResults); |
| } |
| if (!camera && window.Camera) { |
| camera = new Camera(videoElement, { |
| onFrame: async () => { if (hands) await hands.send({ image: videoElement }); }, |
| width: 640, height: 480 |
| }); |
| } |
| if (camera) await camera.start(); |
| videoElement.style.display = 'block'; |
| const camoff = document.getElementById('camoff'); if (camoff) camoff.style.display = 'none'; |
| if (canvasElement) canvasElement.style.display = 'block'; |
| const camBtn = document.getElementById('camBtn'); if (camBtn) { camBtn.textContent = 'CAM ON'; camBtn.classList.add('on'); } |
| const camtoggle = document.getElementById('camtoggle'); if (camtoggle) camtoggle.textContent = 'DISABLE CAMERA'; |
| const visready = document.getElementById('visready'); if (visready) visready.textContent = 'β VISION: ACTIVE'; |
| const gestbar = document.getElementById('gestbar'); if (gestbar) gestbar.style.width = '100%'; |
| const m = MODULES.find(x => x.id === 'gesture'); if (m) { m.pct = 100; m.desc = 'Vision active'; } buildModPanel(); |
| camOn = true; notif('Vision module activated', 'ng'); |
| } catch (e) { console.error(e); notif('Camera access denied', ''); } |
| } else { |
| if (camera) await camera.stop(); |
| videoElement.style.display = 'none'; |
| const camoff = document.getElementById('camoff'); if (camoff) camoff.style.display = 'flex'; |
| if (canvasElement) canvasElement.style.display = 'none'; |
| const camBtn = document.getElementById('camBtn'); if (camBtn) { camBtn.textContent = 'CAM OFF'; camBtn.classList.remove('on'); } |
| const camtoggle = document.getElementById('camtoggle'); if (camtoggle) camtoggle.textContent = 'ENABLE CAMERA'; |
| const visready = document.getElementById('visready'); if (visready) visready.textContent = 'β VISION: STANDBY'; |
| const gestbar = document.getElementById('gestbar'); if (gestbar) gestbar.style.width = '0%'; |
| const m = MODULES.find(x => x.id === 'gesture'); if (m) { m.pct = 0; m.desc = 'Camera offline'; } buildModPanel(); |
| camOn = false; notif('Vision module disabled', ''); |
| } |
| } |
| |
| function toggleMic() { |
| const b = document.getElementById('micBtn'); if (!b) return; |
| const on = b.textContent === 'MIC ON'; |
| b.textContent = on ? 'MIC OFF' : 'MIC ON'; |
| on ? b.classList.remove('on') : b.classList.add('on'); |
| notif(!on ? 'Microphone enabled' : 'Microphone muted', !on ? 'ng' : ''); |
| } |
| |
| function notif(msg, cls = '') { |
| const c = document.getElementById('notif'); if (!c) return; |
| const el = document.createElement('div'); |
| el.className = 'nitem ' + cls; el.textContent = msg; c.appendChild(el); setTimeout(() => el.remove(), 3200); |
| } |
| |
| setTimeout(() => notif('JARVIS v2 initialized β neural interface ready', 'ng'), 500); |
| setTimeout(() => notif('Sovereign Cloud: Sync established', 'ng'), 1500); |
| </script> |
| </body> |
| </html> |
|
|