endurasolution's picture
Initial release: Aksharakuppy Manglish->Malayalam IME
a828eeb verified
Raw
History Blame Contribute Delete
3.92 kB
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')&&current.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