mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-07 04:42:46 +08:00
集中发版: 新功能(10) - 心甜Claw 引擎入口(第 3 个引擎模式) - Hermes 22 个 Provider 注册表 + 安装/仪表盘动态加载 - Hermes .env 高级编辑(拒绝触碰托管 Provider 密钥) - Hermes 会话与用量分析增强 - Hermes Dashboard 自动拉起 + Windows POSIX-only 兼容模态 - Hermes Skills 工具集面板 - 官网 Hermes Agent 黑金特色区 + 图文指南 - Boot Manifest 启动页(双语 + 错峰动画) - 官网 Markdown 阅读器图片 lightbox - Hermes Memory 概览卡 改进(9) - Hermes 仪表盘/扩展页全面本地化 - 记忆编辑大尺寸模态 - 日志下载 Web/桌面分流 - 侧边栏导航补全 - 模型备选管理 UI(PR #232) - 模型加载错误 UX 重做(错误卡 + 详情 + 重试) - .page 布局 clamp + .page-narrow - Memory 单列断点提早到 1100px - Web 模式跳过前端热更新检查 修复(12) - Gateway 启动 platforms.api_server.enabled 自修复(含 7 unit test) - Memory 页 overview 卡穿模(旧 flex 列约束 → 自然块流) - Skills 页 hero/toolsets 被压缩(flex-shrink:0) - Web 模式 Skills ReferenceError(补 _readHermesDisabledSkills) - 日志/记忆下载行为分流 - src/pages/models.js 5 处 typo - 删除 56 行 .hm-memory-* 死代码 + line-clamp 标准属性 - Dependabot rustls-webpki / postcss / rand
461 lines
21 KiB
HTML
461 lines
21 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>ClawPanel</title>
|
||
<link rel="icon" href="/favicon.ico">
|
||
<!-- 样式由 main.js 通过 Vite 统一加载 -->
|
||
<style>
|
||
/* Splash · Boot Manifest · 零依赖、内联渲染、双语支持(zh-CN / en,与应用 localStorage('clawpanel_lang') 共享) */
|
||
#splash {
|
||
position: fixed; inset: 0; z-index: 99999;
|
||
display: flex; align-items: center; justify-content: center;
|
||
overflow: hidden;
|
||
background: #FAFAFA; color: #18181B;
|
||
font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
transition: opacity .4s ease, visibility .4s ease;
|
||
animation: sp-fade-in .4s ease;
|
||
}
|
||
@keyframes sp-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
||
@media (prefers-color-scheme: dark) { #splash { background: #09090B; color: #F4F4F5; } }
|
||
#splash.hide { opacity: 0; visibility: hidden; pointer-events: none; }
|
||
#splash [hidden] { display: none !important; }
|
||
|
||
/* ambient grid background */
|
||
#splash::before {
|
||
content: ''; position: absolute; inset: 0; pointer-events: none;
|
||
background-image:
|
||
linear-gradient(rgba(24,24,27,.045) 1px, transparent 1px),
|
||
linear-gradient(90deg, rgba(24,24,27,.045) 1px, transparent 1px);
|
||
background-size: 48px 48px;
|
||
mask-image: radial-gradient(ellipse 60% 60% at center, #000, transparent 78%);
|
||
-webkit-mask-image: radial-gradient(ellipse 60% 60% at center, #000, transparent 78%);
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
#splash::before {
|
||
background-image:
|
||
linear-gradient(rgba(244,244,245,.04) 1px, transparent 1px),
|
||
linear-gradient(90deg, rgba(244,244,245,.04) 1px, transparent 1px);
|
||
}
|
||
}
|
||
/* subtle accent glow under the panel */
|
||
#splash::after {
|
||
content: ''; position: absolute; left: 50%; top: 50%; width: 520px; height: 280px;
|
||
transform: translate(-50%, -50%); pointer-events: none; z-index: 1;
|
||
background: radial-gradient(ellipse, rgba(99,102,241,.10), transparent 70%);
|
||
filter: blur(40px);
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
#splash::after { background: radial-gradient(ellipse, rgba(99,102,241,.18), transparent 70%); }
|
||
}
|
||
|
||
/* panel */
|
||
.sp-panel {
|
||
position: relative; z-index: 2;
|
||
width: min(540px, calc(100vw - 40px));
|
||
padding: 30px 34px 26px;
|
||
border-radius: 16px;
|
||
background: rgba(255,255,255,.72);
|
||
border: 1px solid rgba(24,24,27,.08);
|
||
backdrop-filter: blur(24px) saturate(140%); -webkit-backdrop-filter: blur(24px) saturate(140%);
|
||
box-shadow:
|
||
0 1px 0 0 rgba(255,255,255,.7) inset,
|
||
0 24px 56px -28px rgba(24,24,27,.22),
|
||
0 4px 16px -8px rgba(24,24,27,.08);
|
||
text-align: left;
|
||
animation: sp-panel-in .5s cubic-bezier(.22,.61,.36,1);
|
||
}
|
||
@keyframes sp-panel-in {
|
||
from { opacity: 0; transform: translateY(8px) scale(.985); }
|
||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
.sp-panel {
|
||
background: rgba(15,15,18,.74);
|
||
border-color: rgba(244,244,245,.08);
|
||
box-shadow:
|
||
0 1px 0 0 rgba(255,255,255,.05) inset,
|
||
0 24px 56px -28px #000,
|
||
0 4px 16px -8px rgba(0,0,0,.4);
|
||
}
|
||
}
|
||
|
||
/* head */
|
||
.sp-head { display: flex; align-items: center; gap: 12px; margin-bottom: 24px; }
|
||
.sp-head svg { width: 22px; height: 22px; flex-shrink: 0; color: inherit; opacity: .85; }
|
||
.sp-name { font: 700 17px/1 ui-sans-serif, system-ui, 'PingFang SC', 'Microsoft YaHei'; letter-spacing: -.02em; flex: 1; }
|
||
.sp-version {
|
||
padding: 4px 9px; border-radius: 5px;
|
||
font: 600 11px/1 ui-monospace, 'JetBrains Mono', Menlo, Consolas, monospace;
|
||
letter-spacing: .02em;
|
||
background: rgba(24,24,27,.06); color: rgba(24,24,27,.7);
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
.sp-version { background: rgba(244,244,245,.06); color: rgba(244,244,245,.7); }
|
||
}
|
||
|
||
/* steps */
|
||
.sp-steps { display: grid; gap: 11px; font: 500 13px/1.4 ui-monospace, 'JetBrains Mono', Menlo, Consolas, monospace; }
|
||
.sp-step {
|
||
display: flex; align-items: center; gap: 12px;
|
||
color: rgba(24,24,27,.5);
|
||
opacity: 0; transform: translateY(4px);
|
||
animation: sp-step-in .45s cubic-bezier(.22,.61,.36,1) forwards;
|
||
}
|
||
.sp-step:nth-child(1) { animation-delay: .15s; }
|
||
.sp-step:nth-child(2) { animation-delay: .65s; }
|
||
.sp-step:nth-child(3) { animation-delay: 1.20s; }
|
||
.sp-step:nth-child(4) { animation-delay: 1.75s; }
|
||
@keyframes sp-step-in { to { opacity: 1; transform: translateY(0); } }
|
||
@media (prefers-color-scheme: dark) { .sp-step { color: rgba(244,244,245,.5); } }
|
||
.sp-step.is-active { color: #18181B; }
|
||
@media (prefers-color-scheme: dark) { .sp-step.is-active { color: #F4F4F5; } }
|
||
|
||
.sp-tick { width: 14px; height: 14px; flex-shrink: 0; color: #16A34A; }
|
||
.sp-tick--indigo { color: #6366F1; }
|
||
.sp-tick--gold { color: #C8A24A; }
|
||
@media (prefers-color-scheme: dark) {
|
||
.sp-tick { color: #4ADE80; }
|
||
.sp-tick--indigo { color: #A5B4FC; }
|
||
.sp-tick--gold { color: #E1C26A; }
|
||
}
|
||
.sp-spinner {
|
||
width: 12px; height: 12px; flex-shrink: 0;
|
||
border: 1.5px solid currentColor; border-top-color: transparent;
|
||
border-radius: 50%; animation: sp-spin .8s linear infinite;
|
||
color: #6366F1;
|
||
}
|
||
@media (prefers-color-scheme: dark) { .sp-spinner { color: #A5B4FC; } }
|
||
@keyframes sp-spin { to { transform: rotate(360deg); } }
|
||
|
||
/* divider */
|
||
.sp-divider {
|
||
margin: 22px 0 14px;
|
||
height: 1px;
|
||
background: linear-gradient(90deg, transparent, rgba(24,24,27,.10), transparent);
|
||
}
|
||
@media (prefers-color-scheme: dark) { .sp-divider { background: linear-gradient(90deg, transparent, rgba(244,244,245,.10), transparent); } }
|
||
|
||
/* status */
|
||
.sp-phase {
|
||
font: 600 11px/1 ui-monospace, 'JetBrains Mono', Menlo, Consolas, monospace;
|
||
letter-spacing: .14em; text-transform: uppercase;
|
||
color: rgba(24,24,27,.5);
|
||
margin-bottom: 6px;
|
||
}
|
||
@media (prefers-color-scheme: dark) { .sp-phase { color: rgba(244,244,245,.5); } }
|
||
.sp-status {
|
||
font: 500 13px/1.55 ui-sans-serif, system-ui, 'PingFang SC', 'Microsoft YaHei';
|
||
color: rgba(24,24,27,.7); min-height: 21px;
|
||
}
|
||
@media (prefers-color-scheme: dark) { .sp-status { color: rgba(244,244,245,.72); } }
|
||
|
||
/* diagnostics */
|
||
.sp-diagnostics {
|
||
margin-top: 14px; padding: 14px 16px; border-radius: 10px;
|
||
background: rgba(99,102,241,.06); border: 1px solid rgba(99,102,241,.20);
|
||
color: rgba(24,24,27,.78);
|
||
font: 500 12px/1.65 ui-sans-serif, system-ui, 'PingFang SC', 'Microsoft YaHei';
|
||
}
|
||
.sp-diagnostics strong { color: #18181B; font-weight: 700; }
|
||
@media (prefers-color-scheme: dark) {
|
||
.sp-diagnostics { background: rgba(99,102,241,.12); border-color: rgba(165,180,252,.25); color: rgba(244,244,245,.85); }
|
||
.sp-diagnostics strong { color: #F4F4F5; }
|
||
}
|
||
|
||
/* actions */
|
||
.sp-actions { margin-top: 14px; display: flex; gap: 8px; flex-wrap: wrap; }
|
||
.sp-btn {
|
||
display: inline-flex; align-items: center; justify-content: center; gap: 7px;
|
||
min-height: 32px; padding: 7px 14px; border-radius: 8px;
|
||
font: 600 12px/1 ui-sans-serif, system-ui, 'PingFang SC', 'Microsoft YaHei';
|
||
text-decoration: none; cursor: pointer;
|
||
border: 1px solid rgba(99,102,241,.32); background: transparent; color: #6366F1;
|
||
transition: background .2s, border-color .2s, transform .2s, color .2s;
|
||
}
|
||
.sp-btn:hover { background: rgba(99,102,241,.08); border-color: #6366F1; transform: translateY(-1px); }
|
||
.sp-btn-primary { background: #6366F1; color: #fff; border-color: #6366F1; }
|
||
.sp-btn-primary:hover { background: #4F46E5; border-color: #4F46E5; color: #fff; }
|
||
@media (prefers-color-scheme: dark) {
|
||
.sp-btn { color: #A5B4FC; border-color: rgba(165,180,252,.32); }
|
||
.sp-btn:hover { background: rgba(165,180,252,.10); border-color: #A5B4FC; }
|
||
.sp-btn-primary { background: #6366F1; color: #fff; border-color: #6366F1; }
|
||
.sp-btn-primary:hover { background: #818CF8; border-color: #818CF8; }
|
||
}
|
||
|
||
/* footer */
|
||
.sp-footer {
|
||
margin-top: 22px; padding-top: 14px;
|
||
border-top: 1px dashed rgba(24,24,27,.10);
|
||
display: flex; justify-content: space-between; align-items: center; gap: 12px;
|
||
font: 500 11px/1 ui-monospace, 'JetBrains Mono', Menlo, Consolas, monospace;
|
||
color: rgba(24,24,27,.45); letter-spacing: .04em;
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
.sp-footer { border-top-color: rgba(244,244,245,.10); color: rgba(244,244,245,.45); }
|
||
}
|
||
.sp-footer a { color: #6366F1; text-decoration: none; transition: color .2s; }
|
||
.sp-footer a:hover { color: #4F46E5; text-decoration: underline; }
|
||
@media (prefers-color-scheme: dark) {
|
||
.sp-footer a { color: #A5B4FC; }
|
||
.sp-footer a:hover { color: #C7D2FE; }
|
||
}
|
||
|
||
/* language toggle (top-right pill) */
|
||
#sp-lang {
|
||
position: absolute; right: 22px; top: 22px; z-index: 5;
|
||
display: flex; align-items: center; padding: 3px;
|
||
border-radius: 999px;
|
||
background: rgba(255,255,255,.7);
|
||
border: 1px solid rgba(24,24,27,.08);
|
||
backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
|
||
box-shadow: 0 2px 12px -6px rgba(24,24,27,.12);
|
||
font: 600 11px/1 ui-sans-serif, system-ui, 'PingFang SC', 'Microsoft YaHei';
|
||
letter-spacing: .04em;
|
||
}
|
||
#sp-lang button {
|
||
all: unset; cursor: pointer;
|
||
padding: 6px 12px; border-radius: 999px;
|
||
color: rgba(24,24,27,.6);
|
||
transition: background .2s, color .2s;
|
||
}
|
||
#sp-lang button:hover { color: #18181B; }
|
||
#sp-lang button.is-on { background: #18181B; color: #FAFAFA; }
|
||
@media (prefers-color-scheme: dark) {
|
||
#sp-lang { background: rgba(15,15,18,.7); border-color: rgba(244,244,245,.08); }
|
||
#sp-lang button { color: rgba(244,244,245,.6); }
|
||
#sp-lang button:hover { color: #F4F4F5; }
|
||
#sp-lang button.is-on { background: #F4F4F5; color: #09090B; }
|
||
}
|
||
|
||
@media (max-width: 540px) {
|
||
.sp-panel { padding: 24px 24px 22px; border-radius: 14px; }
|
||
.sp-head { gap: 10px; margin-bottom: 20px; }
|
||
.sp-name { font-size: 16px; }
|
||
.sp-steps { font-size: 12.5px; gap: 9px; }
|
||
.sp-footer { font-size: 10px; gap: 8px; }
|
||
#sp-lang { right: 14px; top: 14px; }
|
||
#sp-lang button { padding: 5px 10px; }
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
#splash, #splash *, #splash::before, #splash::after { animation: none !important; transition: none !important; }
|
||
.sp-step { opacity: 1 !important; transform: none !important; }
|
||
.sp-spinner { animation: none !important; border-top-color: currentColor !important; opacity: .5; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 启动加载屏 · Boot Manifest,双语(zh-CN / en,默认中文) -->
|
||
<div id="splash">
|
||
<div class="sp-panel" role="status" aria-live="polite">
|
||
<div class="sp-head">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||
<polyline points="4 17 10 11 4 5"/>
|
||
<line x1="12" y1="19" x2="20" y2="19"/>
|
||
</svg>
|
||
<span class="sp-name">ClawPanel</span>
|
||
<span class="sp-version" data-i18n="version">多引擎</span>
|
||
</div>
|
||
<div class="sp-steps">
|
||
<div class="sp-step is-active">
|
||
<svg class="sp-tick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
|
||
<span data-i18n="step.frontend">前端模块已就绪</span>
|
||
</div>
|
||
<div class="sp-step">
|
||
<svg class="sp-tick sp-tick--indigo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
|
||
<span data-i18n="step.openclaw">引擎 · openclaw</span>
|
||
</div>
|
||
<div class="sp-step">
|
||
<svg class="sp-tick sp-tick--gold" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
|
||
<span data-i18n="step.hermes">引擎 · hermes</span>
|
||
</div>
|
||
<div class="sp-step is-active">
|
||
<span class="sp-spinner" aria-hidden="true"></span>
|
||
<span data-i18n="step.preparing">正在准备工作空间…</span>
|
||
</div>
|
||
</div>
|
||
<div class="sp-divider"></div>
|
||
<div class="sp-phase" data-i18n="phase.boot">启动</div>
|
||
<div class="sp-status" data-i18n="status.boot">正在加载前端资源,请稍候…</div>
|
||
<div class="sp-diagnostics" hidden></div>
|
||
<div class="sp-actions" hidden></div>
|
||
<div class="sp-footer">
|
||
<span data-i18n="footer.left">启动 · 多引擎</span>
|
||
<a href="https://claw.qt.cool" target="_blank" rel="noopener">claw.qt.cool</a>
|
||
</div>
|
||
</div>
|
||
<div id="sp-lang" role="group" aria-label="language">
|
||
<button type="button" data-lang="zh-CN">中文</button>
|
||
<button type="button" data-lang="en">EN</button>
|
||
</div>
|
||
</div>
|
||
<script>
|
||
(function () {
|
||
// ─── i18n strings (splash 自带迷你 i18n,与应用 localStorage('clawpanel_lang') 共享) ───
|
||
var STRINGS = {
|
||
'zh-CN': {
|
||
'version': '多引擎',
|
||
'step.frontend': '前端模块已就绪',
|
||
'step.openclaw': '引擎 · openclaw',
|
||
'step.hermes': '引擎 · hermes',
|
||
'step.preparing': '正在准备工作空间…',
|
||
'phase.boot': '启动',
|
||
'status.boot': '正在加载前端资源,请稍候…',
|
||
'footer.left': '启动 · 多引擎',
|
||
'phase.slow': '正在唤醒前端资源',
|
||
'status.slow': '这次启动比平时稍久,系统仍在等待资源响应… ({s}s)',
|
||
'diag.slow': '<strong>你可以继续等待。</strong> 如果长期停留在这里,通常与 WebView2 运行时、安装完整性或安全软件拦截有关。',
|
||
'phase.init': '正在初始化工作空间',
|
||
'status.init': '前端资源已就绪,正在连接配置、引擎和运行环境… ({s}s)',
|
||
'phase.timeout': '启动仍未完成',
|
||
'status.timeout': '应用等待时间较长,建议刷新重试;如果反复出现,请检查运行环境。',
|
||
'diag.timeout': '<strong>可尝试的处理方式</strong> · 刷新当前窗口、重启 ClawPanel、检查 WebView2 Runtime,或前往官网下载最新版本。',
|
||
'btn.reload': '刷新重试',
|
||
'btn.webview2': '检查 WebView2',
|
||
'btn.site': '访问官网'
|
||
},
|
||
'en': {
|
||
'version': 'multi-engine',
|
||
'step.frontend': 'frontend modules ready',
|
||
'step.openclaw': 'engine · openclaw',
|
||
'step.hermes': 'engine · hermes',
|
||
'step.preparing': 'preparing workspace…',
|
||
'phase.boot': 'BOOT',
|
||
'status.boot': 'Loading frontend assets, please wait…',
|
||
'footer.left': 'STARTUP · MULTI-ENGINE',
|
||
'phase.slow': 'Waking the frontend',
|
||
'status.slow': 'This boot is taking a little longer than usual… ({s}s)',
|
||
'diag.slow': '<strong>You can keep waiting.</strong> If it stays here, it usually relates to the WebView2 runtime, installation integrity, or security software interference.',
|
||
'phase.init': 'Initializing workspace',
|
||
'status.init': 'Frontend ready, connecting config, engines and runtime… ({s}s)',
|
||
'phase.timeout': 'Boot did not complete',
|
||
'status.timeout': 'The app has been waiting for a while. Try refreshing; if it keeps happening, check your runtime environment.',
|
||
'diag.timeout': '<strong>Things to try</strong> · refresh this window, restart ClawPanel, verify WebView2 Runtime, or download the latest build from the official website.',
|
||
'btn.reload': 'Reload',
|
||
'btn.webview2': 'Check WebView2',
|
||
'btn.site': 'Website'
|
||
}
|
||
};
|
||
|
||
function pickLang() {
|
||
try {
|
||
var saved = localStorage.getItem('clawpanel_lang');
|
||
if (saved && STRINGS[saved]) return saved;
|
||
var nav = (navigator.language || '').toLowerCase();
|
||
if (nav.indexOf('en') === 0) return 'en';
|
||
} catch (_) {}
|
||
return 'zh-CN';
|
||
}
|
||
var lang = pickLang();
|
||
|
||
function t(key, vars) {
|
||
var s = (STRINGS[lang] && STRINGS[lang][key]) != null ? STRINGS[lang][key] : (STRINGS['en'][key] || key);
|
||
if (vars) s = s.replace(/\{(\w+)\}/g, function (_, k) { return vars[k] != null ? vars[k] : '{' + k + '}'; });
|
||
return s;
|
||
}
|
||
|
||
function applyI18n() {
|
||
try { document.documentElement.setAttribute('lang', lang); } catch (_) {}
|
||
var els = document.querySelectorAll('#splash [data-i18n]');
|
||
for (var i = 0; i < els.length; i++) {
|
||
els[i].textContent = t(els[i].getAttribute('data-i18n'));
|
||
}
|
||
var btns = document.querySelectorAll('#sp-lang button');
|
||
for (var j = 0; j < btns.length; j++) {
|
||
btns[j].classList.toggle('is-on', btns[j].getAttribute('data-lang') === lang);
|
||
}
|
||
// re-render dynamic state if state machine has fired
|
||
if (window._splashLastState) window._setSplashState.apply(null, window._splashLastState);
|
||
}
|
||
function setLang(newLang) {
|
||
if (!STRINGS[newLang]) return;
|
||
lang = newLang;
|
||
try { localStorage.setItem('clawpanel_lang', newLang); } catch (_) {}
|
||
applyI18n();
|
||
// 通知应用 i18n 模块(若已加载)同步切换语言
|
||
try { window.dispatchEvent(new CustomEvent('clawpanel-lang-change', { detail: newLang })); } catch (_) {}
|
||
}
|
||
|
||
// ─── boot state machine ───
|
||
window._splashStart = Date.now();
|
||
window._jsLoaded = false;
|
||
window._bootDone = false;
|
||
window._splashLastState = null;
|
||
|
||
window._setSplashState = function (phaseKey, statusKey, statusVars, diagKey, showActions) {
|
||
window._splashLastState = [phaseKey, statusKey, statusVars, diagKey, showActions];
|
||
var splash = document.getElementById('splash');
|
||
if (!splash) return;
|
||
var phase = splash.querySelector('.sp-phase');
|
||
var status = splash.querySelector('.sp-status');
|
||
var diag = splash.querySelector('.sp-diagnostics');
|
||
var actions = splash.querySelector('.sp-actions');
|
||
if (phase) { phase.textContent = phaseKey ? t(phaseKey) : ''; phase.removeAttribute('data-i18n'); }
|
||
if (status) { status.textContent = statusKey ? t(statusKey, statusVars) : ''; status.removeAttribute('data-i18n'); }
|
||
if (diag) {
|
||
var hasDiag = !!diagKey;
|
||
diag.hidden = !hasDiag;
|
||
diag.innerHTML = hasDiag ? t(diagKey) : '';
|
||
}
|
||
if (actions) {
|
||
actions.hidden = !showActions;
|
||
actions.innerHTML = showActions
|
||
? '<button class="sp-btn sp-btn-primary" onclick="location.reload()">' + t('btn.reload') + '</button>'
|
||
+ '<a class="sp-btn" href="https://go.microsoft.com/fwlink/p/?LinkId=2124703" target="_blank" rel="noopener">' + t('btn.webview2') + '</a>'
|
||
+ '<a class="sp-btn" href="https://claw.qt.cool" target="_blank" rel="noopener">' + t('btn.site') + '</a>'
|
||
: '';
|
||
}
|
||
};
|
||
|
||
// ─── language toggle init ───
|
||
var langEl = document.getElementById('sp-lang');
|
||
if (langEl) {
|
||
langEl.addEventListener('click', function (e) {
|
||
var btn = e.target.closest('button[data-lang]');
|
||
if (btn) setLang(btn.getAttribute('data-lang'));
|
||
});
|
||
}
|
||
applyI18n();
|
||
|
||
// ─── boot watcher (15s / 20s / 90s 多阶段恢复) ───
|
||
window._splashTimer = setInterval(function () {
|
||
var s = document.getElementById('splash');
|
||
if (!s) { clearInterval(window._splashTimer); return; }
|
||
var content = document.getElementById('content');
|
||
if (content && content.children.length > 0) {
|
||
s.classList.add('hide');
|
||
setTimeout(function () { s.remove(); }, 500);
|
||
clearInterval(window._splashTimer);
|
||
return;
|
||
}
|
||
if (window._bootDone) { clearInterval(window._splashTimer); return; }
|
||
var elapsed = Date.now() - window._splashStart;
|
||
var sec = Math.floor(elapsed / 1000);
|
||
if (!window._jsLoaded && elapsed > 15000) {
|
||
window._setSplashState('phase.slow', 'status.slow', { s: sec }, 'diag.slow', true);
|
||
}
|
||
if (window._jsLoaded && elapsed > 20000) {
|
||
window._setSplashState('phase.init', 'status.init', { s: sec }, null, false);
|
||
}
|
||
if (elapsed > 90000) {
|
||
clearInterval(window._splashTimer);
|
||
window._setSplashState('phase.timeout', 'status.timeout', null, 'diag.timeout', true);
|
||
}
|
||
}, 3000);
|
||
})();
|
||
</script>
|
||
|
||
<div id="app">
|
||
<aside id="sidebar"></aside>
|
||
<div id="main-col">
|
||
<div id="update-banner" class="update-banner update-banner-hidden"></div>
|
||
<div id="gw-banner" class="gw-banner gw-banner-hidden"></div>
|
||
<main id="content"></main>
|
||
</div>
|
||
</div>
|
||
<script type="module" src="/src/main.js"></script>
|
||
</body>
|
||
</html>
|