fix(settings): support custom git path and robust skills bundled scanning

This commit is contained in:
晴天
2026-04-05 21:33:35 +08:00
parent 2829be1bd2
commit b2ab316353
5 changed files with 372 additions and 33 deletions

View File

@@ -76,4 +76,10 @@ export default {
proxyCleared: _('网络代理已关闭', 'Network proxy disabled', '網路代理已關閉', 'ネットワークプロキシ無効化', '네트워크 프록시 비활성화됨'),
modelProxyOn: _('模型请求将走代理', 'Model requests will use proxy', '模型請求將走代理', 'モデルリクエストはプロキシを使用します', '모델 요청이 프록시를 사용합니다'),
modelProxyOff: _('模型请求已关闭代理', 'Model requests proxy disabled', '模型請求已關閉代理', 'モデルリクエストプロキシ無効化', '모델 요청 프록시 비활성화됨'),
gitPath: _('Git 可执行文件路径', 'Git Executable Path', 'Git 可執行檔路徑', 'Git 実行ファイルパス', 'Git 실행 파일 경로'),
gitPathHint: _('自定义 Git 可执行文件路径。留空则自动从系统 PATH 中查找。当系统找不到 Git 时,可在此手动指定完整路径。', 'Custom Git executable path. Leave empty to auto-detect from system PATH. Specify the full path here if the system cannot find Git.', '自定義 Git 可執行檔路徑。留空則自動從系統 PATH 中尋找。當系統找不到 Git 時,可在此手動指定完整路徑。', 'カスタム Git 実行ファイルパス。空欄にするとシステム PATH から自動検出します。システムが Git を見つけられない場合はここにフルパスを指定してください。'),
gitPathPlaceholder: _('留空自动检测,例如 C:\\Program Files\\Git\\cmd\\git.exe', 'Leave empty for auto-detect, e.g. C:\\Program Files\\Git\\cmd\\git.exe', '留空自動檢測,例如 /usr/local/bin/git', '空欄で自動検出、例: C:\\Program Files\\Git\\cmd\\git.exe'),
gitPathSaved: _('Git 路径已保存', 'Git path saved', 'Git 路徑已儲存', 'Git パス保存済み', 'Git 경로 저장됨'),
gitPathCleared: _('已恢复 Git 自动检测', 'Git auto-detect restored', '已恢復 Git 自動檢測', 'Git 自動検出に戻しました', 'Git 자동 감지로 복원됨'),
gitPathInvalid: _('指定的 Git 路径不存在', 'The specified Git path does not exist', '指定的 Git 路徑不存在', '指定された Git パスが存在しません'),
}

View File

@@ -100,6 +100,11 @@ export async function render() {
<div id="docker-defaults-bar"><div class="stat-card loading-placeholder" style="height:84px"></div></div>
</div>
<div class="config-section" id="git-path-section">
<div class="config-section-title">${t('settings.gitPath')}</div>
<div id="git-path-bar"><div class="stat-card loading-placeholder" style="height:48px"></div></div>
</div>
<div class="config-section" id="cli-binding-section">
<div class="config-section-title">${t('settings.openclawCli')}</div>
<div id="cli-binding-bar"><div class="stat-card loading-placeholder" style="height:48px"></div></div>
@@ -123,7 +128,7 @@ export async function render() {
}
async function loadAll(page) {
const tasks = [loadProxyConfig(page), loadModelProxyConfig(page), loadOpenclawDir(page), loadOpenclawSearchPaths(page), loadDockerDefaults(page), loadCliBinding(page)]
const tasks = [loadProxyConfig(page), loadModelProxyConfig(page), loadOpenclawDir(page), loadOpenclawSearchPaths(page), loadDockerDefaults(page), loadGitPath(page), loadCliBinding(page)]
tasks.push(loadRegistry(page))
if (window.__TAURI_INTERNALS__) tasks.push(loadAutostart(page))
await Promise.all(tasks)
@@ -460,6 +465,12 @@ function bindEvents(page) {
case 'save-docker-defaults':
await handleSaveDockerDefaults(page)
break
case 'save-git-path':
await handleSaveGitPath(page)
break
case 'reset-git-path':
await handleResetGitPath(page)
break
case 'bind-cli':
await handleBindCli(page, btn.dataset.path)
break
@@ -548,6 +559,68 @@ async function handleSaveRegistry(page) {
toast(t('settings.registrySaved'), 'success')
}
// ===== Git 路径 =====
async function loadGitPath(page) {
const bar = page.querySelector('#git-path-bar')
if (!bar) return
try {
const gitInfo = await api.checkGit()
const cfg = await api.readPanelConfig()
const customValue = cfg?.gitPath || ''
const invalidCustom = gitInfo.isCustom && !gitInfo.installed
const statusText = gitInfo.installed
? `<span style="color:var(--success)">✓ ${escapeHtml(gitInfo.version || 'Git')}</span>`
: invalidCustom
? `<span style="color:var(--error)">✗ ${t('settings.gitPathInvalid')}</span>`
: `<span style="color:var(--error)">✗ Git ${t('setup.notInstalled')}</span>`
const pathText = gitInfo.path ? `<span style="font-size:var(--font-size-xs);opacity:0.7">${escapeHtml(gitInfo.path)}</span>` : ''
const customBadge = gitInfo.isCustom ? `<span class="badge" style="margin-left:6px;font-size:10px">${t('settings.customBadge')}</span>` : ''
bar.innerHTML = `
<div class="stat-card" style="padding:16px">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
${statusText}${customBadge}
</div>
${pathText ? `<div style="margin-bottom:10px">${pathText}</div>` : ''}
<p style="font-size:var(--font-size-xs);color:var(--text-tertiary);margin-bottom:12px;line-height:1.5">${t('settings.gitPathHint')}</p>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input class="input" data-name="git-path" value="${escapeHtml(customValue)}" placeholder="${t('settings.gitPathPlaceholder')}" style="flex:1;min-width:200px">
<button class="btn btn-primary btn-sm" data-action="save-git-path">${t('common.save')}</button>
<button class="btn btn-secondary btn-sm" data-action="reset-git-path">${t('settings.resetDefault')}</button>
</div>
</div>`
} catch (e) {
bar.innerHTML = `<div class="stat-card" style="padding:16px;color:var(--error)">${e}</div>`
}
}
async function handleSaveGitPath(page) {
const input = page.querySelector('[data-name="git-path"]')
const value = (input?.value || '').trim()
const cfg = await api.readPanelConfig()
if (value) {
cfg.gitPath = value
} else {
delete cfg.gitPath
}
await api.writePanelConfig(cfg)
const gitInfo = await api.checkGit()
if (value && gitInfo.isCustom && !gitInfo.installed) {
toast(t('settings.gitPathInvalid'), 'error')
} else {
toast(value ? t('settings.gitPathSaved') : t('settings.gitPathCleared'), 'success')
}
await loadGitPath(page)
}
async function handleResetGitPath(page) {
const cfg = await api.readPanelConfig()
delete cfg.gitPath
await api.writePanelConfig(cfg)
toast(t('settings.gitPathCleared'), 'success')
await loadGitPath(page)
}
// ===== CLI 绑定 =====
async function loadCliBinding(page) {