"""The "Web agent" view: give the agent any task and watch it work in a real browser. One gr.HTML with its own markup, styles and script (like setup_bar.py and shot_tool.py). The script drives the run through server functions: web_start, then web_plan / web_act for every step, so it can animate the agent's cursor between the two calls. """ SUGGESTIONS = [ # short tasks first: a visitor's daily GPU quota covers a few minutes of steps {"icon": "paper", "title": "The DeskForge paper", "task": "Find the DeskForge paper on arXiv and tell me who its first author is."}, {"icon": "weather", "title": "Weekend weather", "task": "What is the weather forecast for Zurich this weekend?"}, {"icon": "trend", "title": "Trending on Hugging Face", "task": "Open huggingface.co, go to its Models page, and tell me which model is trending first right now."}, {"icon": "tag", "title": "Docling's latest release", "task": "Find the Docling repository on GitHub and tell me its latest release."}, {"icon": "paddle", "title": "A table tennis racket", "task": "Search for table tennis rackets and tell me the name and price of the first result."}, {"icon": "plane", "title": "Flights to Zurich", "long": True, "task": "On Google Flights, find the cheapest one-way flight from San Francisco to Zurich on November 12."}, ] TEMPLATE = '
' LOGO = ('') SCRIPT = r""" const SUG = props.suggestions || []; const LOGO = props.logo || ''; const root = element.querySelector('.dwa') || element; const esc = s => String(s ?? '').replace(/[&<>"]/g, c => ({'&': '&', '<': '<', '>': '>', '"': '"'}[c])); const sleep = ms => new Promise(r => setTimeout(r, ms)); let CLIENT = ''; try { CLIENT = sessionStorage.getItem('dwa-client') || ''; } catch (e) {} if (!CLIENT) { CLIENT = Math.random().toString(36).slice(2) + Date.now().toString(36); try { sessionStorage.setItem('dwa-client', CLIENT); } catch (e) {} } const S = { phase: 'idle', run: null, task: '', t0: 0, step: 0, maxSteps: 30, url: '', title: '', front: 0, last: null }; // Small stroke icons (24 x 24), drawn for this view so they look the same on every platform. const svg = (d, extra) => ``; const ICONS = { plane: svg('M21 15.5 13.5 12V6a1.5 1.5 0 0 0-3 0v6L3 15.5V17l7.5-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5l-2-1.5v-4.5L21 17z'), paddle: svg('M14.3 14.3l5.2 5.2', ''), paper: svg('M6 2h9l5 5v15H6zM15 2v5h5M9 13h8M9 17h6'), weather: svg('M8 2v1.5M2 8h1.5M3.8 3.8l1 1M12.2 3.8l-1 1M7 20h10a4 4 0 0 0 0-8 5 5 0 0 0-9.6 1.5A3.3 3.3 0 0 0 7 20z', ''), trend: svg('M3 17l6-6 4 4 8-8M15 7h6v6'), star: svg('M12 3l2.7 5.6 6.1.9-4.4 4.3 1 6.1L12 17l-5.4 2.9 1-6.1-4.4-4.3 6.1-.9z'), tag: svg('M20.6 13.4l-7.2 7.2a2 2 0 0 1-2.8 0L3 13V3h10l7.6 7.6a2 2 0 0 1 0 2.8zM7.5 7.5h.01'), train: svg('M8 21l2-3M16 21l-2-3M7 14h.01M17 14h.01M6 3h12a2 2 0 0 1 2 2v9a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4V5a2 2 0 0 1 2-2zM4 10h16'), book: svg('M4 4.5A2.5 2.5 0 0 1 6.5 2H20v17H6.5A2.5 2.5 0 0 0 4 21.5zM4 21.5A2.5 2.5 0 0 0 6.5 22H20'), planner: svg('M12 2l2.4 7.4L22 12l-7.6 2.6L12 22l-2.4-7.4L2 12l7.6-2.6z'), clicker: svg('M12 1v4M12 19v4M1 12h4M19 12h4', ''), click: svg('M5 3l14 7-6 2-2 6z'), type: svg('M6 10h.01M10 10h.01M14 10h.01M18 10h.01M7 14h10', ''), press: svg('M9 10l-5 5 5 5M20 4v7a4 4 0 0 1-4 4H4'), scroll: svg('M12 3v18M8 7l4-4 4 4M8 17l4 4 4-4'), back: svg('M19 12H5M12 19l-7-7 7-7'), globe: svg('M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18', ''), wait: svg('M12 7v5l3 2', ''), finish: svg('M5 12l5 5 9-10'), drag: svg('M5 9l-3 3 3 3M9 5l3-3 3 3M15 19l-3 3-3-3M19 9l3 3-3 3M2 12h20M12 2v20'), error: svg('M12 8v5M12 16.5h.01', ''), }; const ICON = { click: ICONS.click, double_click: ICONS.click, right_click: ICONS.click, hover: ICONS.click, drag: ICONS.drag, type: ICONS.type, press: ICONS.press, scroll: ICONS.scroll, back: ICONS.back, wait: ICONS.wait, finish: ICONS.finish, goto: ICONS.globe }; const VERB = { click: 'Click', double_click: 'Double-click', right_click: 'Right-click', hover: 'Hover', drag: 'Drag', type: 'Type', press: 'Press', scroll: 'Scroll', back: 'Go back', wait: 'Wait', finish: 'Finish', goto: 'Open' }; const KEYCAP = k => ({ enter: '⏎ Enter', return: '⏎ Enter', tab: '⇥ Tab', escape: 'Esc', esc: 'Esc', backspace: '⌫', ctrl: 'Ctrl', control: 'Ctrl', shift: '⇧ Shift', alt: 'Alt' }[String(k).toLowerCase()] || String(k)); root.innerHTML = `
${LOGO}
New tab
‹ › ⟳deskforge://new-tab
DeskForge web agent

Give it a task. Watch it use the web.

Qwen3.5-9B plans every step and DeskForge-Qwen3.5-4B, fine-tuned on DeskForge-1M, finds exactly where to click, in a real browser running in this Space.

${SUG.map((s, i) => ``).join('')}
A private, throwaway browser in this Space. It never signs in, pays, or types personal data, and it stops after 30 steps. Each step runs on ZeroGPU and uses your daily GPU quota; signing in to Hugging Face gives you more. Built on DeskForge GUI Grounding by @multimodalart at Hugging Face. Thank you!
Agent log
${ICONS.planner}PlannerQwen3.5-9B decides the next step
${ICONS.clicker}ClickerDeskForge-Qwen3.5-4B finds the spot
Steps appear here as the agent works: what it decided, where it clicked, and how long each step took.
`; const $ = s => root.querySelector(s); const ui = { input: $('.dwa-input'), go: $('.dwa-go'), stop: $('.dwa-stop'), timer: $('.dwa-timer'), ntp: $('.dwa-ntp'), screen: $('.dwa-screen'), imgs: root.querySelectorAll('.dwa-img'), fx: $('.dwa-fx'), cursor: $('.dwa-cursor'), think: $('.dwa-think'), thinkT: $('.dwa-think-t'), result: $('.dwa-result'), live: $('.dwa-live'), stepn: $('.dwa-stepn'), tabT: $('.dwa-tab-t'), fav: $('.dwa-fav'), url: $('.dwa-url-t'), lock: $('.dwa-lock'), log: $('.dwa-log'), scan: $('.dwa-scan') }; function showImage(uri) { if (!uri) return Promise.resolve(); return new Promise(res => { const back = ui.imgs[1 - S.front], front = ui.imgs[S.front]; back.onload = () => { back.classList.add('on'); front.classList.remove('on'); S.front = 1 - S.front; res(); }; back.onerror = () => res(); back.src = uri; }); } function setPage(url, title) { if (url == null) return; S.url = url; S.title = title || ''; let host = ''; try { host = new URL(url).hostname.replace(/^www\./, ''); } catch (e) {} ui.url.textContent = url.replace(/^https?:\/\//, ''); ui.lock.style.visibility = url.startsWith('https:') ? 'visible' : 'hidden'; ui.tabT.textContent = title || host || 'Loading…'; ui.fav.textContent = (host[0] || '•').toUpperCase(); } function setThinking(on, text) { ui.think.hidden = !on; ui.scan.classList.toggle('on', on); if (text) ui.thinkT.textContent = text; ui.cursor.classList.toggle('idle', on); } function tick() { if (S.phase !== 'running') return; const s = (Date.now() - S.t0) / 1000; ui.timer.textContent = `${Math.floor(s / 60)}:${String(Math.floor(s % 60)).padStart(2, '0')}`; setTimeout(tick, 250); } function describe(p) { const a = p.action; if (a === 'type') return `“${p.text}”` + (p.submit ? ' ⏎' : ''); if (a === 'press') return (p.keys || []).map(KEYCAP).join(' + '); if (a === 'scroll') return `${p.direction} · ${p.target}`; if (a === 'drag') return `${p.target} → ${p.destination}`; if (a === 'finish') return p.answer || 'Done'; if (a === 'goto') return p.url || ''; return p.target || ''; } function addCard(p) { const empty = ui.log.querySelector('.dwa-empty'); if (empty) empty.remove(); const card = document.createElement('div'); const plan = p.plan || {}; const a = plan.action || 'error'; card.className = 'dwa-card' + (p.error ? ' warn' : '') + (a === 'finish' ? ' fin' : ''); card.innerHTML = `
${String(p.step).padStart(2, '0')} ${p.error ? ICONS.error : (ICON[a] || '')}${p.error ? 'Retrying' : esc(VERB[a] || a)} ${(p.t_total || 0).toFixed(1)}s
${p.error ? `
${esc(p.error)}
` : `
${esc(describe(plan))}
`} ${plan.memory ? `
${esc(plan.memory)}
` : ''} ${p.error ? '' : `
plan ${(p.t_plan || 0).toFixed(1)}s${plan.target ? ` · locate ${(p.t_ground || 0).toFixed(1)}s` : ''}
`}`; ui.log.appendChild(card); ui.log.scrollTop = ui.log.scrollHeight; return card; } function place(el, x, y) { el.style.left = (x * 100) + '%'; el.style.top = (y * 100) + '%'; } function bubble(x, y, html, cls) { const b = document.createElement('div'); b.className = 'dwa-bub ' + (cls || '') + (y < 0.18 ? ' below' : '') + (x > 0.78 ? ' left' : x < 0.12 ? ' right' : ''); b.innerHTML = html; place(b, x, y); ui.fx.appendChild(b); return b; } function ripple(x, y) { const r = document.createElement('div'); r.className = 'dwa-ripple'; place(r, x, y); ui.fx.appendChild(r); const r2 = document.createElement('div'); r2.className = 'dwa-ripple two'; place(r2, x, y); ui.fx.appendChild(r2); setTimeout(() => { r.remove(); r2.remove(); }, 1100); } async function animate(p) { ui.fx.innerHTML = ''; const plan = p.plan || {}, a = plan.action, pts = p.points || []; if (pts.length) { const [x, y] = pts[0]; const b = bubble(x, y, `${esc(VERB[a] || a)}${esc(plan.target || '')}`, 'target'); place(ui.cursor, x, y); await sleep(720); if (a === 'click' || a === 'double_click' || a === 'right_click') { ripple(x, y); if (a === 'double_click') { await sleep(160); ripple(x, y); } } if (a === 'drag' && pts[1]) { place(ui.cursor, pts[1][0], pts[1][1]); await sleep(700); } if (a === 'scroll') { const s = bubble(x, y, plan.direction === 'up' ? '▲▲' : plan.direction === 'down' ? '▼▼' : plan.direction === 'left' ? '◀◀' : '▶▶', 'scroll'); } setTimeout(() => b.classList.add('fade'), 900); await sleep(380); } else if (a === 'type') { const cx = parseFloat(ui.cursor.style.left) / 100 || 0.5, cy = parseFloat(ui.cursor.style.top) / 100 || 0.5; const b = bubble(cx, cy, 'Type', 'typing'); const out = b.querySelector('.dwa-typed'), text = plan.text || ''; const per = Math.max(12, Math.min(45, 900 / Math.max(1, text.length))); for (const ch of text) { out.textContent += ch; await sleep(per); } if (plan.submit) { b.insertAdjacentHTML('beforeend', '⏎'); } await sleep(250); } else if (a === 'press') { const cx = parseFloat(ui.cursor.style.left) / 100 || 0.5, cy = parseFloat(ui.cursor.style.top) / 100 || 0.5; bubble(cx, cy, (plan.keys || []).map(k => `${esc(KEYCAP(k))}`).join('+'), 'keys'); await sleep(650); } else if (a === 'goto') { // the address is typed into the window's URL bar const u = (plan.url || '').replace(/^https?:\/\//, ''), bar = ui.url.parentElement; bar.classList.add('typing'); ui.url.textContent = ''; const per = Math.max(10, Math.min(35, 700 / Math.max(1, u.length))); for (const ch of u) { ui.url.textContent += ch; await sleep(per); } await sleep(250); bar.classList.remove('typing'); } else if (a === 'back') { bubble(0.06, 0.04, '← Back', 'chip below right'); await sleep(500); } else if (a === 'wait') { await sleep(400); } } function showResult(r) { const quota = !r.answer && friendlyGpuError(r.note); if (quota) { // the visitor's daily ZeroGPU quota ran out: say so plainly, with a way to get more ui.result.className = 'dwa-result muted'; ui.result.innerHTML = `
${esc(quota.title)}
${esc(quota.text)}
`; ui.result.hidden = false; const card = document.createElement('div'); card.className = 'dwa-final muted'; card.innerHTML = `
${esc(quota.title)}
${esc(quota.text)}
${esc(quota.detail)}
`; ui.log.appendChild(card); ui.log.scrollTop = ui.log.scrollHeight; S.last = ''; return; } const ok = !r.note || r.answer; ui.result.className = 'dwa-result' + (ok ? '' : ' muted'); ui.result.innerHTML = `
${ok ? '✓ Done' : 'Stopped'} ${r.steps ?? S.step} steps · ${Math.round(r.seconds ?? (Date.now() - S.t0) / 1000)} s
${esc(r.answer || r.note || 'The run ended.')}
`; ui.result.hidden = false; const card = document.createElement('div'); card.className = 'dwa-final' + (ok ? '' : ' muted'); card.innerHTML = `
${ok ? '✓ Result' : 'Stopped'}
${esc(r.answer || r.note || '')}
${r.answer ? '' : ''}
`; ui.log.appendChild(card); ui.log.scrollTop = ui.log.scrollHeight; S.last = r.answer || ''; } function setPhase(ph) { S.phase = ph; const running = ph === 'running' || ph === 'starting' || ph === 'waiting'; ui.go.hidden = running; ui.stop.hidden = !running; ui.timer.hidden = ph === 'idle'; ui.input.readOnly = running; ui.live.hidden = ph !== 'running'; root.classList.toggle('is-running', running); } async function start(task) { task = (task || ui.input.value || '').trim(); if (task.length < 4) { ui.input.focus(); ui.input.classList.add('shake'); setTimeout(() => ui.input.classList.remove('shake'), 400); return; } ui.input.value = task; S.task = task; S.step = 0; ui.log.innerHTML = ''; ui.result.hidden = true; ui.fx.innerHTML = ''; setPhase('starting'); S.t0 = Date.now(); tick(); ui.ntp.hidden = true; ui.screen.hidden = false; setThinking(true, 'Opening a fresh browser…'); let r = null; for (let tries = 0; tries < 60 && (S.phase === 'starting' || S.phase === 'waiting'); tries++) { try { r = await server.web_start(task, CLIENT); } catch (e) { r = { error: String(e && e.message || e) }; } if (!r.busy) break; setPhase('waiting'); setThinking(true, 'All browsers are busy right now. Waiting for a free one…'); await sleep(3000); } if (S.phase !== 'starting' && S.phase !== 'waiting') return; // stopped while waiting if (r && r.busy) { fail('The demo is very busy right now. Please try again in a minute.'); return; } if (!r || r.error || !r.run) { fail(r && r.error || 'Could not start a browser.'); return; } S.run = r.run; S.maxSteps = r.max_steps || 30; await showImage(r.image); setPage(r.url, r.title); setPhase('running'); S.t0 = Date.now(); tick(); loop(); } async function loop() { while (S.phase === 'running') { setThinking(true, S.step === 0 ? 'Reading the page and planning the first step…' : 'Reading the page…'); let p; try { p = await gpuCall('web_plan', { run: S.run }); } catch (e) { fail(String(e && e.message || e)); return; } if (S.phase !== 'running') return; if (p.image) await showImage(p.image); setPage(p.url, p.title); if (p.done) { setThinking(false); end(p); return; } S.step = p.step; ui.stepn.textContent = ` · step ${p.step}`; setThinking(false); const card = addCard(p); if (p.error) { continue; } const actP = server.web_act(S.run); let a; try { [a] = await Promise.all([actP, animate(p)]); } catch (e) { fail(String(e && e.message || e)); return; } if (S.phase !== 'running') return; if (a.image) await showImage(a.image); setPage(a.url, a.title); if (a.error) { card.classList.add('warn'); card.insertAdjacentHTML('beforeend', `
${esc(a.error)}
`); } if (a.blocked) { card.classList.add('shield'); card.insertAdjacentHTML('beforeend', `
🛡 ${esc(a.blocked)}
`); } if (a.done) { end(a); return; } } } function end(r) { setPhase('done'); ui.fx.innerHTML = ''; showResult(r); } function fail(msg) { setPhase('done'); setThinking(false); showResult({ note: msg, answer: '', steps: S.step }); } async function stop() { const run = S.run; setPhase('done'); setThinking(false); showResult({ note: 'Stopped by you.', answer: '', steps: S.step }); if (run) { try { await server.web_stop(run); } catch (e) {} } } root.addEventListener('submit', e => { e.preventDefault(); if (S.phase !== 'running' && S.phase !== 'starting') start(); }); root.addEventListener('click', e => { const t = e.target.closest('[data-sug],[data-act],.dwa-stop'); if (!t) return; if (t.classList.contains('dwa-stop')) { stop(); return; } if (t.dataset.sug) { const s = SUG[+t.dataset.sug]; ui.input.value = s.task; start(s.task); return; } if (t.dataset.act === 'again') { setPhase('idle'); ui.screen.hidden = true; ui.ntp.hidden = false; ui.result.hidden = true; ui.fx.innerHTML = ''; ui.log.innerHTML = '
Steps appear here as the agent works.
'; setPage('deskforge://new-tab', 'New tab'); ui.input.value = ''; ui.input.focus(); } if (t.dataset.act === 'copy' && S.last) { navigator.clipboard && navigator.clipboard.writeText(S.last); t.textContent = 'Copied'; } }); window.addEventListener('pagehide', () => { if (S.run && S.phase === 'running') { try { server.web_stop(S.run); } catch (e) {} } }); """ CSS = """ #df-view-web { background: #F3EDE2; padding: 0 !important; } #df-view-web .dwa { font-family: 'Hanken Grotesk', system-ui, sans-serif; color: #2B2722; } #df-view-web .dwa-shell { display: grid; grid-template-columns: minmax(0, 1fr) 380px; min-height: calc(100vh - 52px); } @media (max-width: 1100px) { #df-view-web .dwa-shell { grid-template-columns: 1fr; } } #df-view-web .dwa-main { padding: 18px 24px 16px; display: flex; flex-direction: column; gap: 12px; min-width: 0; } #df-view-web .dwa-spacer { flex: 1; } /* the task bar */ #df-view-web .dwa-ask { display: flex; align-items: center; gap: 10px; background: #fff; border: 1px solid #E0DCD6; border-radius: 14px; padding: 7px 7px 7px 10px; box-shadow: 0 6px 22px rgba(60,40,20,.08); } #df-view-web .dwa-ask:focus-within { border-color: #D2603A; box-shadow: 0 0 0 4px rgba(210,96,58,.10), 0 6px 22px rgba(60,40,20,.08); } #df-view-web .dwa-ask-icon { width: 30px; height: 30px; flex: none; border-radius: 8px; background: #D2603A; display: flex; align-items: center; justify-content: center; } #df-view-web .dwa-input { flex: 1; min-width: 0; font: 500 16px 'Hanken Grotesk', system-ui, sans-serif; color: #2B2722; border: none; outline: none; background: transparent; padding: 8px 4px; } #df-view-web .dwa-input::placeholder { color: #A39D94; } #df-view-web .dwa-input.shake { animation: dwa-shake .35s; } #df-view-web .dwa-go, #df-view-web .dwa-stop { font: 600 15px 'Hanken Grotesk', system-ui, sans-serif; border-radius: 10px; padding: 10px 18px; cursor: pointer; flex: none; } #df-view-web .dwa-go { color: #fff; background: #D2603A; border: 1px solid #C0552F; } #df-view-web .dwa-go:hover { filter: brightness(1.05); } #df-view-web .dwa-stop { color: #A8452A; background: #FBF3EC; border: 1px solid #EBCDBF; } #df-view-web .dwa-timer { font: 600 13px 'JetBrains Mono', monospace; color: #8a8378; font-variant-numeric: tabular-nums; } #df-view-web [hidden] { display: none !important; } /* the browser window */ #df-view-web .dwa-win { background: #fff; border: 1px solid #DDD8D1; border-radius: 12px; overflow: hidden; box-shadow: 0 18px 50px rgba(60,40,20,.16), 0 2px 6px rgba(60,40,20,.06); } #df-view-web .dwa-bar { height: 38px; display: flex; align-items: center; gap: 12px; padding: 0 12px; background: #EFEAE2; } #df-view-web .dwa-dots { display: flex; gap: 7px; } #df-view-web .dwa-dots i { width: 11px; height: 11px; border-radius: 50%; background: #E46A5E; display: block; } #df-view-web .dwa-dots i + i { background: #E9B44C; } #df-view-web .dwa-dots i + i + i { background: #62C554; } #df-view-web .dwa-tab { align-self: flex-end; height: 31px; max-width: 300px; display: flex; align-items: center; gap: 8px; padding: 0 14px; background: #fff; border-radius: 9px 9px 0 0; font-size: 13px; color: #3a332b; min-width: 0; } #df-view-web .dwa-tab-t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #df-view-web .dwa-fav { width: 16px; height: 16px; flex: none; border-radius: 4px; background: #E9E4DC; color: #6f6a62; font: 700 10px/16px 'Hanken Grotesk', sans-serif; text-align: center; } #df-view-web .dwa-live { display: flex; align-items: center; gap: 7px; font: 700 11px 'JetBrains Mono', monospace; letter-spacing: .08em; color: #C0552F; background: #fff; border: 1px solid #EBCDBF; border-radius: 999px; padding: 4px 10px; } #df-view-web .dwa-stepn { letter-spacing: 0; font-weight: 500; color: #8a8378; } #df-view-web .dwa-led { width: 7px; height: 7px; border-radius: 50%; background: #D2603A; animation: dwa-pulse 1.4s ease-in-out infinite; } #df-view-web .dwa-omni { height: 40px; display: flex; align-items: center; gap: 12px; padding: 0 12px; border-bottom: 1px solid #EEE9E2; background: #fff; } #df-view-web .dwa-nav { font-size: 15px; color: #B3ADA4; letter-spacing: .25em; flex: none; } #df-view-web .dwa-url { flex: 1; min-width: 0; height: 28px; display: flex; align-items: center; gap: 7px; padding: 0 12px; background: #F4F1EC; border-radius: 999px; font: 13px 'Hanken Grotesk', system-ui, sans-serif; color: #4a443c; } #df-view-web .dwa-url-t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #df-view-web .dwa-url.typing { background: #fff; box-shadow: inset 0 0 0 2px #D2603A; } #df-view-web .dwa-lock { display: inline-flex; color: #8a8378; } #df-view-web .dwa-view { position: relative; aspect-ratio: 16 / 10; background: #FBF9F6; overflow: hidden; } /* new tab page */ #df-view-web .dwa-ntp { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 28px; text-align: center; background: radial-gradient(1200px 500px at 50% -10%, #FBEDE3 0%, #FBF9F6 60%); overflow: auto; } #df-view-web .dwa-ntp-mark { display: flex; align-items: center; gap: 9px; font-weight: 700; font-size: 15px; color: #2B2722; } #df-view-web .dwa-ntp-mark span:last-child { color: #8a8378; font-weight: 500; } #df-view-web .dwa-ntp-logo { width: 28px; height: 28px; border-radius: 8px; background: #D2603A; display: inline-flex; align-items: center; justify-content: center; } #df-view-web .dwa-ntp h2 { font: 600 clamp(22px, 3vw, 34px)/1.15 'Newsreader', Georgia, serif; letter-spacing: -.01em; margin: 0; color: #2B2722; } #df-view-web .dwa-ntp p { max-width: 640px; margin: 0; font-size: 15px; line-height: 1.55; color: #5f594f; } #df-view-web .dwa-acc { color: #C0552F; } #df-view-web .dwa-sugs { width: 100%; max-width: 860px; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 6px; } @media (max-width: 760px) { #df-view-web .dwa-sugs { grid-template-columns: 1fr 1fr; } } #df-view-web .dwa-sug { display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto auto; column-gap: 10px; row-gap: 2px; text-align: left; background: #fff; border: 1px solid #E6E3DF; border-radius: 12px; padding: 11px 12px; cursor: pointer; font-family: inherit; color: inherit; box-shadow: 0 1px 2px rgba(40,30,20,.04); transition: transform .15s, border-color .15s, box-shadow .15s; } #df-view-web .dwa-sug:hover { transform: translateY(-2px); border-color: #D2603A; box-shadow: 0 8px 20px rgba(60,40,20,.10); } #df-view-web .dwa-sug-i { grid-row: span 2; width: 34px; height: 34px; border-radius: 9px; background: #FBF3EC; color: #C0552F; display: flex; align-items: center; justify-content: center; } #df-view-web .dwa-sug-t { font-weight: 700; font-size: 14px; } #df-view-web .dwa-long { margin-left: 7px; font: 600 10.5px 'JetBrains Mono', monospace; letter-spacing: .03em; color: #8a6f45; background: #F6EFE2; border: 1px solid #E8DCC6; border-radius: 999px; padding: 1px 7px; vertical-align: 1px; white-space: nowrap; } #df-view-web .dwa-signin { display: inline-block; margin-top: 8px; font: 600 13px 'Hanken Grotesk', sans-serif; color: #fff !important; background: #D2603A; border-radius: 8px; padding: 7px 12px; text-decoration: none !important; } #df-view-web .dwa-sug-d { font-size: 12.5px; line-height: 1.4; color: #7a7369; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } /* the live screen and its effects */ #df-view-web .dwa-screen { position: absolute; inset: 0; } #df-view-web .dwa-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; opacity: 0; transition: opacity .22s ease; user-select: none; } #df-view-web .dwa-img.on { opacity: 1; } #df-view-web .dwa-scan { position: absolute; inset: 0; pointer-events: none; opacity: 0; transition: opacity .3s; background: linear-gradient(180deg, rgba(210,96,58,0) 0%, rgba(210,96,58,.10) 48%, rgba(210,96,58,.0) 52%); background-size: 100% 220%; } #df-view-web .dwa-scan.on { opacity: 1; animation: dwa-scan 2.4s linear infinite; } #df-view-web .dwa-fx { position: absolute; inset: 0; pointer-events: none; } #df-view-web .dwa-cursor { position: absolute; z-index: 5; pointer-events: none; transform: translate(-4px, -3px); transition: left .7s cubic-bezier(.45,.05,.2,1), top .7s cubic-bezier(.45,.05,.2,1); filter: drop-shadow(0 3px 5px rgba(0,0,0,.28)); } #df-view-web .dwa-cursor.idle svg { animation: dwa-bob 1.6s ease-in-out infinite; } #df-view-web .dwa-ctag { position: absolute; left: 20px; top: 21px; font: 700 11.5px 'Hanken Grotesk', sans-serif; color: #fff; background: #D2603A; padding: 3px 8px; border-radius: 6px 6px 6px 2px; white-space: nowrap; } #df-view-web .dwa-ripple { position: absolute; width: 22px; height: 22px; margin: -11px 0 0 -11px; border-radius: 50%; border: 3px solid #D2603A; animation: dwa-ripple .9s ease-out forwards; } #df-view-web .dwa-ripple.two { animation-delay: .14s; border-color: rgba(210,96,58,.55); } #df-view-web .dwa-bub { position: absolute; z-index: 4; transform: translate(-50%, calc(-100% - 22px)); max-width: 46%; background: rgba(43,39,34,.92); color: #fff; font: 600 13px/1.35 'Hanken Grotesk', sans-serif; padding: 7px 11px; border-radius: 10px; box-shadow: 0 8px 22px rgba(0,0,0,.22); animation: dwa-pop .22s ease-out; transition: opacity .4s; } #df-view-web .dwa-bub.below { transform: translate(-50%, 26px); } #df-view-web .dwa-bub.left { transform: translate(calc(-100% + 30px), calc(-100% - 22px)); } #df-view-web .dwa-bub.right { transform: translate(-20px, calc(-100% - 22px)); } #df-view-web .dwa-bub.below.left { transform: translate(calc(-100% + 30px), 26px); } #df-view-web .dwa-bub.below.right { transform: translate(-20px, 26px); } #df-view-web .dwa-bub.fade { opacity: 0; } #df-view-web .dwa-bub-v { display: inline-block; font: 700 10.5px 'JetBrains Mono', monospace; letter-spacing: .06em; text-transform: uppercase; color: #F2B49E; margin-right: 7px; } #df-view-web .dwa-bub.typing { background: #fff; color: #2B2722; border: 2px solid #D2603A; font: 600 14px 'JetBrains Mono', monospace; } #df-view-web .dwa-bub.typing .dwa-bub-v { color: #C0552F; } #df-view-web .dwa-caret { display: inline-block; width: 2px; height: 1.05em; background: #D2603A; vertical-align: -2px; margin-left: 1px; animation: dwa-blink .9s step-end infinite; } #df-view-web .dwa-bub.keys { background: transparent; box-shadow: none; padding: 0; } #df-view-web kbd { display: inline-block; font: 700 14px 'JetBrains Mono', monospace; color: #2B2722; background: #fff; border: 1px solid #D6D1CA; border-bottom-width: 3px; border-radius: 8px; padding: 6px 10px; box-shadow: 0 6px 16px rgba(0,0,0,.18); } #df-view-web .dwa-plus { color: #fff; margin: 0 5px; font-weight: 700; text-shadow: 0 1px 3px rgba(0,0,0,.5); } #df-view-web .dwa-enter { margin-left: 8px; padding: 1px 7px; font-size: 12px; box-shadow: none; } #df-view-web .dwa-bub.scroll { background: #D2603A; font-size: 12px; letter-spacing: -.1em; transform: translate(-50%, 18px); } #df-view-web .dwa-bub.chip { left: 12px !important; top: 12px !important; transform: none; } #df-view-web .dwa-think { position: absolute; left: 50%; top: 14px; transform: translateX(-50%); z-index: 6; display: flex; align-items: center; gap: 9px; background: rgba(255,255,255,.96); border: 1px solid #E6E3DF; border-radius: 999px; padding: 7px 14px 7px 10px; font-size: 13.5px; font-weight: 600; color: #3a332b; box-shadow: 0 8px 24px rgba(60,40,20,.14); white-space: nowrap; } #df-view-web .dwa-spin { width: 14px; height: 14px; border-radius: 50%; border: 2px solid #EBCDBF; border-top-color: #D2603A; animation: df-spin .7s linear infinite; } #df-view-web .dwa-result { position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%); width: min(720px, calc(100% - 36px)); z-index: 7; background: rgba(255,255,255,.97); border: 1px solid #E6E3DF; border-left: 4px solid #4D9A43; border-radius: 12px; padding: 14px 16px; box-shadow: 0 16px 40px rgba(40,30,20,.22); animation: dwa-rise .35s ease-out; } #df-view-web .dwa-result.muted { border-left-color: #C9B79A; } #df-view-web .dwa-res-h { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 14px; color: #3f7a34; } #df-view-web .dwa-result.muted .dwa-res-h { color: #8a6f45; } #df-view-web .dwa-check { width: 20px; height: 20px; border-radius: 50%; background: #4D9A43; color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; } #df-view-web .dwa-res-m { margin-left: auto; font: 500 12px 'JetBrains Mono', monospace; color: #9a9389; } #df-view-web .dwa-res-a { margin-top: 6px; font-size: 16px; line-height: 1.5; color: #2B2722; } #df-view-web .dwa-foot { font: italic 13.5px/1.5 'Newsreader', Georgia, serif; color: #8a8378; padding: 0 4px; } #df-view-web .dwa-thanks { display: block; margin-top: 4px; } #df-view-web .dwa-foot a { color: #C0552F; text-decoration: underline; text-underline-offset: 2px; } /* the agent log */ #df-view-web .dwa-side { background: #FBF6EE; border-left: 1px solid #E6E3DF; display: flex; flex-direction: column; min-height: 0; } #df-view-web .dwa-side-head { height: 46px; display: flex; align-items: center; padding: 0 16px; border-bottom: 1px solid #E6E3DF; font-weight: 700; font-size: 14px; } #df-view-web .dwa-models { display: grid; gap: 8px; padding: 12px 14px; border-bottom: 1px solid #E6E3DF; } #df-view-web .dwa-model { display: flex; align-items: center; gap: 10px; background: #fff; border: 1px solid #E6E3DF; border-radius: 10px; padding: 8px 10px; font-size: 13px; color: #5f594f; } #df-view-web .dwa-model b { display: block; font-size: 11px; letter-spacing: .07em; text-transform: uppercase; color: #3a332b; } #df-view-web .dwa-model-df { border-color: #EBCDBF; background: #FFFBF8; } #df-view-web .dwa-model-df b { color: #C0552F; } #df-view-web .dwa-mi { width: 30px; height: 30px; flex: none; border-radius: 8px; background: #F4F1EC; color: #6f6a62; display: flex; align-items: center; justify-content: center; } #df-view-web .dwa-model-df .dwa-mi { background: #FBF3EC; color: #C0552F; } #df-view-web .dwa-log { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 9px; max-height: calc(100vh - 210px); min-height: 240px; } #df-view-web .dwa-log > * { flex-shrink: 0; } #df-view-web .dwa-empty { font: italic 14px/1.5 'Newsreader', Georgia, serif; color: #8a8378; } #df-view-web .dwa-card { background: #fff; border: 1px solid #E6E3DF; border-radius: 10px; padding: 9px 11px; animation: dwa-in .2s ease-out; box-shadow: 0 1px 2px rgba(40,30,20,.04); } #df-view-web .dwa-card.warn { border-color: #EED9B5; background: #FFFCF5; } #df-view-web .dwa-card.fin { border-color: #CFE3C9; } #df-view-web .dwa-card-h { display: flex; align-items: center; gap: 8px; } #df-view-web .dwa-num { font: 600 11px 'JetBrains Mono', monospace; color: #A39D94; } #df-view-web .dwa-ico { width: 24px; height: 24px; border-radius: 7px; background: #FBF3EC; color: #C0552F; display: inline-flex; align-items: center; justify-content: center; } #df-view-web .dwa-ico svg { width: 15px; height: 15px; } #df-view-web .dwa-card.warn .dwa-ico { background: #FFF3DC; color: #9a6a1f; } #df-view-web .dwa-card.fin .dwa-ico { background: #EAF3E6; color: #3f7a34; } #df-view-web .dwa-verb { font-weight: 700; font-size: 13.5px; } #df-view-web .dwa-t { font: 500 11.5px 'JetBrains Mono', monospace; color: #A39D94; } #df-view-web .dwa-what { margin-top: 4px; font-size: 13.5px; line-height: 1.45; color: #3a332b; overflow-wrap: anywhere; } #df-view-web .dwa-err { color: #9a6a1f; font-size: 12.5px; } #df-view-web .dwa-shield { color: #3B6EBE; font-size: 12.5px; } #df-view-web .dwa-mem { margin-top: 4px; font: italic 13.5px/1.45 'Newsreader', Georgia, serif; color: #7a7369; } #df-view-web .dwa-tm { margin-top: 4px; font: 500 11px 'JetBrains Mono', monospace; color: #B0AAA1; } #df-view-web .dwa-final { background: #fff; border: 1px solid #CFE3C9; border-left: 4px solid #4D9A43; border-radius: 10px; padding: 11px 12px; animation: dwa-in .25s ease-out; } #df-view-web .dwa-final.muted { border-color: #E6E3DF; border-left-color: #C9B79A; } #df-view-web .dwa-final-h { font-weight: 700; font-size: 13px; color: #3f7a34; } #df-view-web .dwa-final.muted .dwa-final-h { color: #8a6f45; } #df-view-web .dwa-final-a { margin-top: 5px; font-size: 14.5px; line-height: 1.5; } #df-view-web .dwa-final-r { display: flex; gap: 8px; margin-top: 10px; } #df-view-web .dwa-final-r button { font: 600 13px 'Hanken Grotesk', sans-serif; color: #C0552F; background: #FBF3EC; border: 1px solid #EBCDBF; border-radius: 8px; padding: 6px 11px; cursor: pointer; } @keyframes dwa-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } } @keyframes dwa-scan { from { background-position: 0 120%; } to { background-position: 0 -120%; } } @keyframes dwa-ripple { from { transform: scale(.4); opacity: 1; } to { transform: scale(3.2); opacity: 0; } } @keyframes dwa-pop { from { opacity: 0; margin-top: 6px; } to { opacity: 1; margin-top: 0; } } @keyframes dwa-rise { from { opacity: 0; bottom: 4px; } to { opacity: 1; bottom: 18px; } } @keyframes dwa-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } } @keyframes dwa-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } } @keyframes dwa-blink { 50% { opacity: 0; } } @keyframes dwa-shake { 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } } @media (prefers-reduced-motion: reduce) { #df-view-web * { animation: none !important; transition: none !important; } } """