Files
clawpanel/index.html
晴天 70d768be17 feat: new pages + dashboard enhancements + backend improvements
New pages:
- Plugin Hub: grid cards, search, install/toggle/enable plugins
- Route Map: SVG visualization of channels→agents bindings with legends
- Diagnose: gateway connectivity diagnosis with step-by-step checks

Dashboard enhancements:
- WebSocket status indicator (connected/handshaking/reconnecting/disconnected)
- Connected channels overview with platform icons
- Colored log level badges (ERROR/WARN/INFO/DEBUG) with timestamps
- Channels data loading in dashboard secondary fetch

Splash screen:
- Multi-stage boot detection (JS not loaded vs boot slow vs timeout)
- 15s: WebView2/resource load failure
- 20s: "initializing..." hint with elapsed counter
- 90s: true timeout error

Backend (Rust):
- diagnose.rs: gateway connectivity diagnosis command
- messaging.rs: plugin management commands
- service.rs: improvements
- lib.rs: register new commands

Frontend libs:
- feature-gates.js: feature flag system
- ws-client.js: reconnect state tracking
- tauri-api.js: new API bindings
- model-presets.js: provider fixes
- Remove gateway-guardian-policy.js (unused)

Dev API (scripts/dev-api.js):
- list_all_plugins, toggle_plugin, install_plugin handlers
- probe_gateway_port, diagnose_gateway_connection handlers

i18n: dashboard, sidebar, diagnose, extensions, routeMap locale modules
CSS: plugin-hub cards, route-map SVG styles
2026-04-11 00:44:06 +08:00

142 lines
7.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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 {
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;
}
@media (prefers-color-scheme: dark) { #splash { background: #0f0f14; } }
#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 .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 .sp-name { color: #e4e4e7; } }
#splash .sp-bar {
width: 120px; height: 3px; border-radius: 2px; overflow: hidden;
background: rgba(99, 102, 241, 0.15);
}
#splash .sp-bar-inner {
width: 40%; height: 100%; border-radius: 2px;
background: #6366f1;
animation: sp-slide 1.2s ease-in-out infinite;
}
#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;
}
#splash .sp-site a {
color: #6366f1; text-decoration: none;
}
#splash .sp-site a:hover { text-decoration: underline; }
@keyframes sp-slide {
0% { transform: translateX(-100%); }
50% { transform: translateX(200%); }
100% { transform: translateX(-100%); }
}
@keyframes sp-pulse {
0%, 100% { opacity: 0.7; transform: scale(1); }
50% { opacity: 1; transform: scale(1.05); }
}
</style>
</head>
<body>
<!-- 启动加载屏 -->
<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>
<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);
// 阶段115秒内 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;
}
// 阶段2JS 已加载但 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);
}
hint.textContent = '\u6B63\u5728\u521D\u59CB\u5316\u73AF\u5883\uFF0C\u8BF7\u7A0D\u5019... (' + sec + 's)';
}
// 阶段390秒仍未完成 → 才显示真正的错误
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>';
}
}, 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>