Spaces:
Running
Running
| """ | |
| ╔═══════════════════════════════════════════════════════════════╗ | |
| ║ GHOST — Private Intelligence Layer ║ | |
| ║ Build Small Hackathon 2026 · HuggingFace · Backyard AI ║ | |
| ║ Model : microsoft/Phi-3-mini-4k-instruct (3.8 B) ║ | |
| ╚═══════════════════════════════════════════════════════════════╝ | |
| """ | |
| import gradio as gr | |
| from huggingface_hub import InferenceClient | |
| import datetime, os, time | |
| # ───────────────────────────────────────────────────────────── | |
| # MODEL | |
| # ───────────────────────────────────────────────────────────── | |
| MODEL_ID = "microsoft/Phi-3-mini-4k-instruct" | |
| CLIENT = InferenceClient(MODEL_ID, token=os.environ.get("HF_TOKEN")) | |
| def ts(): | |
| return datetime.datetime.now().strftime("%H:%M:%S") | |
| def logline(icon, msg): | |
| return f"[{ts()}] {icon} {msg}" | |
| def call_llm(system, user, temperature=0.35, max_tokens=700): | |
| resp = CLIENT.chat_completion( | |
| messages=[{"role":"system","content":system}, | |
| {"role":"user", "content":user}], | |
| max_tokens=max_tokens, temperature=temperature, | |
| ) | |
| return resp.choices[0].message.content.strip() | |
| def stream_text(text, chunk=6): | |
| out = "" | |
| for i in range(0, len(text), chunk): | |
| out += text[i:i+chunk] | |
| yield out | |
| # ───────────────────────────────────────────────────────────── | |
| # HEADER HTML — cinematic ghost + starfield + emotion system | |
| # ───────────────────────────────────────────────────────────── | |
| HEADER = r""" | |
| <div id="gh-root"> | |
| <canvas id="gh-bg"></canvas> | |
| <div id="gh-wrap"> | |
| <!-- ═══ GHOST STAGE ═══ --> | |
| <div id="gh-stage" onclick="petGhost()" title="Pet me!"> | |
| <div class="ring r1"><span class="rdot"></span></div> | |
| <div class="ring r2"></div> | |
| <div class="ring r3"><span class="rdot rd2"></span></div> | |
| <div id="gh-glow"></div> | |
| <!-- accessories layer (above body, below everything else) --> | |
| <div id="gh-acc"> | |
| <div class="acc-heart h1">💜</div> | |
| <div class="acc-heart h2">💙</div> | |
| <div class="acc-heart h3">🩷</div> | |
| <div class="acc-star s1">✦</div> | |
| <div class="acc-star s2">✧</div> | |
| <div class="acc-star s3">✦</div> | |
| <div class="acc-sweat sw1">💧</div> | |
| </div> | |
| <svg id="gh-svg" viewBox="0 0 120 148" fill="none" | |
| xmlns="http://www.w3.org/2000/svg"> | |
| <!-- shadow --> | |
| <ellipse cx="60" cy="138" rx="26" ry="7" fill="#a78bfa18"/> | |
| <!-- body --> | |
| <path id="gh-body" | |
| d="M10 72 C10 36 32 10 60 10 C88 10 110 36 110 72 | |
| L110 116 C110 116 97 106 86 116 C75 126 73 108 60 108 | |
| C47 108 45 126 34 116 C23 106 10 116 10 116 Z" | |
| fill="url(#bodyG)" stroke="var(--gc)44" stroke-width="1.2"/> | |
| <!-- inner body shimmer --> | |
| <path | |
| d="M24 52 C24 36 40 20 60 18 C80 20 96 36 96 52 | |
| L93 82 C78 76 42 76 27 82 Z" | |
| fill="url(#shineG)" opacity="0.28"/> | |
| <!-- LEFT EYEBROW --> | |
| <path id="gh-lbrow" | |
| d="M26 50 Q36 45 46 50" | |
| stroke="var(--gc)88" stroke-width="2.5" | |
| stroke-linecap="round" fill="none"/> | |
| <!-- RIGHT EYEBROW --> | |
| <path id="gh-rbrow" | |
| d="M64 50 Q74 45 84 50" | |
| stroke="var(--gc)88" stroke-width="2.5" | |
| stroke-linecap="round" fill="none"/> | |
| <!-- Eyes group (idle blink) --> | |
| <g class="gh-blink"> | |
| <!-- LEFT EYE base --> | |
| <ellipse id="gh-leye" cx="36" cy="66" rx="9" ry="11" fill="#06061a"/> | |
| <!-- RIGHT EYE base --> | |
| <ellipse id="gh-reye" cx="74" cy="66" rx="9" ry="11" fill="#06061a"/> | |
| <!-- iris glow (wanders) --> | |
| <ellipse id="gh-liris" class="gh-iris" cx="36" cy="66" rx="5.5" ry="7" | |
| fill="var(--gc)" opacity="0.55"/> | |
| <ellipse id="gh-riris" class="gh-iris" cx="74" cy="66" rx="5.5" ry="7" | |
| fill="var(--gc)" opacity="0.55"/> | |
| <!-- eye shine (large) --> | |
| <circle cx="40" cy="60" r="3.2" fill="rgba(255,255,255,.96)"/> | |
| <circle cx="78" cy="60" r="3.2" fill="rgba(255,255,255,.96)"/> | |
| <!-- eye shine (small) --> | |
| <circle cx="33" cy="65" r="1.5" fill="rgba(255,255,255,.6)"/> | |
| <circle cx="71" cy="65" r="1.5" fill="rgba(255,255,255,.6)"/> | |
| </g> | |
| <!-- HEART eyes overlay (hidden by default) --> | |
| <g id="gh-hearts-eyes" opacity="0"> | |
| <path d="M28 60 C28 56 32 53 36 57 C40 53 44 56 44 60 | |
| C44 65 36 72 36 72 C36 72 28 65 28 60 Z" | |
| fill="#f472b6"/> | |
| <path d="M66 60 C66 56 70 53 74 57 C78 53 82 56 82 60 | |
| C82 65 74 72 74 72 C74 72 66 65 66 60 Z" | |
| fill="#f472b6"/> | |
| </g> | |
| <!-- STAR eyes overlay (hidden by default) --> | |
| <g id="gh-stars-eyes" opacity="0"> | |
| <text x="27" y="74" font-size="20" fill="#fbbf24">★</text> | |
| <text x="65" y="74" font-size="20" fill="#fbbf24">★</text> | |
| </g> | |
| <!-- SPIRAL eyes overlay (thinking — hidden by default) --> | |
| <g id="gh-spiral-eyes" opacity="0"> | |
| <text x="26" y="74" font-size="22" fill="var(--gc)">◎</text> | |
| <text x="64" y="74" font-size="22" fill="var(--gc)">◎</text> | |
| </g> | |
| <!-- MOUTH --> | |
| <path id="gh-mouth" | |
| d="M44 84 Q60 93 76 84" | |
| stroke="var(--gc)CC" stroke-width="2.8" | |
| stroke-linecap="round" fill="none"/> | |
| <!-- TONGUE (big smile only) --> | |
| <ellipse id="gh-tongue" cx="60" cy="92" rx="8" ry="5" | |
| fill="#f472b6" opacity="0"/> | |
| <!-- LEFT BLUSH --> | |
| <ellipse id="gh-lblush" cx="18" cy="80" rx="11" ry="6" | |
| fill="rgba(244,114,182,.25)" | |
| style="animation:blushPulse 2s ease-in-out infinite"/> | |
| <!-- RIGHT BLUSH --> | |
| <ellipse id="gh-rblush" cx="92" cy="80" rx="11" ry="6" | |
| fill="rgba(244,114,182,.25)" | |
| style="animation:blushPulse 2s ease-in-out infinite .4s"/> | |
| <!-- SWEAT DROP (thinking — hidden) --> | |
| <g id="gh-sweatdrop" opacity="0"> | |
| <path d="M96 30 C96 30 100 22 104 30 C104 35 100 38 96 30 Z" | |
| fill="#93c5fd"/> | |
| </g> | |
| <defs> | |
| <linearGradient id="bodyG" x1="10" y1="10" x2="110" y2="130" | |
| gradientUnits="userSpaceOnUse"> | |
| <stop offset="0%" stop-color="#f5f3ff"/> | |
| <stop offset="100%" stop-color="#ddd6fe"/> | |
| </linearGradient> | |
| <radialGradient id="shineG" cx="50%" cy="18%" r="65%"> | |
| <stop offset="0%" stop-color="white" stop-opacity=".9"/> | |
| <stop offset="100%" stop-color="white" stop-opacity="0"/> | |
| </radialGradient> | |
| </defs> | |
| </svg> | |
| <!-- Particle canvas --> | |
| <canvas id="gh-particles"></canvas> | |
| <!-- Speech bubble --> | |
| <div id="gh-bubble"></div> | |
| </div> | |
| <!-- ═══ COPY ═══ --> | |
| <div id="gh-copy"> | |
| <div id="gh-badge"> | |
| <span class="bdot"></span> | |
| <span id="gh-status">GHOST · 3.8B · PRIVATE</span> | |
| </div> | |
| <h1 id="gh-title"> | |
| <span class="gl" style="--i:0">G</span> | |
| <span class="gl" style="--i:1">H</span> | |
| <span class="gl" style="--i:2">O</span> | |
| <span class="gl" style="--i:3">S</span> | |
| <span class="gl" style="--i:4">T</span> | |
| </h1> | |
| <p id="gh-sub"> | |
| Your private intelligence layer.<br> | |
| No cloud. No logs. Just you. | |
| </p> | |
| <div id="gh-chips"> | |
| <span class="chip chip-p" onclick="setTab('chief')">📋 Chief of Staff</span> | |
| <span class="chip chip-b" onclick="setTab('letter')">💌 Unsent Letter</span> | |
| <span class="chip chip-g" onclick="setTab('reality')">🔍 Reality Check</span> | |
| </div> | |
| <div id="gh-model">✦ Phi-3-mini · 3.8B parameters · Tiny Titan eligible</div> | |
| </div> | |
| </div> | |
| </div> | |
| <style> | |
| /* Accessories */ | |
| .acc-heart,.acc-star,.acc-sweat{ | |
| position:absolute;font-size:16px;opacity:0; | |
| pointer-events:none;transition:opacity .4s; | |
| } | |
| .h1{top:5%; left:50%; animation:floatAcc 2.2s ease-in-out infinite .0s;} | |
| .h2{top:15%;left:10%; animation:floatAcc 2.6s ease-in-out infinite .3s;} | |
| .h3{top:10%;right:10%; animation:floatAcc 2s ease-in-out infinite .6s;} | |
| .s1{top:5%; left:40%; animation:floatAcc 2.4s ease-in-out infinite .1s;} | |
| .s2{top:0%; left:60%; animation:floatAcc 2.8s ease-in-out infinite .5s;} | |
| .s3{top:8%; right:20%; animation:floatAcc 2.2s ease-in-out infinite .8s;} | |
| .sw1{top:2%;right:5%; animation:floatAcc 1.8s ease-in-out infinite 0s;} | |
| @keyframes floatAcc{ | |
| 0%,100%{transform:translateY(0) scale(1);} | |
| 50%{transform:translateY(-12px) scale(1.1);} | |
| } | |
| @keyframes blushPulse{ | |
| 0%,100%{opacity:.25;transform:scale(1);} | |
| 50%{opacity:.45;transform:scale(1.15);} | |
| } | |
| /* ── Idle blink ── */ | |
| .gh-blink{ | |
| transform-box: fill-box; | |
| transform-origin: center; | |
| animation: ghBlink 4.6s ease-in-out infinite; | |
| } | |
| @keyframes ghBlink{ | |
| 0%,94%,100%{ transform:scaleY(1); } | |
| 96%{ transform:scaleY(.08); } | |
| 98%{ transform:scaleY(1); } | |
| } | |
| /* ── Eyes wander like they're curious ── */ | |
| .gh-iris{ animation: irisWander 6s ease-in-out infinite; } | |
| @keyframes irisWander{ | |
| 0%,100%{ transform:translate(0,0); } | |
| 20%{ transform:translate(1.6px,-1px); } | |
| 40%{ transform:translate(-1.6px,.6px); } | |
| 60%{ transform:translate(1px,1.2px); } | |
| 80%{ transform:translate(-1px,-.6px); } | |
| } | |
| /* ── Speech bubble ── */ | |
| #gh-bubble{ | |
| position:absolute; | |
| top:-4px; left:58%; | |
| min-width:118px; max-width:200px; | |
| background:rgba(10,10,26,.94); | |
| border:1px solid color-mix(in srgb,var(--gc) 45%,transparent); | |
| border-radius:14px; | |
| padding:8px 13px; | |
| font-family:'Space Mono',monospace; | |
| font-size:11px; color:var(--gc); | |
| text-align:center; line-height:1.4; | |
| opacity:0; transform:translateY(8px) scale(.9); | |
| transition:opacity .3s ease, transform .3s ease, color .7s, border-color .7s; | |
| pointer-events:none; z-index:8; | |
| backdrop-filter:blur(8px); | |
| } | |
| #gh-bubble::after{ | |
| content:''; position:absolute; | |
| bottom:-6px; left:22px; | |
| width:12px; height:12px; | |
| background:rgba(10,10,26,.94); | |
| border-right:1px solid color-mix(in srgb,var(--gc) 45%,transparent); | |
| border-bottom:1px solid color-mix(in srgb,var(--gc) 45%,transparent); | |
| transform:rotate(45deg); | |
| transition:border-color .7s; | |
| } | |
| #gh-bubble.show{ opacity:1; transform:translateY(0) scale(1); } | |
| /* ── Pet bounce on click ── */ | |
| #gh-stage{ cursor:pointer; } | |
| .gh-pet-bounce{ animation: petBounce .52s ease !important; } | |
| @keyframes petBounce{ | |
| 0% { transform:translateY(0) rotate(-1.8deg) scale(1,1); } | |
| 30% { transform:translateY(-24px) rotate(5deg) scale(1.1,.92); } | |
| 55% { transform:translateY(3px) rotate(-4deg) scale(.95,1.06); } | |
| 100%{ transform:translateY(0) rotate(-1.8deg) scale(1,1); } | |
| } | |
| </style> | |
| <script> | |
| /* ═══ GHOST EMOTION ENGINE ═══ */ | |
| const EMOTIONS = { | |
| idle:{ | |
| eyeRx:9,eyeRy:11,irisRx:5.5,irisRy:7, | |
| lbrow:'M26 50 Q36 45 46 50',rbrow:'M64 50 Q74 45 84 50', | |
| mouth:'M44 84 Q60 93 76 84',tongue:0, | |
| heartsEyes:0,starsEyes:0,spiralEyes:0,sweat:0, | |
| acc:'none',gc:'#a78bfa', | |
| status:'GHOST · 3.8B · PRIVATE', | |
| }, | |
| chief:{ | |
| eyeRx:10,eyeRy:7,irisRx:6.5,irisRy:4.5, | |
| lbrow:'M26 48 Q36 43 46 47',rbrow:'M64 47 Q74 43 84 48', | |
| mouth:'M46 86 Q60 87 74 86',tongue:0, | |
| heartsEyes:0,starsEyes:0,spiralEyes:0,sweat:0, | |
| acc:'none',gc:'#60a5fa', | |
| status:'CHIEF OF STAFF · ANALYZING', | |
| }, | |
| letter:{ | |
| eyeRx:9,eyeRy:11,irisRx:5.5,irisRy:7, | |
| lbrow:'M26 47 Q36 42 46 47',rbrow:'M64 47 Q74 42 84 47', | |
| mouth:'M41 82 Q60 96 79 82',tongue:0.8, | |
| heartsEyes:1,starsEyes:0,spiralEyes:0,sweat:0, | |
| acc:'hearts',gc:'#f472b6', | |
| status:'UNSENT LETTER · FEELING', | |
| }, | |
| reality:{ | |
| eyeRx:11,eyeRy:13,irisRx:7,irisRy:9, | |
| lbrow:'M26 50 Q36 47 46 52',rbrow:'M64 52 Q74 47 84 50', | |
| mouth:'M44 90 Q60 83 76 90',tongue:0, | |
| heartsEyes:0,starsEyes:0,spiralEyes:0,sweat:0, | |
| acc:'none',gc:'#f59e0b', | |
| status:'REALITY CHECK · SCANNING', | |
| }, | |
| think:{ | |
| eyeRx:9,eyeRy:6,irisRx:5.5,irisRy:3.5, | |
| lbrow:'M26 50 Q36 46 46 50',rbrow:'M64 50 Q74 46 84 50', | |
| mouth:'M48 87 Q60 86 72 87',tongue:0, | |
| heartsEyes:0,starsEyes:0,spiralEyes:1,sweat:1, | |
| acc:'none',gc:'#818cf8', | |
| status:'GHOST IS THINKING...', | |
| }, | |
| done:{ | |
| eyeRx:9,eyeRy:11,irisRx:5.5,irisRy:7, | |
| lbrow:'M26 46 Q36 41 46 46',rbrow:'M64 46 Q74 41 84 46', | |
| mouth:'M38 82 Q60 98 82 82',tongue:0.9, | |
| heartsEyes:0,starsEyes:1,spiralEyes:0,sweat:0, | |
| acc:'stars',gc:'#34d399', | |
| status:'DONE · REVIEW ABOVE ✦', | |
| }, | |
| }; | |
| function lerp(a,b,t){ return a+(b-a)*t; } | |
| let currentState = 'idle'; | |
| function applyEmotion(key,fast){ | |
| if(!EMOTIONS[key]) key='idle'; | |
| currentState = key; | |
| const e = EMOTIONS[key]; | |
| const r = document.documentElement; | |
| /* ghost color cascade */ | |
| r.style.setProperty('--gc', e.gc); | |
| /* SVG elements */ | |
| const set=(id,attr,val)=>{ | |
| const el=document.getElementById(id); | |
| if(el) el.setAttribute(attr,val); | |
| }; | |
| const setO=(id,val)=>{ | |
| const el=document.getElementById(id); | |
| if(el){ el.style.transition='opacity .4s'; el.style.opacity=val; } | |
| }; | |
| set('gh-leye','rx',e.eyeRx); set('gh-leye','ry',e.eyeRy); | |
| set('gh-reye','rx',e.eyeRx); set('gh-reye','ry',e.eyeRy); | |
| set('gh-liris','rx',e.irisRx); set('gh-liris','ry',e.irisRy); | |
| set('gh-riris','rx',e.irisRx); set('gh-riris','ry',e.irisRy); | |
| set('gh-lbrow','d',e.lbrow); | |
| set('gh-rbrow','d',e.rbrow); | |
| set('gh-mouth','d',e.mouth); | |
| setO('gh-tongue', e.tongue); | |
| setO('gh-hearts-eyes', e.heartsEyes); | |
| setO('gh-stars-eyes', e.starsEyes); | |
| setO('gh-spiral-eyes', e.spiralEyes); | |
| setO('gh-sweatdrop', e.sweat); | |
| /* accessories */ | |
| const hearts = document.querySelectorAll('.acc-heart'); | |
| const stars = document.querySelectorAll('.acc-star'); | |
| const sweat = document.querySelectorAll('.acc-sweat'); | |
| hearts.forEach(h=>h.style.opacity = e.acc==='hearts'?'1':'0'); | |
| stars.forEach(s=>s.style.opacity = e.acc==='stars' ?'1':'0'); | |
| sweat.forEach(s=>s.style.opacity = e.sweat ?'1':'0'); | |
| /* badge dot + status */ | |
| document.querySelectorAll('.bdot').forEach(d=>{ | |
| d.style.background=e.gc; | |
| d.style.boxShadow=`0 0 8px ${e.gc}`; | |
| }); | |
| const st=document.getElementById('gh-status'); | |
| if(st) st.textContent=e.status; | |
| } | |
| function setTab(mode){ applyEmotion(mode); } | |
| /* ═══ PET PERSONALITY ═══ */ | |
| const IDLE_LINES = [ | |
| "👋 hi, I'm Ghost", | |
| "tap me anytime", | |
| "I keep your secrets 🤫", | |
| "ready when you are ✨", | |
| "your words stay yours 💜", | |
| "*floats happily*", | |
| "pick a mode above 👆", | |
| "I don't judge, I just help", | |
| "私はあなたの秘密を守ります", | |
| "no cloud, just us 🌙", | |
| ]; | |
| function showBubble(text, ms){ | |
| const b=document.getElementById('gh-bubble'); | |
| if(!b) return; | |
| b.textContent=text; | |
| b.classList.add('show'); | |
| clearTimeout(b.__t); | |
| b.__t=setTimeout(()=>b.classList.remove('show'), ms||2200); | |
| } | |
| function petGhost(){ | |
| const svg=document.getElementById('gh-svg'); | |
| if(svg){ | |
| svg.classList.remove('gh-pet-bounce'); | |
| void svg.offsetWidth; /* restart animation */ | |
| svg.classList.add('gh-pet-bounce'); | |
| setTimeout(()=>svg.classList.remove('gh-pet-bounce'),520); | |
| } | |
| showBubble(IDLE_LINES[Math.floor(Math.random()*IDLE_LINES.length)], 1900); | |
| if(window.ghostBurst) ghostBurst(); | |
| if(currentState==='idle'){ | |
| applyEmotion('done'); | |
| setTimeout(()=>{ if(currentState==='done') applyEmotion('idle'); },1700); | |
| } | |
| } | |
| /* gentle idle chatter so Ghost feels alive */ | |
| setInterval(()=>{ | |
| if(currentState==='idle'){ | |
| showBubble(IDLE_LINES[Math.floor(Math.random()*IDLE_LINES.length)],2400); | |
| } | |
| },10000); | |
| setTimeout(()=>showBubble("👋 hi, I'm Ghost — tap me!",2800),1200); | |
| /* Watch Gradio buttons */ | |
| function watchBtns(){ | |
| document.querySelectorAll('button').forEach(btn=>{ | |
| if(btn.__ghWatched) return; | |
| btn.__ghWatched=true; | |
| const t=btn.textContent.trim().toLowerCase(); | |
| if(t.includes('analyze')){ | |
| btn.addEventListener('click',()=>{ | |
| applyEmotion('think'); | |
| setTimeout(()=>applyEmotion('chief'),900); | |
| setTimeout(()=>{ applyEmotion('done'); ghostBurst(); }, 4000); | |
| }); | |
| } else if(t.includes('write')){ | |
| btn.addEventListener('click',()=>{ | |
| applyEmotion('think'); | |
| setTimeout(()=>applyEmotion('letter'),900); | |
| setTimeout(()=>{ applyEmotion('done'); ghostBurst(); }, 4000); | |
| }); | |
| } else if(t.includes('reality')){ | |
| btn.addEventListener('click',()=>{ | |
| applyEmotion('think'); | |
| setTimeout(()=>applyEmotion('reality'),900); | |
| setTimeout(()=>{ applyEmotion('done'); ghostBurst(); }, 4000); | |
| }); | |
| } | |
| }); | |
| } | |
| setInterval(watchBtns,1500); | |
| /* ═══ STARFIELD ═══ */ | |
| (function(){ | |
| const cv=document.getElementById('gh-bg'); | |
| if(!cv) return; | |
| const ctx=cv.getContext('2d'); | |
| function resize(){cv.width=cv.offsetWidth;cv.height=cv.offsetHeight;} | |
| resize(); | |
| window.addEventListener('resize',resize); | |
| const stars=Array.from({length:240},()=>({ | |
| x:Math.random(),y:Math.random(), | |
| r:Math.random()*1.6+.2, | |
| base:Math.random()*.9+.1, | |
| spd:Math.random()*.008+.001, | |
| t:Math.random()*Math.PI*2, | |
| c:Math.random()<.12?'#c4b5fd':Math.random()<.08?'#93c5fd':'#fff', | |
| })); | |
| const nebs=[ | |
| {x:.12,y:.22,r:180,c:'rgba(109,40,217,.13)'}, | |
| {x:.88,y:.7, r:160,c:'rgba(30,58,138,.1)'}, | |
| {x:.5, y:.5, r:110,c:'rgba(157,23,77,.07)'}, | |
| {x:.7, y:.2, r:90, c:'rgba(6,78,59,.06)'}, | |
| ]; | |
| const shoots=[]; | |
| function shoot(){ | |
| shoots.push({x:Math.random()*.8,y:Math.random()*.6, | |
| len:60+Math.random()*100,spd:7+Math.random()*6, | |
| life:1,a:Math.PI*.25}); | |
| if(Math.random()<.3) shoots.push({x:Math.random()*.8,y:Math.random()*.5, | |
| len:40+Math.random()*80,spd:5+Math.random()*5,life:1,a:Math.PI*.2}); | |
| } | |
| setInterval(shoot,2600); | |
| function draw(){ | |
| const W=cv.width,H=cv.height; | |
| ctx.clearRect(0,0,W,H); | |
| nebs.forEach(n=>{ | |
| const g=ctx.createRadialGradient(n.x*W,n.y*H,0,n.x*W,n.y*H,n.r); | |
| g.addColorStop(0,n.c);g.addColorStop(1,'transparent'); | |
| ctx.fillStyle=g;ctx.beginPath(); | |
| ctx.arc(n.x*W,n.y*H,n.r,0,Math.PI*2);ctx.fill(); | |
| }); | |
| stars.forEach(s=>{ | |
| s.t+=s.spd; | |
| ctx.globalAlpha=((Math.sin(s.t)+1)/2)*s.base; | |
| ctx.fillStyle=s.c;ctx.beginPath(); | |
| ctx.arc(s.x*W,s.y*H,s.r,0,Math.PI*2);ctx.fill(); | |
| }); | |
| ctx.globalAlpha=1; | |
| for(let i=shoots.length-1;i>=0;i--){ | |
| const s=shoots[i]; | |
| const g=ctx.createLinearGradient(s.x*W,s.y*H, | |
| s.x*W-Math.cos(s.a)*s.len,s.y*H-Math.sin(s.a)*s.len); | |
| g.addColorStop(0,`rgba(196,181,253,${s.life})`); | |
| g.addColorStop(.5,`rgba(167,139,250,${s.life*.4})`); | |
| g.addColorStop(1,'transparent'); | |
| ctx.beginPath(); | |
| ctx.moveTo(s.x*W,s.y*H); | |
| ctx.lineTo(s.x*W-Math.cos(s.a)*s.len,s.y*H-Math.sin(s.a)*s.len); | |
| ctx.strokeStyle=g;ctx.lineWidth=1.8;ctx.stroke(); | |
| s.x+=Math.cos(s.a)*s.spd/W*2.2; | |
| s.y+=Math.sin(s.a)*s.spd/H*2.2; | |
| s.life-=.018; | |
| if(s.life<=0) shoots.splice(i,1); | |
| } | |
| requestAnimationFrame(draw); | |
| } | |
| draw(); | |
| })(); | |
| /* ═══ PARTICLE BURST ═══ */ | |
| (function(){ | |
| const cv=document.getElementById('gh-particles'); | |
| if(!cv) return; | |
| const ctx=cv.getContext('2d'); | |
| cv.width=340;cv.height=340; | |
| const parts=[]; | |
| window.ghostBurst=function(){ | |
| const gc=getComputedStyle(document.documentElement) | |
| .getPropertyValue('--gc').trim()||'#34d399'; | |
| const colors=[gc,'#fff','#f472b6','#fbbf24','#60a5fa']; | |
| for(let i=0;i<40;i++){ | |
| const a=Math.random()*Math.PI*2; | |
| const sp=3+Math.random()*7; | |
| parts.push({ | |
| x:170,y:170, | |
| vx:Math.cos(a)*sp,vy:Math.sin(a)*sp, | |
| r:1.5+Math.random()*4, | |
| life:1, | |
| color:colors[Math.floor(Math.random()*colors.length)], | |
| shape:Math.random()<.3?'star':'circle', | |
| }); | |
| } | |
| }; | |
| function draw(){ | |
| ctx.clearRect(0,0,340,340); | |
| for(let i=parts.length-1;i>=0;i--){ | |
| const p=parts[i]; | |
| ctx.globalAlpha=p.life; | |
| ctx.fillStyle=p.color; | |
| ctx.beginPath(); | |
| ctx.arc(p.x,p.y,p.r,0,Math.PI*2); | |
| ctx.fill(); | |
| p.x+=p.vx;p.y+=p.vy; | |
| p.vy+=.15;p.vx*=.98; | |
| p.life-=.022; | |
| if(p.life<=0) parts.splice(i,1); | |
| } | |
| ctx.globalAlpha=1; | |
| requestAnimationFrame(draw); | |
| } | |
| draw(); | |
| })(); | |
| </script> | |
| """ | |
| # ───────────────────────────────────────────────────────────── | |
| # CSS | |
| # ───────────────────────────────────────────────────────────── | |
| CSS = """ | |
| @import url('https://fonts.googleapis.com/css2?family=Syne:wght@300;400;600;700;800&family=Space+Mono:wght@400;700&display=swap'); | |
| :root{ | |
| --gc :#a78bfa; | |
| --bg :#000008; | |
| --glass :rgba(255,255,255,.025); | |
| --border :rgba(139,92,246,.18); | |
| --text :#f1f5f9; | |
| --muted :#374151; | |
| --mono :'Space Mono',monospace; | |
| --sans :'Syne',sans-serif; | |
| --r :16px; | |
| --ease :cubic-bezier(.16,1,.3,1); | |
| } | |
| *,*::before,*::after{box-sizing:border-box;margin:0;padding:0;} | |
| ::-webkit-scrollbar{width:3px;} | |
| ::-webkit-scrollbar-track{background:transparent;} | |
| ::-webkit-scrollbar-thumb{background:rgba(139,92,246,.4);border-radius:99px;} | |
| body,.gradio-container{ | |
| background:var(--bg) !important; | |
| font-family:var(--sans) !important; | |
| color:var(--text) !important; | |
| } | |
| .gradio-container{max-width:1060px !important;margin:0 auto !important;} | |
| .gradio-container>div,.gr-block{background:transparent !important;} | |
| /* ════ HEADER ════ */ | |
| #gh-root{ | |
| position:relative;overflow:hidden; | |
| background:linear-gradient(168deg,#0e0e26 0%,#070714 55%,var(--bg) 100%); | |
| border-bottom:1px solid rgba(139,92,246,.14); | |
| min-height:210px; | |
| } | |
| #gh-bg{ | |
| position:absolute;inset:0; | |
| width:100%;height:100%; | |
| pointer-events:none; | |
| } | |
| #gh-wrap{ | |
| position:relative;z-index:3; | |
| display:flex;align-items:center;gap:44px; | |
| padding:46px 54px 38px;flex-wrap:wrap; | |
| } | |
| /* Stage */ | |
| #gh-stage{ | |
| position:relative;flex-shrink:0; | |
| width:170px;height:178px; | |
| display:flex;align-items:center;justify-content:center; | |
| } | |
| #gh-svg{ | |
| width:110px;height:132px; | |
| position:relative;z-index:5; | |
| filter: | |
| drop-shadow(0 0 36px color-mix(in srgb,var(--gc) 60%,transparent)) | |
| drop-shadow(0 0 12px color-mix(in srgb,var(--gc) 30%,transparent)); | |
| animation:ghFloat 3.4s ease-in-out infinite; | |
| transition:filter .7s ease; | |
| } | |
| #gh-glow{ | |
| position:absolute;top:50%;left:50%; | |
| transform:translate(-50%,-50%); | |
| width:128px;height:128px;border-radius:50%; | |
| background:radial-gradient(circle, | |
| color-mix(in srgb,var(--gc) 22%,transparent),transparent 72%); | |
| animation:haloPulse 2.8s ease-in-out infinite; | |
| transition:background .7s ease;z-index:2; | |
| } | |
| /* Rings */ | |
| .ring{ | |
| position:absolute;top:50%;left:50%; | |
| border-radius:50%; | |
| border:1px solid rgba(167,139,250,.1); | |
| } | |
| .r1{width:132px;height:132px;animation:spinCW 12s linear infinite;} | |
| .r2{width:166px;height:166px;border-style:dashed; | |
| border-color:rgba(167,139,250,.06);animation:spinCCW 18s linear infinite;} | |
| .r3{width:206px;height:206px;border-color:rgba(96,165,250,.04); | |
| animation:spinCW 25s linear infinite;} | |
| .rdot{ | |
| position:absolute;width:7px;height:7px;border-radius:50%; | |
| top:-3px;left:50%;transform:translateX(-50%); | |
| background:var(--gc);box-shadow:0 0 10px var(--gc); | |
| transition:background .7s,box-shadow .7s; | |
| } | |
| .rd2{top:auto;bottom:-3px;background:#60a5fa;box-shadow:0 0 10px #60a5fa;} | |
| #gh-acc{position:absolute;inset:0;z-index:6;pointer-events:none;} | |
| #gh-particles{ | |
| position:absolute;top:50%;left:50%; | |
| transform:translate(-50%,-50%); | |
| width:340px;height:340px; | |
| pointer-events:none;z-index:7; | |
| } | |
| /* Copy */ | |
| #gh-copy{flex:1;min-width:240px;} | |
| #gh-badge{ | |
| display:inline-flex;align-items:center;gap:8px; | |
| font-family:var(--mono);font-size:10px; | |
| color:var(--gc);letter-spacing:.1em; | |
| background:color-mix(in srgb,var(--gc) 10%,transparent); | |
| border:1px solid color-mix(in srgb,var(--gc) 32%,transparent); | |
| border-radius:100px;padding:5px 15px;margin-bottom:16px; | |
| transition:all .7s ease; | |
| } | |
| .bdot{ | |
| width:7px;height:7px;border-radius:50%; | |
| background:var(--gc);box-shadow:0 0 8px var(--gc); | |
| animation:blink 1.3s ease infinite; | |
| transition:background .7s,box-shadow .7s; | |
| } | |
| #gh-title{ | |
| font-size:70px;font-weight:800; | |
| letter-spacing:-.05em;line-height:1; | |
| margin-bottom:12px;display:flex; | |
| } | |
| .gl{ | |
| display:inline-block; | |
| background:linear-gradient(135deg,#fff 0%,#c4b5fd 40%,#818cf8 66%,#60a5fa 100%); | |
| -webkit-background-clip:text;-webkit-text-fill-color:transparent; | |
| background-clip:text; | |
| animation:letterWave 5s ease-in-out infinite; | |
| animation-delay:calc(var(--i) * .14s); | |
| } | |
| #gh-sub{font-size:14px;color:#4b5563;line-height:1.7;margin-bottom:20px;} | |
| #gh-chips{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px;} | |
| .chip{ | |
| font-size:11px;font-family:var(--mono);letter-spacing:.04em; | |
| padding:6px 15px;border-radius:100px;border:1px solid; | |
| cursor:pointer;transition:all .22s var(--ease);user-select:none; | |
| } | |
| .chip:hover{transform:translateY(-3px);} | |
| .chip-p{color:#a78bfa;background:rgba(167,139,250,.08);border-color:rgba(167,139,250,.26);} | |
| .chip-p:hover{background:rgba(167,139,250,.18);box-shadow:0 4px 16px rgba(167,139,250,.2);} | |
| .chip-b{color:#60a5fa;background:rgba(96,165,250,.08);border-color:rgba(96,165,250,.26);} | |
| .chip-b:hover{background:rgba(96,165,250,.18);box-shadow:0 4px 16px rgba(96,165,250,.2);} | |
| .chip-g{color:#34d399;background:rgba(52,211,153,.08);border-color:rgba(52,211,153,.26);} | |
| .chip-g:hover{background:rgba(52,211,153,.18);box-shadow:0 4px 16px rgba(52,211,153,.2);} | |
| #gh-model{font-family:var(--mono);font-size:10px;color:#1f2937;letter-spacing:.08em;} | |
| /* ════ SECTION LABEL ════ */ | |
| .slabel{ | |
| font-family:var(--mono);font-size:9px; | |
| color:#1f2937;letter-spacing:.18em;text-transform:uppercase; | |
| padding:22px 26px 8px; | |
| } | |
| /* ════ INPUTS ════ */ | |
| textarea,input[type=text]{ | |
| background:var(--glass) !important; | |
| border:1px solid var(--border) !important; | |
| border-radius:var(--r) !important; | |
| color:var(--text) !important; | |
| font-family:var(--sans) !important; | |
| font-size:14px !important;line-height:1.78 !important; | |
| padding:18px 20px !important; | |
| transition:border-color .25s,box-shadow .25s !important; | |
| resize:none !important; | |
| backdrop-filter:blur(14px) !important; | |
| -webkit-backdrop-filter:blur(14px) !important; | |
| } | |
| textarea:focus,input[type=text]:focus{ | |
| border-color:color-mix(in srgb,var(--gc) 75%,transparent) !important; | |
| box-shadow:0 0 0 3px color-mix(in srgb,var(--gc) 10%,transparent), | |
| 0 0 30px color-mix(in srgb,var(--gc) 7%,transparent) !important; | |
| outline:none !important; | |
| } | |
| /* ════ LABELS ════ */ | |
| label>span:first-child{ | |
| color:#374151 !important;font-size:10px !important; | |
| font-family:var(--mono) !important; | |
| letter-spacing:.14em !important;text-transform:uppercase !important; | |
| } | |
| /* ════ BUTTON ════ */ | |
| button.lg{ | |
| position:relative !important;overflow:hidden !important; | |
| background:linear-gradient(135deg, | |
| color-mix(in srgb,var(--gc) 90%,#000) 0%, | |
| color-mix(in srgb,var(--gc) 60%,#4f46e5) 100%) !important; | |
| border:none !important;border-radius:14px !important; | |
| color:#fff !important;font-family:var(--sans) !important; | |
| font-size:15px !important;font-weight:700 !important; | |
| padding:18px 26px !important;cursor:pointer !important; | |
| letter-spacing:.02em !important;width:100% !important; | |
| box-shadow:0 4px 30px color-mix(in srgb,var(--gc) 45%,transparent), | |
| 0 0 0 1px rgba(255,255,255,.07) inset !important; | |
| transition:all .28s var(--ease) !important; | |
| } | |
| button.lg::before{ | |
| content:'';position:absolute; | |
| top:0;left:-120%;width:60%;height:100%; | |
| background:linear-gradient(90deg,transparent,rgba(255,255,255,.14),transparent); | |
| animation:shimmer 3.2s ease infinite; | |
| } | |
| button.lg:hover{ | |
| transform:translateY(-3px) !important; | |
| box-shadow:0 12px 48px color-mix(in srgb,var(--gc) 65%,transparent), | |
| 0 0 0 1px rgba(255,255,255,.12) inset !important; | |
| } | |
| button.lg:active{transform:translateY(-1px) !important;} | |
| /* ════ OUTPUT BOX ════ */ | |
| .out-box textarea{ | |
| background:color-mix(in srgb,var(--gc) 3%,transparent) !important; | |
| border-color:color-mix(in srgb,var(--gc) 25%,transparent) !important; | |
| line-height:1.9 !important; | |
| transition:border-color .7s,background .7s !important; | |
| } | |
| /* ════ LOG BOX ════ */ | |
| .log-box textarea{ | |
| background:rgba(0,0,0,.72) !important; | |
| border-color:rgba(52,211,153,.16) !important; | |
| color:#34d399 !important; | |
| font-family:var(--mono) !important; | |
| font-size:11px !important;letter-spacing:.04em !important; | |
| line-height:2 !important; | |
| text-shadow:0 0 12px rgba(52,211,153,.35) !important; | |
| } | |
| /* ════ RADIO ════ */ | |
| .tone-wrap fieldset{border:none !important;padding:0 !important;} | |
| .tone-wrap label{ | |
| background:var(--glass) !important; | |
| border:1px solid var(--border) !important; | |
| border-radius:10px !important;color:#6b7280 !important; | |
| font-size:11px !important;font-family:var(--mono) !important; | |
| padding:8px 16px !important;cursor:pointer !important; | |
| transition:all .22s var(--ease) !important; | |
| margin:0 4px 4px 0 !important; | |
| display:inline-flex !important;align-items:center !important; | |
| backdrop-filter:blur(8px) !important; | |
| } | |
| .tone-wrap label:hover{ | |
| border-color:color-mix(in srgb,var(--gc) 50%,transparent) !important; | |
| color:var(--gc) !important;transform:translateY(-2px) !important; | |
| } | |
| .tone-wrap label:has(input:checked){ | |
| background:color-mix(in srgb,var(--gc) 14%,transparent) !important; | |
| border-color:color-mix(in srgb,var(--gc) 55%,transparent) !important; | |
| color:var(--gc) !important; | |
| box-shadow:0 0 18px color-mix(in srgb,var(--gc) 18%,transparent) !important; | |
| } | |
| /* ════ TABS ════ */ | |
| .tabs>.tab-nav{ | |
| background:rgba(0,0,0,.38) !important; | |
| border-bottom:1px solid rgba(139,92,246,.1) !important; | |
| padding:0 26px !important; | |
| backdrop-filter:blur(10px) !important; | |
| } | |
| .tab-nav button{ | |
| color:#374151 !important;font-family:var(--mono) !important; | |
| font-size:11px !important;letter-spacing:.1em !important; | |
| text-transform:uppercase !important;padding:14px 22px !important; | |
| background:transparent !important;border:none !important; | |
| border-bottom:2px solid transparent !important; | |
| transition:all .22s var(--ease) !important;cursor:pointer !important; | |
| } | |
| .tab-nav button:hover{color:#6b7280 !important;} | |
| .tab-nav button.selected{ | |
| color:var(--gc) !important; | |
| border-bottom-color:var(--gc) !important; | |
| text-shadow:0 0 22px color-mix(in srgb,var(--gc) 70%,transparent) !important; | |
| transition:color .7s,border-bottom-color .7s,text-shadow .7s !important; | |
| } | |
| /* ════ FOOTER ════ */ | |
| #gh-footer{ | |
| display:flex;align-items:center;justify-content:center; | |
| gap:10px;padding:20px 40px;flex-wrap:wrap; | |
| background:rgba(0,0,0,.3); | |
| border-top:1px solid rgba(139,92,246,.07); | |
| backdrop-filter:blur(10px); | |
| } | |
| /* ════ ANIMATIONS ════ */ | |
| @keyframes ghFloat{ | |
| 0%,100%{transform:translateY(0) rotate(-1.8deg) scale(1);} | |
| 33% {transform:translateY(-14px) rotate(2deg) scale(1.02);} | |
| 66% {transform:translateY(-6px) rotate(-1deg) scale(1.008);} | |
| } | |
| @keyframes haloPulse{ | |
| 0%,100%{opacity:.38;transform:translate(-50%,-50%) scale(1);} | |
| 50% {opacity:.92;transform:translate(-50%,-50%) scale(1.26);} | |
| } | |
| @keyframes spinCW{ | |
| from{transform:translate(-50%,-50%) rotate(0deg);} | |
| to{transform:translate(-50%,-50%) rotate(360deg);} | |
| } | |
| @keyframes spinCCW{ | |
| from{transform:translate(-50%,-50%) rotate(0deg);} | |
| to{transform:translate(-50%,-50%) rotate(-360deg);} | |
| } | |
| @keyframes blink{0%,100%{opacity:1;}50%{opacity:.12;}} | |
| @keyframes letterWave{ | |
| 0%,100%{transform:translateY(0);} | |
| 50%{transform:translateY(-4px);} | |
| } | |
| @keyframes shimmer{ | |
| 0%{left:-120%;}60%{left:160%;}100%{left:160%;} | |
| } | |
| @keyframes blushPulse{ | |
| 0%,100%{opacity:.25;transform:scale(1);} | |
| 50%{opacity:.5;transform:scale(1.18);} | |
| } | |
| """ | |
| # ───────────────────────────────────────────────────────────── | |
| # PROMPTS | |
| # ───────────────────────────────────────────────────────────── | |
| SYS_CHIEF = "You are GHOST, a razor-sharp private executive assistant. Extract only what matters. Be direct and specific." | |
| SYS_LETTER = "You are GHOST. Help people say what they have always meant to say. Write in their voice — completely human, never robotic." | |
| SYS_REAL = "You are GHOST, a sharp-eyed private advisor. Find what others miss. Be direct and specific." | |
| TONE_MAP = { | |
| "Warm & Loving": "warm, loving, deeply heartfelt", | |
| "Professional": "professional, respectful, clear", | |
| "Honest & Direct": "honest, direct, quietly courageous", | |
| "Apologetic": "sincere, remorseful, genuinely healing", | |
| } | |
| def p_chief(text): | |
| return f"""Analyze this. Respond EXACTLY in this format: | |
| SUMMARY: | |
| [2 sentences max. What it's really about. No fluff.] | |
| DEADLINES: | |
| [Every date/time/urgency. Quote them. If none: None found.] | |
| COMMITMENTS: | |
| [What's been promised by whom. If none: None found.] | |
| NEXT ACTIONS: | |
| 1. [Most urgent] | |
| 2. [Second] | |
| 3. [Third] | |
| Document: | |
| --- | |
| {text.strip()} | |
| ---""" | |
| def p_letter(rough, tone): | |
| return f"""Turn this rough idea into a beautiful letter. | |
| Tone: {tone} | |
| Rules: Sound completely human. No clichés. Every sentence earns its place. | |
| Write the letter directly — no preamble. | |
| What they want to say: | |
| --- | |
| {rough.strip()} | |
| --- | |
| Write the letter:""" | |
| def p_real(text): | |
| return f"""Reality-check this. Be ruthlessly direct. | |
| CONTRADICTIONS: | |
| [Conflicting claims. Quote them. If none: None found.] | |
| MISSING CONTEXT: | |
| [Absent/assumed info. If none: None found.] | |
| RISKY COMMITMENTS: | |
| [Problematic promises. If none: None found.] | |
| RED FLAGS: | |
| [Vague language, manipulation, inconsistency. If none: None found.] | |
| VERDICT: | |
| [One sentence: trust it, revise it, or reject it — and why.] | |
| Text: | |
| --- | |
| {text.strip()} | |
| ---""" | |
| # ───────────────────────────────────────────────────────────── | |
| # STREAMING FUNCTIONS | |
| # ───────────────────────────────────────────────────────────── | |
| def chief_of_staff(text): | |
| if not text.strip(): | |
| yield "Paste a document above to begin.", logline("⚠","No input"); return | |
| yield "👻 Ghost is reading your document...", logline("👁","Reading...") | |
| time.sleep(.35) | |
| yield "🧠 Extracting what actually matters...", logline("🧠","Analyzing...") | |
| try: | |
| result = call_llm(SYS_CHIEF, p_chief(text), temperature=0.22, max_tokens=660) | |
| out="" | |
| for chunk in (result[i:i+7] for i in range(0,len(result),7)): | |
| out+=chunk; yield out, logline("📝","Writing..."); time.sleep(.008) | |
| yield result, "\n".join([ | |
| logline("👁","Document read"), | |
| logline("🧠","Analysis complete"), | |
| logline("📋","Deadlines & commitments extracted"), | |
| logline("✅","Done — review and edit above"), | |
| ]) | |
| except Exception as e: | |
| yield f"⚠ Error: {e}\n\nTip: wait 30s and retry.", logline("❌",str(e)) | |
| def unsent_letter(rough, tone): | |
| if not rough.strip(): | |
| yield "Describe what you want to say above.", logline("⚠","No input"); return | |
| tone_desc = TONE_MAP.get(tone,"warm and genuine") | |
| yield "👻 Ghost is listening...", logline("👻","Listening...") | |
| time.sleep(.35) | |
| yield "💜 Understanding what you really mean...", logline("💜","Processing...") | |
| try: | |
| result = call_llm(SYS_LETTER, p_letter(rough,tone_desc), temperature=0.82, max_tokens=740) | |
| out="" | |
| for chunk in (result[i:i+6] for i in range(0,len(result),6)): | |
| out+=chunk; yield out, logline("✍","Writing in your voice..."); time.sleep(.008) | |
| yield result, "\n".join([ | |
| logline("👻","Ghost listened"), | |
| logline("💜","Real meaning understood"), | |
| logline("✍",f"Written — tone: {tone}"), | |
| logline("✅","Done — read it, edit it, send it"), | |
| ]) | |
| except Exception as e: | |
| yield f"⚠ Error: {e}", logline("❌",str(e)) | |
| def reality_check(text): | |
| if not text.strip(): | |
| yield "Paste a message or document above.", logline("⚠","No input"); return | |
| yield "👁 Ghost is reading very carefully...", logline("👁","Reading...") | |
| time.sleep(.35) | |
| yield "🔍 Scanning for contradictions...", logline("🔍","Scanning...") | |
| try: | |
| result = call_llm(SYS_REAL, p_real(text), temperature=0.22, max_tokens=660) | |
| out="" | |
| for chunk in (result[i:i+7] for i in range(0,len(result),7)): | |
| out+=chunk; yield out, logline("⚡","Checking..."); time.sleep(.008) | |
| yield result, "\n".join([ | |
| logline("👁","Read carefully"), | |
| logline("🔍","Contradictions checked"), | |
| logline("⚡","Risky commitments flagged"), | |
| logline("🛡","Reality check complete"), | |
| ]) | |
| except Exception as e: | |
| yield f"⚠ Error: {e}", logline("❌",str(e)) | |
| # ───────────────────────────────────────────────────────────── | |
| # UI | |
| # ───────────────────────────────────────────────────────────── | |
| with gr.Blocks( | |
| css=CSS, title="GHOST — Private Intelligence Layer", | |
| theme=gr.themes.Base( | |
| primary_hue="violet", neutral_hue="slate", | |
| font=gr.themes.GoogleFont("Syne"), | |
| font_mono=gr.themes.GoogleFont("Space Mono"), | |
| ), | |
| ) as demo: | |
| gr.HTML(HEADER) | |
| with gr.Tabs(): | |
| # ── CHIEF OF STAFF ── | |
| with gr.Tab("📋 Chief of Staff"): | |
| gr.HTML('<div class="slabel">Paste any email, contract or meeting notes — Ghost extracts what actually matters</div>') | |
| with gr.Row(equal_height=False): | |
| with gr.Column(scale=1): | |
| c1i = gr.Textbox(label="EMAIL / DOCUMENT", | |
| placeholder="Paste any email, contract, or document here...\n\nGhost will extract:\n→ What it's really about\n→ Every deadline\n→ All commitments made\n→ Your 3 most important next actions", | |
| lines=16) | |
| c1b = gr.Button("👻 Analyze It", size="lg") | |
| with gr.Column(scale=1): | |
| c1o = gr.Textbox(label="GHOST'S ANALYSIS (editable)", | |
| placeholder="SUMMARY · DEADLINES · COMMITMENTS · NEXT ACTIONS\nwill stream here...", | |
| lines=16, interactive=True, elem_classes=["out-box"]) | |
| c1l = gr.Textbox(label="ACTION LOG", lines=4, interactive=False, elem_classes=["log-box"]) | |
| c1b.click(fn=chief_of_staff, inputs=c1i, outputs=[c1o,c1l]) | |
| # ── UNSENT LETTER ── | |
| with gr.Tab("💌 Unsent Letter"): | |
| gr.HTML('<div class="slabel">Say what you\'ve always meant to say — Ghost writes it beautifully in your voice</div>') | |
| with gr.Row(equal_height=False): | |
| with gr.Column(scale=1): | |
| c2i = gr.Textbox(label="WHAT YOU WANT TO SAY", | |
| placeholder='Just describe it roughly — Ghost does the rest.\n\nExample:\n"My dad sacrificed everything for my education.\nI never told him what that means to me.\nI want him to feel proud — and know it wasn\'t wasted."', | |
| lines=10) | |
| c2t = gr.Radio(choices=["Warm & Loving","Professional","Honest & Direct","Apologetic"], | |
| value="Warm & Loving", label="TONE", elem_classes=["tone-wrap"]) | |
| c2b = gr.Button("👻 Write It", size="lg") | |
| with gr.Column(scale=1): | |
| c2o = gr.Textbox(label="YOUR LETTER (editable)", | |
| placeholder="Ghost will write your letter here.\n\nRead it.\nEdit anything that doesn't feel right.\nThen send it.", | |
| lines=16, interactive=True, elem_classes=["out-box"]) | |
| c2l = gr.Textbox(label="ACTION LOG", lines=4, interactive=False, elem_classes=["log-box"]) | |
| c2b.click(fn=unsent_letter, inputs=[c2i,c2t], outputs=[c2o,c2l]) | |
| # ── REALITY CHECK ── | |
| with gr.Tab("🔍 Reality Check"): | |
| gr.HTML('<div class="slabel">Paste any message or document — Ghost spots what you might miss</div>') | |
| with gr.Row(equal_height=False): | |
| with gr.Column(scale=1): | |
| c3i = gr.Textbox(label="MESSAGE / DOCUMENT TO CHECK", | |
| placeholder="Paste any message, contract, email, or document...\n\nGhost will find:\n→ Contradictions\n→ Missing context\n→ Risky commitments\n→ Red flags\n→ Final verdict", | |
| lines=16) | |
| c3b = gr.Button("👻 Reality Check", size="lg") | |
| with gr.Column(scale=1): | |
| c3o = gr.Textbox(label="GHOST'S VERDICT (editable)", | |
| placeholder="Contradictions · Missing context\nRisky commitments · Red flags · Verdict\n\nwill stream here...", | |
| lines=16, interactive=True, elem_classes=["out-box"]) | |
| c3l = gr.Textbox(label="ACTION LOG", lines=4, interactive=False, elem_classes=["log-box"]) | |
| c3b.click(fn=reality_check, inputs=c3i, outputs=[c3o,c3l]) | |
| gr.HTML(""" | |
| <div id="gh-footer"> | |
| <span style="font-size:22px">👻</span> | |
| <span style="font-family:'Space Mono',monospace;font-size:10px; | |
| color:#1f2937;letter-spacing:.07em;text-align:center"> | |
| Ghost never stores your conversations · No logs · | |
| No cloud · Your words stay yours · | |
| Phi-3-mini 3.8B · Build Small Hackathon 2026 | |
| </span> | |
| </div>""") | |
| demo.queue().launch() | |