198 lines
6.3 KiB
HTML
198 lines
6.3 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>U-Claw 正在启动…</title>
|
||
<!--
|
||
启动首屏(移植自 v2 u-clawx 4.0 的 splash 思路)。
|
||
双击启动脚本后立刻打开本页,给用户即时反馈,消除"黑窗假死"体感。
|
||
本页每秒自查 gateway 是否就绪(fetch /ready)。
|
||
|
||
就绪后【不自动跳转】,而是停在选择页,给两个大按钮:
|
||
🚀 进入 Dashboard(开始聊天)
|
||
⚙️ 配置 / 改模型(打开配置中心)
|
||
让用户自己决定去聊天还是改配置——避免"配过模型就被冲进 Dashboard、想改配置还得自己翻标签"。
|
||
|
||
端口通过 URL 查询参数传入:loading.html?port=18789
|
||
(启动脚本打开时拼好。默认 18789。)
|
||
-->
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
html, body { height: 100%; }
|
||
body {
|
||
font-family: -apple-system, "Microsoft YaHei", "PingFang SC", sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 100vh;
|
||
}
|
||
.box {
|
||
text-align: center;
|
||
max-width: 460px;
|
||
padding: 40px;
|
||
}
|
||
.logo { font-size: 64px; line-height: 1; margin-bottom: 18px; }
|
||
h1 { font-size: 1.7em; font-weight: 600; margin-bottom: 8px; }
|
||
.tip { opacity: .85; font-size: 1.02em; line-height: 1.6; margin-bottom: 28px; }
|
||
.spinner {
|
||
width: 46px; height: 46px;
|
||
margin: 0 auto 24px;
|
||
border: 4px solid rgba(255,255,255,.25);
|
||
border-top-color: #fff;
|
||
border-radius: 50%;
|
||
animation: spin .9s linear infinite;
|
||
}
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
.bar {
|
||
height: 6px; width: 100%;
|
||
background: rgba(255,255,255,.2);
|
||
border-radius: 999px;
|
||
overflow: hidden;
|
||
margin-bottom: 14px;
|
||
}
|
||
.bar > i {
|
||
display: block; height: 100%; width: 30%;
|
||
background: #fff; border-radius: 999px;
|
||
animation: slide 1.6s ease-in-out infinite;
|
||
}
|
||
@keyframes slide {
|
||
0% { margin-left: -30%; }
|
||
100% { margin-left: 100%; }
|
||
}
|
||
.status { font-size: .95em; opacity: .9; min-height: 1.4em; }
|
||
.elapsed { font-size: .82em; opacity: .6; margin-top: 6px; }
|
||
.note {
|
||
margin-top: 30px; font-size: .82em; opacity: .65; line-height: 1.6;
|
||
}
|
||
a.manual { color: #fff; text-decoration: underline; opacity: .9; }
|
||
|
||
/* —— 就绪后的选择页 —— */
|
||
.ready-wrap { display: none; }
|
||
body.ready .loading-wrap { display: none; }
|
||
body.ready .ready-wrap { display: block; }
|
||
.actions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
margin-top: 26px;
|
||
}
|
||
.btn {
|
||
display: block;
|
||
text-decoration: none;
|
||
color: #fff;
|
||
border-radius: 14px;
|
||
padding: 20px 22px;
|
||
font-size: 1.15em;
|
||
font-weight: 600;
|
||
line-height: 1.35;
|
||
cursor: pointer;
|
||
border: 2px solid rgba(255,255,255,.35);
|
||
background: rgba(255,255,255,.12);
|
||
transition: transform .12s ease, background .2s ease, box-shadow .2s ease;
|
||
}
|
||
.btn:hover { transform: translateY(-2px); background: rgba(255,255,255,.22); box-shadow: 0 8px 24px rgba(0,0,0,.18); }
|
||
.btn:active { transform: translateY(0); }
|
||
.btn .sub { display: block; font-size: .68em; font-weight: 400; opacity: .8; margin-top: 4px; }
|
||
.btn-primary { background: #ff7a45; border-color: #ff7a45; }
|
||
.btn-primary:hover { background: #ff6a30; }
|
||
.foot { margin-top: 24px; font-size: .82em; opacity: .65; line-height: 1.6; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="box">
|
||
|
||
<!-- 启动中 -->
|
||
<div class="loading-wrap">
|
||
<div class="logo">🦞</div>
|
||
<h1>U-Claw 正在启动</h1>
|
||
<div class="tip">首次从 U 盘启动需要展开内置组件,<br>稍等片刻,就绪后会提示你下一步。</div>
|
||
<div class="spinner"></div>
|
||
<div class="bar"><i></i></div>
|
||
<div class="status" id="status">正在等待 OpenClaw 网关…</div>
|
||
<div class="elapsed" id="elapsed"></div>
|
||
<div class="note">
|
||
慢 U 盘首次启动通常 30–90 秒。<br>
|
||
如果迟迟没反应,可先打开
|
||
<a class="manual" id="manual" href="#">配置中心</a>。
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 就绪后:让用户自己选 -->
|
||
<div class="ready-wrap">
|
||
<div class="logo">🦞</div>
|
||
<h1>U-Claw 已就绪</h1>
|
||
<div class="tip">选择下一步 — 直接开始聊天,或先去配置模型和渠道。</div>
|
||
<div class="actions">
|
||
<a class="btn btn-primary" id="goDash">🚀 进入 Dashboard
|
||
<span class="sub">开始和 AI 聊天</span>
|
||
</a>
|
||
<a class="btn" id="goConfig">⚙️ 配置 / 改模型
|
||
<span class="sub">填写或更换模型 Key、充值、连接微信等渠道</span>
|
||
</a>
|
||
</div>
|
||
<div class="foot">没配过模型或余额不足?先点「配置 / 改模型」处理 Key 或充值。</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
var params = new URLSearchParams(location.search);
|
||
var port = parseInt(params.get('port'), 10) || 18789;
|
||
var token = params.get('token') || 'uclaw';
|
||
var base = 'http://127.0.0.1:' + port;
|
||
var dashboard = base + '/#token=' + encodeURIComponent(token);
|
||
var configCenter = 'http://127.0.0.1:18788/';
|
||
|
||
document.getElementById('manual').href = configCenter;
|
||
document.getElementById('goDash').href = dashboard;
|
||
document.getElementById('goConfig').href = configCenter;
|
||
|
||
var startedAt = Date.now();
|
||
var ready = false;
|
||
|
||
function fmt(ms) {
|
||
var s = Math.floor(ms / 1000);
|
||
return '已等待 ' + s + ' 秒';
|
||
}
|
||
|
||
function tickElapsed() {
|
||
if (ready) return;
|
||
document.getElementById('elapsed').textContent = fmt(Date.now() - startedAt);
|
||
}
|
||
setInterval(tickElapsed, 1000);
|
||
tickElapsed();
|
||
|
||
function showChoice() {
|
||
if (ready) return;
|
||
ready = true;
|
||
document.body.classList.add('ready');
|
||
}
|
||
|
||
function poll() {
|
||
if (ready) return;
|
||
// /ready 就绪即显示选择页。no-cors 下拿不到 status,但 fetch 成功 resolve 就说明端口活了。
|
||
fetch(base + '/ready', { mode: 'no-cors', cache: 'no-store' })
|
||
.then(function () { showChoice(); })
|
||
.catch(function () {
|
||
// 端口还没起,继续等
|
||
setTimeout(poll, 1000);
|
||
});
|
||
}
|
||
poll();
|
||
|
||
// 兜底:5 分钟还没起来,提示去控制台
|
||
setTimeout(function () {
|
||
if (!ready) {
|
||
document.getElementById('status').innerHTML =
|
||
'启动较慢,可先打开 <a class="manual" href="' + configCenter + '">配置中心</a> 检查';
|
||
}
|
||
}, 300000);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|