mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-07-11 15:33:24 +08:00
chore: release v0.14.0
集中发版: 新功能(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
This commit is contained in:
517
index.html
517
index.html
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@@ -7,125 +7,444 @@
|
||||
<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; flex-direction: column; align-items: center; justify-content: center;
|
||||
background: #f8f9fb;
|
||||
transition: opacity 0.4s ease, visibility 0.4s ease;
|
||||
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;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) { #splash { background: #0f0f14; } }
|
||||
@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 .sp-logo {
|
||||
width: 56px; height: 56px; margin-bottom: 20px;
|
||||
color: #6366f1; animation: sp-pulse 2s ease-in-out infinite;
|
||||
#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%);
|
||||
}
|
||||
#splash .sp-name {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
font-size: 18px; font-weight: 600; letter-spacing: 0.5px;
|
||||
color: #18181b; margin-bottom: 28px;
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: dark) { #splash .sp-name { color: #e4e4e7; } }
|
||||
#splash .sp-bar {
|
||||
width: 120px; height: 3px; border-radius: 2px; overflow: hidden;
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
/* 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);
|
||||
}
|
||||
#splash .sp-bar-inner {
|
||||
width: 40%; height: 100%; border-radius: 2px;
|
||||
background: #6366f1;
|
||||
animation: sp-slide 1.2s ease-in-out infinite;
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#splash::after { background: radial-gradient(ellipse, rgba(99,102,241,.18), transparent 70%); }
|
||||
}
|
||||
#splash .sp-site {
|
||||
margin-top: 24px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
font-size: 12px; color: #a1a1aa; letter-spacing: 0.3px;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
#splash .sp-site a {
|
||||
color: #6366f1; text-decoration: none;
|
||||
@keyframes sp-panel-in {
|
||||
from { opacity: 0; transform: translateY(8px) scale(.985); }
|
||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||
}
|
||||
#splash .sp-site a:hover { text-decoration: underline; }
|
||||
@keyframes sp-slide {
|
||||
0% { transform: translateX(-100%); }
|
||||
50% { transform: translateX(200%); }
|
||||
100% { transform: translateX(-100%); }
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@keyframes sp-pulse {
|
||||
0%, 100% { opacity: 0.7; transform: scale(1); }
|
||||
50% { opacity: 1; transform: scale(1.05); }
|
||||
|
||||
/* 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">
|
||||
<svg class="sp-logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z"/>
|
||||
<path d="M18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 00-2.456 2.456z"/>
|
||||
</svg>
|
||||
<div class="sp-name">ClawPanel</div>
|
||||
<div class="sp-bar"><div class="sp-bar-inner"></div></div>
|
||||
<div class="sp-site"><a href="https://qt.cool" target="_blank">qt.cool</a></div>
|
||||
<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>
|
||||
// 多阶段启动检测:区分"JS未加载(WebView2问题)"与"JS已加载但启动慢"
|
||||
window._splashStart = Date.now();
|
||||
window._jsLoaded = false; // main.js 入口会设为 true
|
||||
window._bootDone = false; // boot() 完成后设为 true
|
||||
window._splashTimer = setInterval(function () {
|
||||
var s = document.getElementById('splash');
|
||||
if (!s) { clearInterval(window._splashTimer); return; }
|
||||
var app = document.getElementById('content');
|
||||
// 已有内容 → 正常隐藏
|
||||
if (app && app.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);
|
||||
// 阶段1:15秒内 JS 模块未加载 → 可能是真正的 WebView2/资源问题
|
||||
if (!window._jsLoaded && elapsed > 15000) {
|
||||
clearInterval(window._splashTimer);
|
||||
var dk = window.matchMedia && window.matchMedia('(prefers-color-scheme:dark)').matches;
|
||||
s.innerHTML = '<div style="text-align:center;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif">'
|
||||
+ '<div style="font-size:40px;margin-bottom:12px">\u26A0\uFE0F</div>'
|
||||
+ '<div style="font-size:16px;font-weight:600;color:' + (dk ? '#e4e4e7' : '#18181b') + ';margin-bottom:8px">\u524D\u7AEF\u8D44\u6E90\u52A0\u8F7D\u5931\u8D25</div>'
|
||||
+ '<div style="font-size:12px;color:#71717a;margin-bottom:16px;line-height:1.7">'
|
||||
+ 'JavaScript \u6A21\u5757\u672A\u80FD\u5728 15 \u79D2\u5185\u52A0\u8F7D\u3002\u53EF\u80FD\u539F\u56E0\uFF1A<br>'
|
||||
+ '\u2022 WebView2 Runtime \u672A\u5B89\u88C5\u6216\u5DF2\u635F\u574F<br>'
|
||||
+ '\u2022 \u524D\u7AEF\u8D44\u6E90\u88AB\u5B89\u5168\u8F6F\u4EF6\u62E6\u622A<br>'
|
||||
+ '\u2022 \u5E94\u7528\u5B89\u88C5\u4E0D\u5B8C\u6574<br><br>'
|
||||
+ '\u8BF7\u5C1D\u8BD5\u5237\u65B0\uFF1B\u5982\u591A\u6B21\u5931\u8D25\uFF0C\u8BF7\u786E\u8BA4 <a href="https://go.microsoft.com/fwlink/p/?LinkId=2124703" style="color:#6366f1">WebView2 Runtime</a> \u5DF2\u5B89\u88C5</div>'
|
||||
+ '<button onclick="location.reload()" style="padding:6px 16px;border-radius:6px;border:none;background:#6366f1;color:#fff;font-size:12px;cursor:pointer">\u5237\u65B0\u91CD\u8BD5</button></div>';
|
||||
return;
|
||||
}
|
||||
// 阶段2:JS 已加载但 boot() 仍在运行 → 显示等待提示(不报错)
|
||||
if (window._jsLoaded && elapsed > 20000) {
|
||||
var hint = s.querySelector('.sp-hint');
|
||||
if (!hint) {
|
||||
hint = document.createElement('div');
|
||||
hint.className = 'sp-hint';
|
||||
hint.style.cssText = 'font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:12px;color:#a1a1aa;margin-top:16px';
|
||||
s.appendChild(hint);
|
||||
(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'
|
||||
}
|
||||
hint.textContent = '\u6B63\u5728\u521D\u59CB\u5316\u73AF\u5883\uFF0C\u8BF7\u7A0D\u5019... (' + sec + 's)';
|
||||
};
|
||||
|
||||
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';
|
||||
}
|
||||
// 阶段3:90秒仍未完成 → 才显示真正的错误
|
||||
if (elapsed > 90000) {
|
||||
clearInterval(window._splashTimer);
|
||||
var dk2 = window.matchMedia && window.matchMedia('(prefers-color-scheme:dark)').matches;
|
||||
s.innerHTML = '<div style="text-align:center;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif">'
|
||||
+ '<div style="font-size:40px;margin-bottom:12px">\u26A0\uFE0F</div>'
|
||||
+ '<div style="font-size:16px;font-weight:600;color:' + (dk2 ? '#e4e4e7' : '#18181b') + ';margin-bottom:8px">\u542F\u52A8\u8D85\u65F6</div>'
|
||||
+ '<div style="font-size:12px;color:#71717a;margin-bottom:16px;line-height:1.7">'
|
||||
+ '\u5E94\u7528\u5DF2\u7B49\u5F85 90 \u79D2\u4ECD\u672A\u5B8C\u6210\u521D\u59CB\u5316\u3002<br>'
|
||||
+ '\u53EF\u80FD\u662F\u7F51\u7EDC\u73AF\u5883\u68C0\u6D4B\u8D85\u65F6\u6216\u540E\u7AEF\u670D\u52A1\u5F02\u5E38\u3002<br>'
|
||||
+ '\u8BF7\u5C1D\u8BD5\u5237\u65B0\u6216\u91CD\u542F\u5E94\u7528\u3002</div>'
|
||||
+ '<button onclick="location.reload()" style="padding:6px 16px;border-radius:6px;border:none;background:#6366f1;color:#fff;font-size:12px;cursor:pointer">\u5237\u65B0\u91CD\u8BD5</button></div>';
|
||||
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;
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
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">
|
||||
|
||||
Reference in New Issue
Block a user