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
This commit is contained in:
晴天
2026-04-11 00:44:06 +08:00
parent c1fb674c44
commit 70d768be17
27 changed files with 2337 additions and 187 deletions

View File

@@ -66,7 +66,67 @@
<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>window._splashTimer=setTimeout(function(){var s=document.getElementById('splash');if(s){var app=document.getElementById('content');if(app&&app.children.length>0){s.classList.add('hide');setTimeout(function(){s.remove()},500)}else{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:#18181b;margin-bottom:8px">\u9875\u9762\u52A0\u8F7D\u5931\u8D25</div><div style="font-size:12px;color:#71717a;margin-bottom:16px;line-height:1.7">\u5E94\u7528\u542F\u52A8\u9636\u6BB5\u6CA1\u6709\u5B8C\u6210\u524D\u7AEF\u52A0\u8F7D\u3002\u8FD9\u901A\u5E38\u610F\u5473\u7740 WebView2 \u672A\u5B89\u88C5/\u5DF2\u635F\u574F\uFF0C\u6216\u8005\u524D\u7AEF\u8D44\u6E90\u88AB\u62E6\u622A\u3002<br>\u8BF7\u5148\u68C0\u67E5\u63A7\u5236\u53F0\u662F\u5426\u6709\u62A5\u9519\uFF1B\u5982\u786E\u8BA4 WebView2 \u672A\u5B89\u88C5\uFF0C\u8BF7\u4E0B\u8F7D <a href="https://go.microsoft.com/fwlink/p/?LinkId=2124703" style="color:#6366f1">WebView2 Runtime</a></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>'}}},30000)</script>
<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>