Files
mimiclaw/main/onboard/onboard_html.h
2026-03-08 23:04:19 +08:00

148 lines
6.3 KiB
C

#pragma once
static const char ONBOARD_HTML[] =
"<!DOCTYPE html><html><head>"
"<meta charset='utf-8'>"
"<meta name='viewport' content='width=device-width,initial-scale=1'>"
"<title>MimiClaw Setup</title>"
"<style>"
"*{box-sizing:border-box;margin:0;padding:0}"
"body{font-family:-apple-system,sans-serif;background:#f5f5f5;color:#333;padding:16px;max-width:480px;margin:0 auto}"
"h1{text-align:center;margin:16px 0;font-size:1.4em;color:#1a73e8}"
".card{background:#fff;border-radius:12px;margin:12px 0;box-shadow:0 1px 3px rgba(0,0,0,.12);overflow:hidden}"
".card-hdr{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;cursor:pointer;user-select:none;font-weight:600;font-size:.95em}"
".card-hdr::after{content:'\\25BC';font-size:.7em;transition:transform .2s}"
".card.collapsed .card-hdr::after{transform:rotate(-90deg)}"
".card.collapsed .card-body{display:none}"
".card-body{padding:0 16px 16px}"
"label{display:block;margin:10px 0 4px;font-size:.85em;color:#555}"
"input,select{width:100%;padding:10px 12px;border:1px solid #ddd;border-radius:8px;font-size:.95em;outline:none}"
"input:focus,select:focus{border-color:#1a73e8}"
".btn{display:block;width:100%;padding:12px;border:none;border-radius:8px;font-size:1em;font-weight:600;cursor:pointer;margin:8px 0}"
".btn-scan{background:#e8f0fe;color:#1a73e8}"
".btn-save{background:#1a73e8;color:#fff;margin-top:20px;font-size:1.1em}"
".btn:active{opacity:.8}"
".ap-list{max-height:200px;overflow-y:auto;border:1px solid #ddd;border-radius:8px;margin:8px 0}"
".ap-item{padding:10px 12px;border-bottom:1px solid #eee;cursor:pointer;display:flex;justify-content:space-between}"
".ap-item:last-child{border-bottom:none}"
".ap-item:active{background:#e8f0fe}"
".ap-rssi{color:#888;font-size:.85em}"
".ap-lock::before{content:'\\1F512';font-size:.75em;margin-right:4px}"
".status{text-align:center;padding:20px;color:#1a73e8;font-size:1.1em;display:none}"
"</style></head><body>"
"<h1>MimiClaw Setup</h1>"
"<p style='text-align:center;color:#666;font-size:.9em;margin-bottom:12px'>"
"This local portal remains available at 192.168.4.1 for later updates."
"</p>"
/* WiFi section (expanded by default) */
"<div class='card' id='sec-wifi'>"
"<div class='card-hdr' onclick='toggle(this)'>WiFi Configuration</div>"
"<div class='card-body'>"
"<button class='btn btn-scan' onclick='scan()'>Scan WiFi Networks</button>"
"<div class='ap-list' id='ap-list' style='display:none'></div>"
"<label>SSID</label>"
"<input id='ssid' placeholder='WiFi network name'>"
"<label>Password</label>"
"<input id='password' type='password' placeholder='WiFi password'>"
"</div></div>"
/* LLM section */
"<div class='card collapsed' id='sec-llm'>"
"<div class='card-hdr' onclick='toggle(this)'>LLM Configuration</div>"
"<div class='card-body'>"
"<label>API Key</label>"
"<input id='api_key' type='password' placeholder='sk-...'>"
"<label>Model</label>"
"<input id='model' placeholder='claude-opus-4-5' value='claude-opus-4-5'>"
"<label>Provider</label>"
"<select id='provider'>"
"<option value='anthropic'>Anthropic</option>"
"<option value='openai'>OpenAI</option>"
"</select>"
"</div></div>"
/* Telegram section */
"<div class='card collapsed' id='sec-tg'>"
"<div class='card-hdr' onclick='toggle(this)'>Telegram Bot</div>"
"<div class='card-body'>"
"<label>Bot Token</label>"
"<input id='tg_token' placeholder='123456:ABC-DEF...'>"
"</div></div>"
/* Feishu section */
"<div class='card collapsed' id='sec-feishu'>"
"<div class='card-hdr' onclick='toggle(this)'>Feishu</div>"
"<div class='card-body'>"
"<label>App ID</label>"
"<input id='feishu_app_id' placeholder='cli_xxxx'>"
"<label>App Secret</label>"
"<input id='feishu_app_secret' type='password' placeholder='App Secret'>"
"</div></div>"
/* Proxy section */
"<div class='card collapsed' id='sec-proxy'>"
"<div class='card-hdr' onclick='toggle(this)'>Proxy</div>"
"<div class='card-body'>"
"<label>Host</label>"
"<input id='proxy_host' placeholder='192.168.1.100'>"
"<label>Port</label>"
"<input id='proxy_port' type='number' placeholder='7890'>"
"<label>Type</label>"
"<select id='proxy_type'>"
"<option value=''>None</option>"
"<option value='http'>HTTP</option>"
"<option value='socks5'>SOCKS5</option>"
"</select>"
"</div></div>"
/* Search section */
"<div class='card collapsed' id='sec-search'>"
"<div class='card-hdr' onclick='toggle(this)'>Search</div>"
"<div class='card-body'>"
"<label>Brave Search API Key</label>"
"<input id='search_key' type='password' placeholder='BSA...'>"
"<label>Tavily API Key</label>"
"<input id='tavily_key' type='password' placeholder='tvly-...'>"
"</div></div>"
"<button class='btn btn-save' onclick='save()'>Save &amp; Restart</button>"
"<div class='status' id='status'>Saving... Device will restart.</div>"
"<script>"
"function toggle(el){"
"el.parentElement.classList.toggle('collapsed')}"
"function loadConfig(){"
"fetch('/config').then(r=>r.json()).then(cfg=>{"
"Object.keys(cfg).forEach(k=>{"
"var el=document.getElementById(k);"
"if(el && cfg[k] !== undefined && cfg[k] !== null){el.value=cfg[k]}"
"})"
"}).catch(()=>{})}"
"function scan(){"
"var btn=event.target;btn.textContent='Scanning...';btn.disabled=true;"
"fetch('/scan').then(r=>r.json()).then(list=>{"
"var el=document.getElementById('ap-list');el.style.display='block';el.innerHTML='';"
"list.forEach(ap=>{"
"var d=document.createElement('div');d.className='ap-item';"
"d.innerHTML='<span>'+(ap.auth?'<span class=ap-lock></span>':'')+ap.ssid+'</span><span class=ap-rssi>'+ap.rssi+' dBm</span>';"
"d.onclick=function(){document.getElementById('ssid').value=ap.ssid};"
"el.appendChild(d)});"
"btn.textContent='Scan WiFi Networks';btn.disabled=false;"
"}).catch(()=>{btn.textContent='Scan WiFi Networks';btn.disabled=false})}"
"function save(){"
"var fields=['ssid','password','api_key','model','provider','tg_token',"
"'feishu_app_id','feishu_app_secret','proxy_host','proxy_port','proxy_type','search_key','tavily_key'];"
"var data={};"
"fields.forEach(f=>{data[f]=document.getElementById(f).value.trim()});"
"document.getElementById('status').style.display='block';"
"fetch('/save',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)})"
".then(()=>{document.getElementById('status').textContent='Saved! Restarting...';})"
".catch(()=>{document.getElementById('status').textContent='Error. Please try again.';})}"
"loadConfig();"
"</script>"
"</body></html>";