| from fastapi import FastAPI |
| from fastapi.responses import HTMLResponse |
| from ime import IME |
|
|
| app = FastAPI() |
| ime = IME() |
|
|
|
|
| @app.get("/suggest") |
| def suggest(q: str, k: int = 5): |
| return {"input": q, "suggestions": ime.suggest(q, k=k)} |
|
|
|
|
| PAGE = """ |
| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <title>Manglish → Malayalam IME</title> |
| <style> |
| :root{ |
| --bg:#0f1115; --panel:#171a21; --panel2:#1e222b; |
| --text:#e6e8ec; --muted:#8b93a1; --accent:#4f8cff; |
| --accent-soft:#22314f; --border:#2a2f3a; --chip:#232833; |
| } |
| *{box-sizing:border-box} |
| body{ |
| font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif; |
| background:var(--bg); color:var(--text); |
| margin:0; min-height:100vh; |
| display:flex; align-items:flex-start; justify-content:center; |
| } |
| .wrap{width:100%; max-width:680px; padding:56px 20px} |
| h1{font-size:22px; font-weight:600; margin:0 0 4px} |
| .sub{color:var(--muted); font-size:14px; margin:0 0 28px} |
| #out{ |
| font-size:26px; line-height:1.5; min-height:52px; |
| background:var(--panel); border:1px solid var(--border); |
| border-radius:12px; padding:14px 16px; margin-bottom:16px; |
| word-break:break-word; |
| } |
| #out:empty::before{content:"Your Malayalam text appears here…"; color:var(--muted); font-size:17px} |
| input{ |
| width:100%; font-size:22px; padding:14px 16px; |
| background:var(--panel2); color:var(--text); |
| border:1px solid var(--border); border-radius:12px; outline:none; |
| transition:border-color .15s; |
| } |
| input:focus{border-color:var(--accent)} |
| input::placeholder{color:var(--muted)} |
| #sug{margin-top:12px; min-height:52px; display:flex; flex-wrap:wrap; gap:8px} |
| .s{ |
| padding:9px 16px; background:var(--chip); color:var(--text); |
| border:1px solid var(--border); border-radius:10px; |
| font-size:22px; cursor:pointer; user-select:none; |
| transition:background .12s, border-color .12s; |
| } |
| .s:hover{background:#2b313d} |
| .s:first-child{background:var(--accent-soft); border-color:var(--accent)} |
| .hint{color:var(--muted); font-size:13px; margin-top:18px; line-height:1.6} |
| .hint b{color:var(--text); font-weight:600} |
| </style> |
| </head> |
| <body> |
| <div class="wrap"> |
| <h1>Manglish → Malayalam IME</h1> |
| <p class="sub">Type Malayalam using English letters.</p> |
| |
| <div id="out"></div> |
| <input id="inp" placeholder="type manglish… e.g. ente veedu" autocomplete="off" autofocus> |
| <div id="sug"></div> |
| |
| <p class="hint"> |
| <b>space</b> or <b>enter</b> = accept first suggestion · |
| <b>click</b> a chip to pick · |
| <b>backspace</b> on empty input deletes last word |
| </p> |
| </div> |
| |
| <script> |
| const inp=document.getElementById('inp'), |
| sug=document.getElementById('sug'), |
| out=document.getElementById('out'); |
| let timer=null,current=[],ctr=0; |
| |
| inp.addEventListener('input',()=>{clearTimeout(timer);timer=setTimeout(fetchSug,110)}); |
| |
| async function fetchSug(){ |
| const q=inp.value.trim(); |
| if(!q){sug.innerHTML='';current=[];return} |
| const my=++ctr; |
| try{ |
| const r=await fetch('/suggest?q='+encodeURIComponent(q)); |
| const j=await r.json(); |
| if(my!==ctr)return; // ignore stale responses |
| current=j.suggestions; |
| sug.innerHTML=current.map(s=>`<span class="s">${s}</span>`).join(''); |
| [...sug.children].forEach((el,i)=>el.onclick=()=>pick(current[i])); |
| }catch(e){/* ignore */} |
| } |
| function pick(s){ |
| out.textContent+=s+' '; |
| inp.value='';sug.innerHTML='';current=[];inp.focus(); |
| } |
| inp.addEventListener('keydown',e=>{ |
| if((e.key===' '||e.key==='Enter')&¤t.length){ |
| e.preventDefault();pick(current[0]); |
| }else if(e.key==='Backspace'&&!inp.value){ |
| e.preventDefault(); |
| out.textContent=out.textContent.trimEnd().split(' ').slice(0,-1).join(' '); |
| if(out.textContent)out.textContent+=' '; |
| } |
| }); |
| </script> |
| </body> |
| </html> |
| """ |
|
|
|
|
| @app.get("/", response_class=HTMLResponse) |
| def index(): |
| return PAGE |