mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-07-31 18:58:37 +08:00
fix(setup): 补齐跨端 Node 检测与新手引导
This commit is contained in:
@@ -922,9 +922,9 @@ function getOnboardingSteps({ gw, config, agents, channels }) {
|
||||
// 步骤 2:至少配了一个 provider 且非空
|
||||
const providers = config?.models?.providers || {}
|
||||
const hasModel = Object.keys(providers).length > 0
|
||||
// 步骤 3:自定义 Agent(默认 main 不算)
|
||||
// 步骤 3:默认 main Agent 已经可用时无需再创建新 Agent。
|
||||
const agentList = Array.isArray(agents) ? agents : []
|
||||
const hasCustomAgent = agentList.some(a => a && a.id && a.id !== 'main')
|
||||
const hasAgent = agentList.some(a => a && (a.id || a.name))
|
||||
// 步骤 4:渠道接入(不是必须,但作为「已开始用」的标志)
|
||||
// 实际上更好的判定是「点过聊天页 / 发过一条消息」,但目前没记录,先用 channels 数量作为可选完成判据
|
||||
// 改为:把第 4 步定义为「尝试聊天」—— 不强校验,CTA 触发跳转即可(用户点了就当完成)
|
||||
@@ -934,7 +934,7 @@ function getOnboardingSteps({ gw, config, agents, channels }) {
|
||||
return [
|
||||
{ id: 'gateway', titleKey: 'onboardingStep1Title', descKey: 'onboardingStep1Desc', ctaKey: 'onboardingStep1Cta', route: '/services', done: gwRunning },
|
||||
{ id: 'model', titleKey: 'onboardingStep2Title', descKey: 'onboardingStep2Desc', ctaKey: 'onboardingStep2Cta', route: '/models', done: hasModel },
|
||||
{ id: 'agent', titleKey: 'onboardingStep3Title', descKey: 'onboardingStep3Desc', ctaKey: 'onboardingStep3Cta', route: '/agents', done: hasCustomAgent },
|
||||
{ id: 'agent', titleKey: 'onboardingStep3Title', descKey: 'onboardingStep3Desc', ctaKey: 'onboardingStep3Cta', route: '/agents', done: hasAgent },
|
||||
{ id: 'chat', titleKey: 'onboardingStep4Title', descKey: 'onboardingStep4Desc', ctaKey: 'onboardingStep4Cta', route: '/chat', done: hasChatTried, markOnClick: 'clawpanel_onboarding_chat_clicked' },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -54,10 +54,36 @@ function isWindowsPlatform() {
|
||||
return navigator.platform?.startsWith('Win') || navigator.userAgent?.includes('Windows')
|
||||
}
|
||||
|
||||
function isLinuxPlatform() {
|
||||
return navigator.platform?.toLowerCase().includes('linux') || navigator.userAgent?.toLowerCase().includes('linux')
|
||||
}
|
||||
|
||||
function canAutoUpgradeNode() {
|
||||
return isTauriRuntime() && isWindowsPlatform()
|
||||
}
|
||||
|
||||
function nodeRuntimeHint(nodeTooOld) {
|
||||
if (!nodeTooOld) return t('setup.winNodeHint')
|
||||
if (isWindowsPlatform()) return t('setup.winNodeUpgradeHint')
|
||||
if (isMacPlatform()) return t('setup.macNodeUpgradeHint')
|
||||
if (isLinuxPlatform()) return t('setup.linuxNodeUpgradeHint')
|
||||
return t('setup.genericNodeUpgradeHint')
|
||||
}
|
||||
|
||||
function nodePathPlaceholder() {
|
||||
if (isMacPlatform()) return '/usr/local/bin'
|
||||
if (isLinuxPlatform()) return '/usr/bin'
|
||||
return 'F:\\AI\\Node'
|
||||
}
|
||||
|
||||
function normalizeNodeUpgradeLog(line) {
|
||||
const text = String(line || '').trimEnd()
|
||||
if (!text.trim()) return null
|
||||
if (/[█▓▒░]/.test(text)) return null
|
||||
if (/^[\s\\|/\-]+$/.test(text)) return null
|
||||
return text
|
||||
}
|
||||
|
||||
function renderDetectionHint(pathValue, sourceLabel = '') {
|
||||
const normalizedPath = String(pathValue || '').trim()
|
||||
const normalizedSource = String(sourceLabel || '').trim()
|
||||
@@ -297,16 +323,16 @@ function renderSteps(page, { node, git, cliOk, config, version }) {
|
||||
<div style="margin-top:var(--space-sm);padding:10px 12px;background:var(--bg-tertiary);border-radius:var(--radius-sm);font-size:var(--font-size-xs);color:var(--text-secondary);line-height:1.6">
|
||||
<strong>${nodeTooOld ? t('setup.nodeUnsupportedTitle') : t('setup.nodeInstalledButNotDetected')}</strong>
|
||||
${isMacPlatform()
|
||||
? `${t('setup.macNodeHint')}<br>
|
||||
? `${nodeTooOld ? t('setup.macNodeUpgradeHint') : t('setup.macNodeHint')}<br>
|
||||
<code style="background:var(--bg-secondary);padding:2px 6px;border-radius:3px;user-select:all">open /Applications/ClawPanel.app</code>`
|
||||
: `${nodeTooOld ? t('setup.winNodeUpgradeHint') : t('setup.winNodeHint')}`
|
||||
: `${nodeRuntimeHint(nodeTooOld)}`
|
||||
}
|
||||
<div style="margin-top:8px;display:flex;gap:6px;align-items:center;flex-wrap:wrap">
|
||||
<button class="btn btn-secondary btn-sm" id="btn-scan-node" style="font-size:11px;padding:3px 10px">${icon('search', 12)} ${t('setup.scanNodeBtn')}</button>
|
||||
<span style="color:var(--text-tertiary)">${t('setup.orManualPath')}</span>
|
||||
</div>
|
||||
<div class="setup-input-row" style="margin-top:6px">
|
||||
<input id="input-node-path" type="text" placeholder="${isMacPlatform() ? '/usr/local/bin' : 'F:\\AI\\Node'}"
|
||||
<input id="input-node-path" type="text" placeholder="${nodePathPlaceholder()}"
|
||||
style="flex:1;padding:4px 8px;border:1px solid var(--border-primary);border-radius:var(--radius-sm);background:var(--bg-secondary);color:var(--text-primary);font-size:11px;font-family:monospace">
|
||||
<button class="btn btn-primary btn-sm" id="btn-check-path" style="font-size:11px;padding:3px 10px">${t('setup.checkPathBtn')}</button>
|
||||
</div>
|
||||
@@ -718,12 +744,17 @@ function bindEvents(page, nodeOk, detectState) {
|
||||
btn.disabled = true
|
||||
btn.textContent = t('setup.upgradingNode')
|
||||
try {
|
||||
unlistenLog = await safeTauriListen('upgrade-log', (e) => modal.appendLog(e.payload))
|
||||
unlistenLog = await safeTauriListen('upgrade-log', (e) => {
|
||||
const line = normalizeNodeUpgradeLog(e.payload)
|
||||
if (line) modal.appendLog(line)
|
||||
})
|
||||
unlistenProgress = await safeTauriListen('upgrade-progress', (e) => modal.setProgress(e.payload))
|
||||
const msg = await api.autoInstallNode()
|
||||
modal.setProgress(100)
|
||||
modal.setDone(msg || t('setup.nodeUpgradeSuccess'))
|
||||
modal.setCloseText(t('common.completed'))
|
||||
modal.appendLog(t('setup.nodeUpgradeRedetecting'))
|
||||
modal.appendLog(t('setup.nodeUpgradeStartGatewayHint'))
|
||||
toast(msg || t('setup.nodeUpgradeSuccess'), 'success')
|
||||
await api.invalidatePathCache().catch(() => {})
|
||||
setTimeout(() => runDetect(page), 800)
|
||||
|
||||
Reference in New Issue
Block a user