Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>Ling 3.0 Flash — Inclusion AI</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Instrument+Serif:ital@0;1&display=swap" rel="stylesheet"> | |
| <style> | |
| :root{ | |
| --bg:#ffffff; | |
| --bg-soft:#f7f8fa; | |
| --bg-softer:#fbfcfd; | |
| --panel:#ffffff; | |
| --fg:#0b0d12; | |
| --fg-muted:#5b6473; | |
| --fg-faint:#9aa3b2; | |
| --border:#e6e8ec; | |
| --border-strong:#d4d8df; | |
| --accent-1:#7BB8FF; | |
| --accent-2:#3B82F6; | |
| --user:#f3f5f9; | |
| --assistant:#ffffff; | |
| --reason:#f7faff; | |
| --shadow:0 1px 2px rgba(15,18,30,.04), 0 8px 24px rgba(15,18,30,.04); | |
| } | |
| *{box-sizing:border-box} | |
| html,body{height:100%} | |
| body{ | |
| margin:0; font-family:"Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; | |
| background:var(--bg); color:var(--fg); | |
| display:flex; flex-direction:column; | |
| -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; | |
| } | |
| /* Top nav */ | |
| header{ | |
| position:sticky; top:0; z-index:10; | |
| padding:18px 28px; | |
| display:flex; align-items:center; gap:36px; | |
| background:rgba(255,255,255,.85); | |
| backdrop-filter:saturate(180%) blur(12px); | |
| -webkit-backdrop-filter:saturate(180%) blur(12px); | |
| border-bottom:1px solid var(--border); | |
| } | |
| .brand{display:flex; align-items:center; gap:10px} | |
| .brand-mark{height:28px; width:auto; display:block} | |
| .spacer{flex:1} | |
| .controls{display:flex; align-items:center; gap:14px} | |
| .switch{display:flex; align-items:center; gap:8px; font-size:13px; color:var(--fg-muted); user-select:none; cursor:pointer} | |
| .switch input{accent-color:var(--accent-2); width:14px; height:14px} | |
| .btn{ | |
| appearance:none; border:1px solid var(--border-strong); background:var(--panel); | |
| color:var(--fg); padding:8px 14px; border-radius:999px; font-size:13px; font-weight:500; cursor:pointer; | |
| transition:background .15s, border-color .15s; | |
| } | |
| .btn:hover{background:var(--bg-soft); border-color:var(--fg-faint)} | |
| .btn.primary{ | |
| background:linear-gradient(135deg,var(--accent-1),var(--accent-2)); | |
| color:#fff; border:none; padding:10px 18px; font-weight:600; | |
| box-shadow:0 4px 14px rgba(59,130,246,.25); | |
| } | |
| .btn.primary:hover{filter:brightness(1.05)} | |
| .btn:disabled{opacity:.5; cursor:not-allowed} | |
| /* Hero (empty state) */ | |
| main{flex:1; overflow-y:auto; padding:24px 0 32px} | |
| .wrap{max-width:760px; margin:0 auto; padding:0 24px; display:flex; flex-direction:column; gap:22px} | |
| .hero{padding:80px 8px 40px; display:flex; flex-direction:column; align-items:flex-start; gap:14px} | |
| .hero .plus{color:var(--fg-faint); font-size:18px; font-weight:500; letter-spacing:.02em} | |
| .hero h1{ | |
| font-family:"Instrument Serif", "Times New Roman", serif; | |
| font-weight:400; font-size:clamp(40px, 7vw, 72px); line-height:.98; letter-spacing:-.02em; | |
| margin:0; color:var(--fg); | |
| } | |
| .hero h1 em{font-style:italic; font-family:"Instrument Serif", serif} | |
| .hero .sub{font-size:22px; color:var(--fg-muted); font-weight:400; margin:6px 0 0} | |
| .hero .accent{ | |
| background:linear-gradient(135deg,var(--accent-1),var(--accent-2)); | |
| -webkit-background-clip:text; background-clip:text; color:transparent; | |
| } | |
| .chips{display:flex; flex-wrap:wrap; gap:10px; margin-top:18px} | |
| .chip{ | |
| background:var(--bg-soft); border:1px solid var(--border); color:var(--fg); | |
| padding:9px 14px; border-radius:999px; font-size:13.5px; cursor:pointer; | |
| transition:background .15s, border-color .15s; | |
| } | |
| .chip:hover{background:var(--user); border-color:var(--border-strong)} | |
| /* Messages */ | |
| .msg{display:flex; flex-direction:column; gap:8px; max-width:100%} | |
| .who{font-size:12px; color:var(--fg-faint); font-weight:500; padding:0 4px; letter-spacing:.02em} | |
| .bubble{ | |
| padding:14px 18px; border-radius:16px; line-height:1.6; font-size:15.5px; | |
| white-space:pre-wrap; word-wrap:break-word; | |
| border:1px solid var(--border); background:var(--panel); | |
| } | |
| .user .bubble{background:var(--user); border-color:var(--border); border-top-right-radius:6px} | |
| .assistant .bubble{background:var(--assistant); border-color:var(--border); border-top-left-radius:6px; box-shadow:var(--shadow)} | |
| .reasoning{ | |
| background:var(--reason); border:1px solid var(--border); | |
| border-radius:12px; padding:12px 16px; color:var(--fg-muted); font-size:13.5px; line-height:1.55; | |
| } | |
| .reasoning summary{cursor:pointer; color:var(--fg); font-weight:600; user-select:none; display:flex; align-items:center; gap:8px} | |
| .reasoning summary::before{content:""; width:8px; height:8px; border-radius:50%; background:linear-gradient(135deg,var(--accent-1),var(--accent-2))} | |
| .reasoning .rbody{margin-top:10px; white-space:pre-wrap; color:var(--fg-muted)} | |
| .typing{display:inline-flex; gap:5px; padding:2px 0} | |
| .typing span{width:7px; height:7px; border-radius:50%; background:var(--fg-faint); animation:bk 1s infinite} | |
| .typing span:nth-child(2){animation-delay:.2s}.typing span:nth-child(3){animation-delay:.4s} | |
| @keyframes bk{0%,60%,100%{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-3px)}} | |
| /* Composer */ | |
| footer{border-top:1px solid var(--border); background:rgba(255,255,255,.85); backdrop-filter:saturate(180%) blur(12px); padding:16px 24px} | |
| .composer{max-width:760px; margin:0 auto; display:flex; gap:10px; align-items:flex-end} | |
| .composer textarea{ | |
| flex:1; resize:none; max-height:180px; min-height:56px; | |
| padding:16px 18px; border-radius:14px; border:1px solid var(--border-strong); | |
| background:var(--panel); color:var(--fg); font:inherit; font-size:15px; outline:none; | |
| transition:border-color .15s, box-shadow .15s; | |
| } | |
| .composer textarea:focus{border-color:var(--accent-2); box-shadow:0 0 0 3px rgba(59,130,246,.15)} | |
| .err{color:#dc2626; font-size:13px; max-width:760px; margin:0 auto 6px; padding:0 24px} | |
| @media (max-width:640px){ | |
| header{padding:14px 18px; gap:18px} | |
| nav{display:none} | |
| .hero{padding:48px 4px 28px} | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="brand"> | |
| <img class="brand-mark" src="https://www.inclusion-ai.org/img/logo_head.png" alt="Inclusion AI" /> | |
| </div> | |
| <div class="spacer"></div> | |
| <div class="controls"> | |
| <label class="switch"><input type="checkbox" id="reason-toggle" checked /> Reasoning</label> | |
| <button class="btn" id="clear-btn">Clear</button> | |
| </div> | |
| </header> | |
| <main><div class="wrap" id="chat"> | |
| <div class="hero" id="hero"> | |
| <div class="plus">+</div> | |
| <h1>AI BUILT<br/>BY <span class="accent">EVERYONE</span></h1> | |
| <div class="sub">For Everyone · Try Ling 3.0 Flash with reasoning.</div> | |
| <div class="chips"> | |
| <div class="chip" data-q="How many r's are in the word 'strawberry'?">Count the r's in strawberry</div> | |
| <div class="chip" data-q="Explain the Monty Hall problem in 3 lines.">Explain the Monty Hall problem</div> | |
| <div class="chip" data-q="Write a haiku about recursion.">Haiku about recursion</div> | |
| <div class="chip" data-q="What is 17 * 23? Show your reasoning step by step.">17 × 23 with reasoning</div> | |
| </div> | |
| </div> | |
| </div></main> | |
| <footer> | |
| <div class="err" id="err"></div> | |
| <div class="composer"> | |
| <textarea id="input" rows="1" placeholder="Ask Ling 3.0 Flash anything…"></textarea> | |
| <button class="btn primary" id="send">Send</button> | |
| </div> | |
| </footer> | |
| <script type="module"> | |
| import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; | |
| const client = await Client.connect(window.location.origin); | |
| const chatEl = document.getElementById("chat"); | |
| const input = document.getElementById("input"); | |
| const sendBtn = document.getElementById("send"); | |
| const reasonToggle = document.getElementById("reason-toggle"); | |
| const errEl = document.getElementById("err"); | |
| const clearBtn = document.getElementById("clear-btn"); | |
| // History is preserved across turns. Assistant turns carry reasoning_details | |
| // so the model continues reasoning from where it left off. | |
| const history = []; | |
| let busy = false; | |
| input.addEventListener("input", () => { | |
| input.style.height = "auto"; | |
| input.style.height = Math.min(input.scrollHeight, 180) + "px"; | |
| }); | |
| input.addEventListener("keydown", (e) => { | |
| if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } | |
| }); | |
| sendBtn.addEventListener("click", send); | |
| clearBtn.addEventListener("click", () => { | |
| history.length = 0; | |
| renderHero(); | |
| }); | |
| document.querySelectorAll(".chip").forEach(c => { | |
| c.addEventListener("click", () => { | |
| input.value = c.dataset.q; | |
| input.dispatchEvent(new Event("input")); | |
| input.focus(); | |
| }); | |
| }); | |
| function renderHero(){ | |
| chatEl.innerHTML = `<div class="hero"> | |
| <div class="plus">+</div> | |
| <h1>AI BUILT<br/>BY <span class="accent">EVERYONE</span></h1> | |
| <div class="sub">For Everyone · Try Ling 3.0 Flash with reasoning.</div> | |
| <div class="chips"> | |
| <div class="chip" data-q="How many r's are in the word 'strawberry'?">Count the r's in strawberry</div> | |
| <div class="chip" data-q="Explain the Monty Hall problem in 3 lines.">Explain the Monty Hall problem</div> | |
| <div class="chip" data-q="Write a haiku about recursion.">Haiku about recursion</div> | |
| <div class="chip" data-q="What is 17 * 23? Show your reasoning step by step.">17 × 23 with reasoning</div> | |
| </div> | |
| </div>`; | |
| chatEl.querySelectorAll(".chip").forEach(c => { | |
| c.addEventListener("click", () => { | |
| input.value = c.dataset.q; | |
| input.dispatchEvent(new Event("input")); | |
| input.focus(); | |
| }); | |
| }); | |
| } | |
| function escapeHtml(s){ | |
| return String(s).replace(/[&<>"']/g, c => | |
| ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); | |
| } | |
| function addMessage(role, content, reasoning){ | |
| const hero = chatEl.querySelector(".hero"); | |
| if (hero) hero.remove(); | |
| const msg = document.createElement("div"); | |
| msg.className = `msg ${role}`; | |
| const who = role === "user" ? "YOU" : "LING 3.0 FLASH"; | |
| let rhtml = ""; | |
| if (reasoning){ | |
| rhtml = `<details class="reasoning"><summary>Reasoning</summary><div class="rbody">${escapeHtml(reasoning)}</div></details>`; | |
| } | |
| msg.innerHTML = `<div class="who">${who}</div>${rhtml}<div class="bubble">${escapeHtml(content)}</div>`; | |
| chatEl.appendChild(msg); | |
| chatEl.parentElement.scrollTop = chatEl.parentElement.scrollHeight; | |
| return msg; | |
| } | |
| function setThinking(el){ | |
| el.innerHTML = `<div class="who">LING 3.0 FLASH</div><div class="bubble"><span class="typing"><span></span><span></span><span></span></span></div>`; | |
| chatEl.parentElement.scrollTop = chatEl.parentElement.scrollHeight; | |
| } | |
| async function send(){ | |
| if (busy) return; | |
| const text = input.value.trim(); | |
| if (!text) return; | |
| errEl.textContent = ""; | |
| input.value = ""; input.style.height = "auto"; | |
| busy = true; sendBtn.disabled = true; | |
| history.push({ role: "user", content: text }); | |
| addMessage("user", text); | |
| const asstMsg = addMessage("assistant", ""); | |
| setThinking(asstMsg); | |
| try { | |
| const res = await client.predict("/chat", { | |
| messages: history, reasoning: reasonToggle.checked, | |
| }); | |
| const data = res.data[0]; | |
| const content = data.content || ""; | |
| const reasoning = data.reasoning || ""; | |
| let reasoningDetails = data.reasoning_details; | |
| if (reasoningDetails && typeof reasoningDetails === "string"){ | |
| try { reasoningDetails = JSON.parse(reasoningDetails); } catch {} | |
| } | |
| let rhtml = ""; | |
| if (reasoning){ | |
| rhtml = `<details class="reasoning"><summary>Reasoning</summary><div class="rbody">${escapeHtml(reasoning)}</div></details>`; | |
| } | |
| asstMsg.innerHTML = `<div class="who">LING 3.0 FLASH</div>${rhtml}<div class="bubble">${escapeHtml(content)}</div>`; | |
| history.push({ | |
| role: "assistant", | |
| content, | |
| reasoning_details: reasoningDetails, | |
| }); | |
| chatEl.parentElement.scrollTop = chatEl.parentElement.scrollHeight; | |
| } catch (e) { | |
| errEl.textContent = "Error: " + (e?.message || String(e)); | |
| chatEl.removeChild(asstMsg); | |
| history.pop(); | |
| } finally { | |
| busy = false; sendBtn.disabled = false; input.focus(); | |
| } | |
| } | |
| input.focus(); | |
| </script> | |
| </body> | |
| </html> |