/**
* 关于页面
* 版本信息、项目链接、相关项目、系统环境
*/
import { api } from '../lib/tauri-api.js'
import { toast } from '../components/toast.js'
import { showUpgradeModal } from '../components/modal.js'
export async function render() {
const page = document.createElement('div')
page.className = 'page'
page.innerHTML = `
ClawPanel 基于 Tauri v2 构建,前端 Vanilla JS + Vite,后端 Rust。
MIT License © 2026 qingchencloud
`
loadData(page)
renderCommunity(page)
renderProjects(page)
renderContribute(page)
renderLinks(page)
return page
}
async function loadData(page) {
const cards = page.querySelector('#version-cards')
try {
const [version, install] = await Promise.all([
api.getVersionInfo(),
api.checkInstallation(),
])
// 尝试从 Tauri API 获取 ClawPanel 自身版本号,失败则 fallback
let panelVersion = '0.1.0'
try {
const { getVersion } = await import('@tauri-apps/api/app')
panelVersion = await getVersion()
} catch {
// 非 Tauri 环境或 API 不可用,使用 fallback
}
// 异步检查 ClawPanel 自身更新
let panelUpdateHtml = '检查更新中... '
api.checkPanelUpdate().then(info => {
const panelCard = cards.querySelector('#panel-update-meta')
if (!panelCard) return
if (info.latest && info.latest !== panelVersion && compareVersions(info.latest, panelVersion) > 0) {
panelCard.innerHTML = `新版本: ${info.latest} 下载更新 `
} else {
panelCard.innerHTML = '已是最新 '
}
}).catch((err) => {
const panelCard = cards.querySelector('#panel-update-meta')
if (!panelCard) return
const msg = String(err?.message || err || '')
if (msg.includes('403') || msg.includes('404') || msg.includes('rate limit')) {
panelCard.innerHTML = '仓库未公开,发布后可自动检测 '
} else {
panelCard.innerHTML = '检查更新失败 '
}
})
cards.innerHTML = `
${panelVersion}
${panelUpdateHtml}
${version.current || '未安装'}
${version.update_available
? `新版本: ${version.latest} 升级 `
: version.current ? '已是最新 ' : '未检测到 '}
${install.path || '未知'}
${install.installed ? '配置文件存在' : '未找到配置文件'}
`
// 绑定升级按钮
const upgradeBtn = cards.querySelector('#btn-upgrade')
if (upgradeBtn) {
upgradeBtn.onclick = async () => {
const modal = showUpgradeModal()
let unlistenLog, unlistenProgress
try {
const { listen } = await import('@tauri-apps/api/event')
unlistenLog = await listen('upgrade-log', (e) => modal.appendLog(e.payload))
unlistenProgress = await listen('upgrade-progress', (e) => modal.setProgress(e.payload))
const msg = await api.upgradeOpenclaw()
modal.setDone(msg)
loadData(page)
} catch (e) {
modal.appendLog(String(e))
modal.setError('升级失败')
} finally {
unlistenLog?.()
unlistenProgress?.()
}
}
}
} catch {
cards.innerHTML = ''
}
}
function compareVersions(a, b) {
const pa = a.split('.').map(Number)
const pb = b.split('.').map(Number)
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
const na = pa[i] || 0
const nb = pb[i] || 0
if (na > nb) return 1
if (na < nb) return -1
}
return 0
}
function renderCommunity(page) {
const el = page.querySelector('#community-section')
el.innerHTML = `
QQ 交流群
微信交流群
扫码或点击链接加入交流群,反馈问题、获取帮助
2000 人大群,满员自动切换 · 碰到问题可直接在群内反馈
`
}
const PROJECTS = [
{
name: 'OpenClaw',
desc: 'AI Agent 框架,支持多模型协作、工具调用、记忆管理',
url: 'https://github.com/openclaw/openclaw',
},
{
name: 'ClawApp',
desc: '跨平台移动聊天客户端,H5 + 代理服务器架构,支持离线和流式传输',
url: 'https://github.com/qingchencloud/clawapp',
},
{
name: 'cftunnel',
desc: '全协议内网穿透工具,Cloud 模式免费 HTTP/WS + Relay 模式自建中继',
url: 'https://github.com/qingchencloud/cftunnel',
},
{
name: 'ClawPanel',
desc: 'OpenClaw 可视化管理面板,Tauri v2 桌面应用',
url: 'https://github.com/qingchencloud/clawpanel',
},
]
function renderProjects(page) {
const el = page.querySelector('#projects-list')
el.innerHTML = PROJECTS.map(p => `
`).join('')
}
const LINKS = [
{ label: 'cftunnel 官网', url: 'https://cftunnel.qt.cool' },
{ label: 'cftunnel 桌面客户端', url: 'https://github.com/qingchencloud/cftunnel-app/releases' },
{ label: 'OpenClaw 中文翻译', url: 'https://github.com/1186258278/OpenClawChineseTranslation' },
{ label: 'ClawApp 文档', url: 'https://github.com/qingchencloud/clawapp#readme' },
]
function renderContribute(page) {
const el = page.querySelector('#contribute-section')
el.innerHTML = `
ClawPanel 是开源项目,欢迎参与贡献!遇到问题请提 Issue,功能建议和代码改进欢迎提 PR。
`
}
function renderLinks(page) {
const el = page.querySelector('#links-list')
el.innerHTML = ``
}