fix: pairing required(metadata-upgrade) + agents 骨架屏

pairing.rs:
- platform 改用 std::env::consts::OS (windows/macos/linux),
  新增 deviceFamily=desktop,与 device.rs connect frame 保持一致
- 已配对设备若 platform 字段不匹配,自动覆盖更新,避免 Gateway
  因 metadata-upgrade 拒绝静默自动配对

agents.js + components.css:
- loadAgents 调用前先渲染 3 条骨架屏占位,消除空白等待期
- 添加 .skeleton 闪光动画样式
This commit is contained in:
晴天
2026-03-04 15:13:27 +08:00
parent 7f2f6db842
commit 317cda70c2
3 changed files with 55 additions and 3 deletions

View File

@@ -47,8 +47,32 @@ pub fn auto_pair_device() -> Result<String, String> {
// 无论是否已配对,都确保 gateway.controlUi.allowedOrigins 已写入
patch_gateway_origins();
// 检查设备是否已配对
if paired.get(&device_id).is_some() {
let os_platform = std::env::consts::OS; // "windows" | "macos" | "linux"
// 如果已配对,档查 platform 字段是否正确;不正确则覆盖更新,
// 避免 Gateway 因 metadata-upgrade 拒绝静默自动配对
if let Some(existing) = paired.get_mut(&device_id) {
let current_platform = existing
.get("platform")
.and_then(|v| v.as_str())
.unwrap_or("");
if current_platform != os_platform {
if let Some(obj) = existing.as_object_mut() {
obj.insert(
"platform".to_string(),
serde_json::Value::String(os_platform.to_string()),
);
obj.insert(
"deviceFamily".to_string(),
serde_json::Value::String("desktop".to_string()),
);
}
let new_content = serde_json::to_string_pretty(&paired)
.map_err(|e| format!("序列化 paired.json 失败: {e}"))?;
std::fs::write(&paired_path, new_content)
.map_err(|e| format!("更新 paired.json 失败: {e}"))?;
return Ok("设备已配对(已修正平台字段)".into());
}
return Ok("设备已配对".into());
}
@@ -61,7 +85,8 @@ pub fn auto_pair_device() -> Result<String, String> {
paired[&device_id] = serde_json::json!({
"deviceId": device_id,
"publicKey": public_key,
"platform": "desktop",
"platform": os_platform,
"deviceFamily": "desktop",
"clientId": "openclaw-control-ui",
"clientMode": "ui",
"role": "operator",

View File

@@ -34,8 +34,23 @@ export async function render() {
return page
}
function renderSkeleton(container) {
const item = () => `
<div class="agent-card" style="pointer-events:none">
<div class="agent-card-header">
<div class="skeleton" style="width:40px;height:40px;border-radius:50%"></div>
<div style="flex:1;display:flex;flex-direction:column;gap:6px">
<div class="skeleton" style="width:45%;height:16px;border-radius:4px"></div>
<div class="skeleton" style="width:60%;height:12px;border-radius:4px"></div>
</div>
</div>
</div>`
container.innerHTML = [item(), item(), item()].join('')
}
async function loadAgents(page, state) {
const container = page.querySelector('#agents-list')
renderSkeleton(container)
try {
state.agents = await api.listAgents()
renderAgents(page, state)

View File

@@ -1,3 +1,15 @@
/* 骨架屏 */
.skeleton {
background: linear-gradient(90deg, var(--bg-secondary) 25%, var(--bg-tertiary, var(--bg-card-hover)) 50%, var(--bg-secondary) 75%);
background-size: 200% 100%;
animation: skeleton-shine 1.4s ease infinite;
}
@keyframes skeleton-shine {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* 卡片 */
.card {
background: var(--bg-card);