chore: release v0.11.5

feat: SkillHub skill store (SDK-based, no CLI dependency)
- Rust SDK (skillhub.rs): HTTP search, index fetch, zip download+extract
- Node.js SDK (skillhub-sdk.js): mirrors Rust SDK for Web/Docker mode
- Skills page: new "Store" tab with full index browse + client-side filter
- Remove 6 old CLI-dependent commands, add 3 SDK commands
- Migrate assistant.js skill tools from ClawHub CLI to SkillHub SDK
- Fix index decode error ({total,skills} wrapper vs bare array)
- Fix skill name display (API field 'name' vs 'display_name')
- Clean up 13 dead CSS rules from old skills hero/tips UI
This commit is contained in:
晴天
2026-04-07 03:25:26 +08:00
parent b57235e2a7
commit ad00ffef3d
20 changed files with 1244 additions and 851 deletions

View File

@@ -0,0 +1,559 @@
# Skills 页面重构规划
> 目标:**完全去掉 CLI 依赖**,用 Rust `reqwest` 内置 SkillHub API 调用 + zip 解压,用户无需安装任何额外工具即可搜索/安装/管理 Skills。
---
## 一、现状分析
### 现有架构(问题)
```
┌─────────────┐ ┌───────────────────┐ ┌──────────────┐
│ skills.js │ ───→ │ skills.rs / dev- │ ───→ │ 外部 CLI 进程 │
│ (前端 UI) │ │ api.js (后端) │ │ │
└─────────────┘ └───────────────────┘ └──────┬───────┘
┌────────────┼────────────┐
▼ ▼ ▼
openclaw CLI skillhub CLI clawhub CLI
(skills list) (search/install) (npx)
```
**痛点清单:**
| # | 问题 | 影响 |
|---|------|------|
| 1 | **必须安装 OpenClaw CLI** 才能查看已安装 Skills 列表 | 新用户看到空白页或报错 |
| 2 | **必须安装 SkillHub CLI** 才能从国内源搜索/安装 | 额外安装步骤,需要 npm |
| 3 | **ClawHub 用 `npx -y clawhub`** 每次冷启动 ~10s | 体验差,且海外源限流 |
| 4 | CLI 输出是人类可读文本,需要**正则解析** | 脆弱CLI 版本更新就可能坏 |
| 5 | 前端有两个安装源下拉 + CLI 检测状态 UI | 复杂且令人困惑 |
| 6 | `skills_list` 依赖 CLI超时/失败才 fallback 本地扫描 | 不可靠,延迟高 |
### 可保留的部分
| 模块 | 保留? | 说明 |
|------|--------|------|
| `scan_local_skills()` / 本地扫描逻辑 | ✅ 保留 | 扫描 `~/.openclaw/skills/` 等目录,不依赖 CLI |
| `scan_single_skill()` | ✅ 保留 | 解析 SKILL.md frontmatter + package.json |
| `skills_uninstall()` | ✅ 保留 | 简单的 `rm -rf`,无 CLI 依赖 |
| `skills_validate()` | ✅ 保留 | 纯本地文件检查 |
| `skills_install_dep()` | ✅ 保留 | brew/npm/go/uv 本地包管理器 |
| 前端已安装 Tab 的分组渲染 | ✅ 保留 | eligible/missing/disabled/blocked 分组 |
| 前端过滤搜索 | ✅ 保留 | 实时 filter |
---
## 二、SkillHub API 协议
从 CLI 源码逆向得到的接口(腾讯云 COS + API 后端):
| 功能 | URL | 返回 |
|------|-----|------|
| **搜索** | `GET https://lightmake.site/api/v1/search?q={query}&limit={limit}` | `{ results: [{ slug, displayName, summary, version }] }` |
| **主下载** | `GET https://lightmake.site/api/v1/download?slug={slug}` | zip 二进制 |
| **COS 镜像下载** | `GET https://skillhub-1388575217.cos.ap-guangzhou.myqcloud.com/skills/{slug}.zip` | zip 二进制(国内加速) |
| **全量索引** | `GET https://skillhub-1388575217.cos.ap-guangzhou.myqcloud.com/skills.json` | JSON 数组 |
### 安装流程(内置化)
```
搜索 → 选择 Skill → 下载 zip → 解压到 ~/.openclaw/skills/{slug}/ → 完成
```
不需要任何 CLI 工具。
---
## 三、新架构设计SDK 模式 + 双平台)
关键设计:**抽出独立 SDK 层**Tauri 桌面端和 Web/Docker 端各实现一份,上层 API 接口一致,后续调整只改 SDK 即可。
```
┌──────────────────┐
│ skills.js (前端) │ 统一 UI不关心后端是 Rust 还是 Node
└────────┬─────────┘
│ invoke / fetch
┌──────────────────────────────────────────────────────────┐
│ Tauri 命令层 / dev-api 路由层 │
│ skills.rs (Tauri) dev-api.js (Web/Docker) │
│ ↓ 调用 ↓ 调用 │
│ skillhub.rs (Rust SDK) skillhub-sdk.js (Node SDK) │
└──────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────────────────────────────────────────────────┐
│ SkillHub API + COS │
│ 搜索: lightmake.site/api/v1/search │
│ 索引: cos.ap-guangzhou.myqcloud.com/skills.json │
│ 下载: cos.ap-guangzhou.myqcloud.com/skills/{slug}.zip │
│ 回退: lightmake.site/api/v1/download │
└──────────────────────────────────────────────────────────┘
```
### 为什么用 SDK 模式?
| 优势 | 说明 |
|------|------|
| **解耦** | SDK 只管 HTTP + zip不涉及 Tauri/Express 框架 |
| **双平台** | Rust SDK 给 Tauri 桌面端用Node SDK 给 Web/Docker 端用 |
| **易调整** | API 域名/路径/认证变了,只改 SDK 一个文件 |
| **可测试** | SDK 函数可单独写单元测试 |
| **可复用** | 未来其他模块要调 SkillHub 也直接引 SDK |
---
## 四、Rust SDK`src-tauri/src/commands/skillhub.rs`
独立模块,不依赖 Tauri 框架,纯 `reqwest` + `zip` + `serde`
### 4.1 数据结构
```rust
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillHubItem {
pub slug: String,
#[serde(alias = "displayName")]
pub display_name: Option<String>,
pub summary: Option<String>,
pub version: Option<String>,
}
#[derive(Debug, Deserialize)]
struct SearchResponse {
results: Vec<SkillHubItem>,
}
```
### 4.2 SDK 公开接口
```rust
/// 搜索 SkillHub
pub async fn search(query: &str, limit: u32) -> Result<Vec<SkillHubItem>, String>
/// 拉取全量索引(带 10 分钟内存缓存)
pub async fn fetch_index() -> Result<Vec<SkillHubItem>, String>
/// 下载并安装 Skillzip → 解压到 target_dir
pub async fn install(slug: &str, skills_dir: &Path) -> Result<PathBuf, String>
/// 仅下载 zip 字节COS 优先,回退主站)
pub async fn download_zip(slug: &str) -> Result<Vec<u8>, String>
```
### 4.3 下载策略
```rust
const COS_BASE: &str = "https://skillhub-1388575217.cos.ap-guangzhou.myqcloud.com";
const API_BASE: &str = "https://lightmake.site/api/v1";
pub async fn download_zip(slug: &str) -> Result<Vec<u8>, String> {
// 1. 优先 COS 镜像(国内 CDN毫秒级
let cos_url = format!("{}/skills/{}.zip", COS_BASE, slug);
if let Ok(resp) = client().get(&cos_url).send().await {
if resp.status().is_success() {
return resp.bytes().await
.map(|b| b.to_vec())
.map_err(|e| format!("COS 下载失败: {e}"));
}
}
// 2. 回退主站 API
let api_url = format!("{}/download?slug={}", API_BASE, slug);
let resp = client().get(&api_url).send().await
.map_err(|e| format!("主站下载失败: {e}"))?;
if !resp.status().is_success() {
return Err(format!("下载失败: HTTP {}", resp.status()));
}
resp.bytes().await
.map(|b| b.to_vec())
.map_err(|e| format!("读取下载内容失败: {e}"))
}
```
### 4.4 全量索引缓存
```rust
use std::sync::Mutex;
use std::time::{Duration, Instant};
use once_cell::sync::Lazy;
static INDEX_CACHE: Lazy<Mutex<Option<(Instant, Vec<SkillHubItem>)>>> =
Lazy::new(|| Mutex::new(None));
pub async fn fetch_index() -> Result<Vec<SkillHubItem>, String> {
// 命中缓存10 分钟有效)
if let Ok(guard) = INDEX_CACHE.lock() {
if let Some((ts, ref items)) = *guard {
if ts.elapsed() < Duration::from_secs(600) {
return Ok(items.clone());
}
}
}
// 拉取远程索引
let url = format!("{}/skills.json", COS_BASE);
let items: Vec<SkillHubItem> = client().get(&url).send().await
.map_err(|e| format!("拉取索引失败: {e}"))?
.json().await
.map_err(|e| format!("解析索引失败: {e}"))?;
// 写入缓存
if let Ok(mut guard) = INDEX_CACHE.lock() {
*guard = Some((Instant::now(), items.clone()));
}
Ok(items)
}
```
### 4.5 zip 解压
```rust
pub fn extract_zip(zip_bytes: &[u8], target_dir: &Path) -> Result<(), String> {
use std::io::Cursor;
use zip::ZipArchive;
if target_dir.exists() {
std::fs::remove_dir_all(target_dir)
.map_err(|e| format!("清理旧目录失败: {e}"))?;
}
std::fs::create_dir_all(target_dir)
.map_err(|e| format!("创建目录失败: {e}"))?;
let reader = Cursor::new(zip_bytes);
let mut archive = ZipArchive::new(reader)
.map_err(|e| format!("打开 zip 失败: {e}"))?;
for i in 0..archive.len() {
let mut file = archive.by_index(i)
.map_err(|e| format!("读取 zip 条目失败: {e}"))?;
let name = file.name().to_string();
// 安全检查:防止路径穿越
if name.contains("..") { continue; }
let out_path = target_dir.join(&name);
if file.is_dir() {
std::fs::create_dir_all(&out_path).ok();
} else {
if let Some(parent) = out_path.parent() {
std::fs::create_dir_all(parent).ok();
}
let mut outfile = std::fs::File::create(&out_path)
.map_err(|e| format!("创建文件失败 {name}: {e}"))?;
std::io::copy(&mut file, &mut outfile)
.map_err(|e| format!("写入文件失败 {name}: {e}"))?;
}
}
Ok(())
}
```
---
## 五、Node.js SDK`scripts/lib/skillhub-sdk.js`
给 Web/Docker 端用API 接口与 Rust SDK **完全对齐**
### 5.1 模块结构
```javascript
// scripts/lib/skillhub-sdk.js
const COS_BASE = 'https://skillhub-1388575217.cos.ap-guangzhou.myqcloud.com'
const API_BASE = 'https://lightmake.site/api/v1'
let _indexCache = null // { ts: Date.now(), items: [] }
const INDEX_TTL = 10 * 60 * 1000 // 10 分钟
module.exports = { search, fetchIndex, install, downloadZip }
```
### 5.2 SDK 公开接口
```javascript
/**
* 搜索 SkillHub
* @param {string} query
* @param {number} [limit=20]
* @returns {Promise<Array<{slug, displayName, summary, version}>>}
*/
async function search(query, limit = 20) {
const url = `${API_BASE}/search?q=${encodeURIComponent(query)}&limit=${limit}`
const resp = await fetch(url)
if (!resp.ok) throw new Error(`搜索失败: HTTP ${resp.status}`)
const data = await resp.json()
return data.results || []
}
/**
* 拉取全量索引(带缓存)
* @returns {Promise<Array<{slug, displayName, summary, version}>>}
*/
async function fetchIndex() {
if (_indexCache && Date.now() - _indexCache.ts < INDEX_TTL) {
return _indexCache.items
}
const resp = await fetch(`${COS_BASE}/skills.json`)
if (!resp.ok) throw new Error(`拉取索引失败: HTTP ${resp.status}`)
const items = await resp.json()
_indexCache = { ts: Date.now(), items }
return items
}
/**
* 下载 zipCOS 优先,回退主站)
* @param {string} slug
* @returns {Promise<Buffer>}
*/
async function downloadZip(slug) {
// COS 优先
try {
const resp = await fetch(`${COS_BASE}/skills/${slug}.zip`)
if (resp.ok) return Buffer.from(await resp.arrayBuffer())
} catch {}
// 回退主站
const resp = await fetch(`${API_BASE}/download?slug=${encodeURIComponent(slug)}`)
if (!resp.ok) throw new Error(`下载失败: HTTP ${resp.status}`)
return Buffer.from(await resp.arrayBuffer())
}
/**
* 下载并安装 Skill
* @param {string} slug
* @param {string} skillsDir - ~/.openclaw/skills/
* @returns {Promise<string>} 安装路径
*/
async function install(slug, skillsDir) {
const zipBuf = await downloadZip(slug)
const targetDir = path.join(skillsDir, slug)
// 清理旧目录
if (fs.existsSync(targetDir)) fs.rmSync(targetDir, { recursive: true, force: true })
fs.mkdirSync(targetDir, { recursive: true })
// 解压(用 Node.js 内置或 adm-zip
const AdmZip = require('adm-zip')
const zip = new AdmZip(zipBuf)
zip.extractAllTo(targetDir, true)
return targetDir
}
```
### 5.3 dev-api.js 集成
```javascript
const skillhub = require('./lib/skillhub-sdk')
// 路由处理器中直接调用 SDK
handlers = {
skillhub_search({ query, limit }) { return skillhub.search(query, limit) },
skillhub_index() { return skillhub.fetchIndex() },
skillhub_install({ slug }) { return skillhub.install(slug, SKILLS_DIR) },
// skills_list → 纯本地扫描(已有 scanLocalSkillsFallback
skills_list() { return scanLocalSkillsFallback() },
}
```
---
## 六、命令层改造
### 6.1 skills.rs — Tauri 命令层(薄包装)
改造后 `skills.rs` 只是 SDK 的 Tauri 命令薄包装:
```rust
mod skillhub; // SDK 模块
#[tauri::command]
pub async fn skillhub_search(query: String, limit: Option<u32>) -> Result<Value, String> {
let items = skillhub::search(&query, limit.unwrap_or(20)).await?;
Ok(serde_json::to_value(items).unwrap())
}
#[tauri::command]
pub async fn skillhub_index() -> Result<Value, String> {
let items = skillhub::fetch_index().await?;
Ok(serde_json::to_value(items).unwrap())
}
#[tauri::command]
pub async fn skillhub_install(slug: String) -> Result<Value, String> {
let skills_dir = super::openclaw_dir().join("skills");
let path = skillhub::install(&slug, &skills_dir).await?;
Ok(serde_json::json!({ "success": true, "slug": slug, "path": path.to_string_lossy() }))
}
// skills_list → 纯本地扫描(复用已有 scan_local_skills
#[tauri::command]
pub async fn skills_list() -> Result<Value, String> {
scan_local_skills(None) // 不再调 CLI
}
```
### 6.2 可删除的命令
| 命令 | 原因 |
|------|------|
| `skills_skillhub_check` | 不再需要检测 CLI |
| `skills_skillhub_setup` | 不再需要安装 CLI |
| `skills_skillhub_search` | 替换为 `skillhub_search`SDK |
| `skills_skillhub_install` | 替换为 `skillhub_install`SDK |
| `skills_clawhub_search` | 合并到 SkillHub |
| `skills_clawhub_install` | 合并到 SkillHub |
### 6.3 保留的命令
| 命令 | 说明 |
|------|------|
| `skills_list` | 改为纯本地扫描 |
| `skills_info` | 改为纯本地文件解析 |
| `skills_uninstall` | 不变(删目录) |
| `skills_validate` | 不变(本地文件检查) |
| `skills_install_dep` | 不变brew/npm/go/uv |
---
## 七、前端改造(`skills.js`
### 7.1 UI 简化
**删除:**
- 安装源下拉(`<select id="install-source-select">`)— 统一为 SkillHub
- SkillHub CLI 状态检测 / 安装按钮
- ClawHub 源相关 UI
- `checkSkillHubStatus()` / `switchInstallSource()` 等函数
**保留:**
- 两个 Tab"已安装" / "技能商店"
- 已安装 Tab 的分组渲染eligible/missing/disabled/blocked
- 实时过滤搜索
- Skill 卡片渲染
**新增:**
- 技能商店 Tab 改为**浏览模式**:默认加载全量索引(热门/推荐),支持搜索过滤
- 安装进度条/状态(下载中 → 解压中 → 完成)
- 已安装 Skill 的**更新检测**(比对本地版本 vs 索引版本)
### 7.2 新的"技能商店"Tab 布局
```
┌──────────────────────────────────────────────────┐
│ 🔍 搜索技能... [浏览 SkillHub] │
├──────────────────────────────────────────────────┤
│ │
│ 📦 weather ☀️ 天气查询 [安装] │
│ 📦 github 🐙 GitHub 操作 [安装] │
│ 📦 tavily 🔍 网页搜索 [安装] │
│ 📦 feishu-doc 📄 飞书文档 [安装] │
│ ... │
│ │
└──────────────────────────────────────────────────┘
```
- 页面进入时自动加载全量索引COS CDN国内毫秒级
- 搜索框实时过滤(客户端)+ 回车触发服务端搜索(更精准)
- 已安装的 Skill 显示"已安装"灰色标记,不显示安装按钮
### 7.3 API 调用映射
| 旧 API | 新 API | 说明 |
|--------|--------|------|
| `api.skillsList()` | `api.skillsList()` | 后端改为纯本地扫描 |
| `api.skillsSkillHubCheck()` | ❌ 删除 | 不再需要 |
| `api.skillsSkillHubSetup()` | ❌ 删除 | 不再需要 |
| `api.skillsSkillHubSearch(q)` | `api.skillhubSearch(q)` | 内置 HTTP 调用 |
| `api.skillsSkillHubInstall(slug)` | `api.skillhubInstall(slug)` | 内置下载+解压 |
| `api.skillsClawHubSearch(q)` | ❌ 删除 | 统一到 SkillHub |
| `api.skillsClawHubInstall(slug)` | ❌ 删除 | 统一到 SkillHub |
| — | `api.skillhubIndex()` | 新增:全量索引 |
---
## 八、i18n 改造
### 删除的 key
```
skillhubNeedCLI, skillhubNeedCLIHint, skillhubSetup,
skillhubInstalling, skillhubInstalled, skillhubInstallFailed,
sourceSkillHub, sourceClawHub, installCLI,
rateLimitClawHub, sourceLocalScanTimeout, sourceLocalScanParseFailed,
sourceLocalScanExecFailed, sourceLocalScan, sourceLocalScanNoCli, sourceCLI,
loadFailedHint (不再需要提示安装 OpenClaw)
```
### 新增/修改的 key
```
storeTitle: '技能商店' / 'Skill Store'
storeLoading: '正在加载技能索引...' / 'Loading skill index...'
storeLoadFailed: '加载技能索引失败' / 'Failed to load skill index'
downloading: '下载中...' / 'Downloading...'
extracting: '解压中...' / 'Extracting...'
updateAvailable: '可更新' / 'Update available'
update: '更新' / 'Update'
```
---
## 九、实施步骤
### Phase 1Rust SDK 模块(`skillhub.rs`
1. 新建 `src-tauri/src/commands/skillhub.rs`
2. 实现 `SkillHubItem` 数据结构
3. 实现 `search()``fetch_index()``download_zip()``install()``extract_zip()`
4. 实现全量索引内存缓存
5.`commands/mod.rs` 中声明 `pub mod skillhub`
6. `cargo check` 验证 SDK 模块编译通过
### Phase 2Node.js SDK 模块(`skillhub-sdk.js`
1. 新建 `scripts/lib/skillhub-sdk.js`
2. 实现 `search()``fetchIndex()``downloadZip()``install()`
3. 确认 `adm-zip` 已在 devDependencies或改用 Node 内置 `zlib` + `tar`
4. `node --check` 验证
### Phase 3命令层改造skills.rs + dev-api.js
1. `skills.rs`:新增 `skillhub_search``skillhub_index``skillhub_install` Tauri 命令(薄包装 SDK
2. `skills.rs`:改造 `skills_list` → 纯本地扫描,改造 `skills_info` → 纯本地解析
3. `skills.rs`:删除 6 个旧 CLI 命令
4. `lib.rs`:更新命令注册
5. `dev-api.js`:路由层接入 `skillhub-sdk.js`,删除旧 CLI 调用
6. `cargo check` + `node --check` 验证
### Phase 4前端 UI 重写skills.js + tauri-api.js
1. 更新 `tauri-api.js` API 映射(新增 3 个,删除 6 个)
2. 重写"技能商店"Tab — 默认加载全量索引,搜索过滤,一键安装
3. 简化已安装 Tab — 删除 CLI 状态提示和诊断信息
4. 删除 `switchInstallSource``checkSkillHubStatus``handleSkillHubSetup`
5. 添加安装进度反馈(下载中 → 解压中 → 完成)
### Phase 5i18n + 清理 + 验证
1. 更新 `locales/modules/skills.js`(删除旧 key新增商店 key
2. 清理 `assistant.js` 中的 skills 工具定义(如有需要)
3. `cargo check` + `npx vite build` 全量验证
4. 手动测试已安装 Tab + 技能商店 Tab
---
## 十、风险与兼容性
| 风险 | 缓解 |
|------|------|
| SkillHub API 不可用 | COS 镜像作为备选;全量索引可离线缓存 |
| zip 解压路径安全 | 校验 slug 无 `..`/`/`/`\`;解压时检查相对路径 |
| 已有用户的 Skills 目录结构不兼容 | 不变 — 仍然解压到 `~/.openclaw/skills/{slug}/` |
| `skills_list` 去掉 CLI 后丢失 bundled skills 信息 | `custom_skill_roots()` 已包含 bundled 路径推导 |
| `skills_install_dep` (brew/npm/go/uv) 仍需本地工具 | 保留 — 这是 Skill 运行时依赖,不是安装工具依赖 |
---
## 十一、预期效果
| 指标 | 改造前 | 改造后 |
|------|--------|--------|
| 首次使用需安装 | OpenClaw CLI + SkillHub CLI | **无需安装** |
| 搜索延迟 | ~3-10sCLI 冷启动) | **<1s**HTTP API |
| 安装延迟 | ~5-15sCLI 调用) | **~2-5s**(直接下载 zip |
| 前端代码复杂度 | 492 行(含双源切换/CLI 检测) | ~300 行(统一 UI |
| 后端 CLI 调用 | 8 个命令依赖外部 CLI | **0 个** |
| 用户认知负担 | 安装源选择 + CLI 状态 | 搜索框 + 安装按钮 |
| Web/Docker 端 | CLI 经常找不到或权限问题 | **内置 HTTP与桌面端体验一致** |

View File

@@ -34,7 +34,7 @@
"description": "OpenClaw AI Agent 可视化管理面板,基于 Tauri v2 的跨平台桌面应用。内置晴辰助手支持工具调用,晴辰云 AI 接口一键接入。支持仪表盘监控、多模型配置、消息渠道管理、内置 QQ 机器人、实时 AI 聊天、记忆管理、Agent 管理、网关配置、内网穿透等功能。支持 11 种语言。",
"url": "https://claw.qt.cool/",
"downloadUrl": "https://github.com/qingchencloud/clawpanel/releases/latest",
"softwareVersion": "0.11.4",
"softwareVersion": "0.11.5",
"author": {
"@type": "Organization",
"name": "晴辰云 QingchenCloud",
@@ -1155,7 +1155,7 @@
<div class="orb orb-2" style="top:auto;bottom:-100px"></div>
<div class="container-sm" style="position:relative;z-index:10">
<div class="section-header">
<div class="reveal download-version"><span class="pulse"></span> <span id="dl-badge" data-i18n="dl.badge">v0.11.4 最新版</span></div>
<div class="reveal download-version"><span class="pulse"></span> <span id="dl-badge" data-i18n="dl.badge">v0.11.5 最新版</span></div>
<h2 class="reveal section-title" data-i18n="dl.title"><span class="gradient-text">下载安装</span></h2>
<p class="reveal section-desc" data-i18n="dl.desc">选择你的操作系统,一键下载安装</p>
</div>
@@ -1165,11 +1165,11 @@
<h3>macOS</h3>
<p class="dl-desc" data-i18n="dl.mac.d">支持 Apple Silicon 和 Intel 芯片</p>
<div class="dl-links">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.4_aarch64.dmg" target="_blank" rel="noopener">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.5_aarch64.dmg" target="_blank" rel="noopener">
Apple Silicon (M1/M2/M3/M4)
<span class="dl-format">.dmg</span>
</a>
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.4_x64.dmg" target="_blank" rel="noopener">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.5_x64.dmg" target="_blank" rel="noopener">
<span data-i18n="dl.mac.intel">Intel 芯片</span>
<span class="dl-format">.dmg</span>
</a>
@@ -1187,15 +1187,15 @@
<h3>Windows</h3>
<p class="dl-desc" data-i18n="dl.win.d">支持 Windows 10 及以上版本</p>
<div class="dl-links">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.4_x64-setup.exe" target="_blank" rel="noopener">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.5_x64-setup.exe" target="_blank" rel="noopener">
<span data-i18n="dl.win.exe">安装程序</span>
<span class="dl-format">.exe</span>
</a>
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.4_x64-setup-full.exe" target="_blank" rel="noopener">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.5_x64-setup-full.exe" target="_blank" rel="noopener">
<span data-i18n="dl.win.full">完整包(含 WebView2</span>
<span class="dl-format">.exe</span>
</a>
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.4_x64_en-US.msi" target="_blank" rel="noopener">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.5_x64_en-US.msi" target="_blank" rel="noopener">
<span data-i18n="dl.win.msi">MSI 安装包</span>
<span class="dl-format">.msi</span>
</a>
@@ -1206,11 +1206,11 @@
<h3>Linux</h3>
<p class="dl-desc" data-i18n="dl.linux.d">支持主流 Linux 发行版</p>
<div class="dl-links">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.4_amd64.AppImage" target="_blank" rel="noopener">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.5_amd64.AppImage" target="_blank" rel="noopener">
<span data-i18n="dl.linux.ai">通用版</span>
<span class="dl-format">.AppImage</span>
</a>
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.4_amd64.deb" target="_blank" rel="noopener">
<a class="dl-link" href="https://claw.qt.cool/proxy/dl/github.com/qingchencloud/clawpanel/releases/latest/download/ClawPanel_0.11.5_amd64.deb" target="_blank" rel="noopener">
Debian / Ubuntu
<span class="dl-format">.deb</span>
</a>