diff --git a/.claude/settings.local.json b/.claude/settings.local.json index bc967eb..c260414 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -2,7 +2,9 @@ "permissions": { "allow": [ "WebSearch", - "Bash(find:*)" + "Bash(find:*)", + "mcp__exa__web_search_exa", + "mcp__ace-tool__search_context" ], "deny": [], "ask": [] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 41139f7..f1da945 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,8 +19,10 @@ jobs: - uses: actions/checkout@v4 - name: Update version from tag + id: version run: | VERSION=${GITHUB_REF#refs/tags/v} + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT echo "Updating version to: $VERSION" sed -i '' "s/const AppVersion = \"v[^\"]*\"/const AppVersion = \"v$VERSION\"/" version_service.go echo "Updated version_service.go:" @@ -64,13 +66,13 @@ jobs: - name: Archive macOS app run: | cd bin - ditto -c -k --sequesterRsrc --keepParent "$(basename ${{ steps.find-app.outputs.app_path }})" codeswitch-macos-${{ matrix.arch }}.zip + ditto -c -k --sequesterRsrc --keepParent "$(basename ${{ steps.find-app.outputs.app_path }})" CodeSwitch-v${{ steps.version.outputs.VERSION }}-macos-${{ matrix.arch }}.zip - name: Upload artifact uses: actions/upload-artifact@v4 with: name: macos-${{ matrix.arch }} - path: bin/codeswitch-macos-${{ matrix.arch }}.zip + path: bin/CodeSwitch-v${{ steps.version.outputs.VERSION }}-macos-${{ matrix.arch }}.zip build-windows: name: Build Windows @@ -79,9 +81,11 @@ jobs: - uses: actions/checkout@v4 - name: Update version from tag + id: version shell: pwsh run: | $VERSION = "${{ github.ref_name }}".TrimStart('v') + "VERSION=$VERSION" | Out-File -FilePath $env:GITHUB_OUTPUT -Append Write-Host "Updating version to: $VERSION" # Update version_service.go @@ -161,11 +165,30 @@ jobs: go build -ldflags="-w -s -H windowsgui" -o bin/updater.exe ./cmd/updater Remove-Item cmd/updater/*.syso -ErrorAction SilentlyContinue + - name: Rename files with version + shell: pwsh + run: | + $VERSION = "${{ steps.version.outputs.VERSION }}" + Push-Location bin + Rename-Item "CodeSwitch.exe" "CodeSwitch-v$VERSION.exe" + Rename-Item "updater.exe" "updater-v$VERSION.exe" + Pop-Location + # Rename installer (generated by NSIS) + if (Test-Path "build/windows/nsis/CodeSwitch-amd64-installer.exe") { + Move-Item "build/windows/nsis/CodeSwitch-amd64-installer.exe" "bin/CodeSwitch-v$VERSION-amd64-installer.exe" + } elseif (Test-Path "bin/CodeSwitch-amd64-installer.exe") { + Rename-Item "bin/CodeSwitch-amd64-installer.exe" "CodeSwitch-v$VERSION-amd64-installer.exe" + } + Write-Host "Files renamed:" + Get-ChildItem bin + - name: Generate SHA256 Checksums + shell: pwsh run: | + $VERSION = "${{ steps.version.outputs.VERSION }}" Push-Location bin - Get-FileHash -Algorithm SHA256 CodeSwitch.exe | ForEach-Object { "$($_.Hash.ToLower()) CodeSwitch.exe" } | Out-File -Encoding ascii CodeSwitch.exe.sha256 - Get-FileHash -Algorithm SHA256 updater.exe | ForEach-Object { "$($_.Hash.ToLower()) updater.exe" } | Out-File -Encoding ascii updater.exe.sha256 + Get-FileHash -Algorithm SHA256 "CodeSwitch-v$VERSION.exe" | ForEach-Object { "$($_.Hash.ToLower()) CodeSwitch-v$VERSION.exe" } | Out-File -Encoding ascii "CodeSwitch-v$VERSION.exe.sha256" + Get-FileHash -Algorithm SHA256 "updater-v$VERSION.exe" | ForEach-Object { "$($_.Hash.ToLower()) updater-v$VERSION.exe" } | Out-File -Encoding ascii "updater-v$VERSION.exe.sha256" Write-Host "SHA256 checksums generated:" Get-Content *.sha256 Pop-Location @@ -175,11 +198,11 @@ jobs: with: name: windows-amd64 path: | - bin/CodeSwitch-amd64-installer.exe - bin/CodeSwitch.exe - bin/CodeSwitch.exe.sha256 - bin/updater.exe - bin/updater.exe.sha256 + bin/CodeSwitch-v${{ steps.version.outputs.VERSION }}-amd64-installer.exe + bin/CodeSwitch-v${{ steps.version.outputs.VERSION }}.exe + bin/CodeSwitch-v${{ steps.version.outputs.VERSION }}.exe.sha256 + bin/updater-v${{ steps.version.outputs.VERSION }}.exe + bin/updater-v${{ steps.version.outputs.VERSION }}.exe.sha256 build-linux: name: Build Linux @@ -188,8 +211,10 @@ jobs: - uses: actions/checkout@v4 - name: Update version from tag + id: version run: | VERSION=${GITHUB_REF#refs/tags/v} + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT echo "Updating version to: $VERSION" sed -i "s/const AppVersion = \"v[^\"]*\"/const AppVersion = \"v$VERSION\"/" version_service.go echo "Updated version_service.go:" @@ -237,18 +262,19 @@ jobs: - name: Rename AppImage run: | + VERSION=${{ steps.version.outputs.VERSION }} cd bin echo "Files in bin/:" ls -la # linuxdeploy creates AppImage with lowercase name and arch suffix for f in *-x86_64.AppImage *-aarch64.AppImage; do if [ -f "$f" ]; then - mv "$f" CodeSwitch.AppImage - echo "Renamed $f -> CodeSwitch.AppImage" + mv "$f" "CodeSwitch-v${VERSION}.AppImage" + echo "Renamed $f -> CodeSwitch-v${VERSION}.AppImage" break fi done - ls -la CodeSwitch.AppImage + ls -la "CodeSwitch-v${VERSION}.AppImage" - name: Set nfpm script permissions run: | @@ -269,8 +295,9 @@ jobs: - name: Generate SHA256 Checksums run: | + VERSION=${{ steps.version.outputs.VERSION }} cd bin - sha256sum CodeSwitch.AppImage > CodeSwitch.AppImage.sha256 + sha256sum "CodeSwitch-v${VERSION}.AppImage" > "CodeSwitch-v${VERSION}.AppImage.sha256" for f in codeswitch_*.deb; do [ -f "$f" ] && sha256sum "$f" > "${f}.sha256"; done for f in codeswitch-*.rpm; do [ -f "$f" ] && sha256sum "$f" > "${f}.sha256"; done echo "SHA256 checksums:" @@ -281,8 +308,8 @@ jobs: with: name: linux-amd64 path: | - bin/CodeSwitch.AppImage - bin/CodeSwitch.AppImage.sha256 + bin/CodeSwitch-v${{ steps.version.outputs.VERSION }}.AppImage + bin/CodeSwitch-v${{ steps.version.outputs.VERSION }}.AppImage.sha256 bin/codeswitch_*.deb bin/codeswitch_*.deb.sha256 bin/codeswitch-*.rpm @@ -305,22 +332,23 @@ jobs: - name: Prepare release assets run: | + VERSION=${GITHUB_REF#refs/tags/v} mkdir -p release-assets # macOS - cp artifacts/macos-arm64/codeswitch-macos-arm64.zip release-assets/ - cp artifacts/macos-amd64/codeswitch-macos-amd64.zip release-assets/ + cp artifacts/macos-arm64/CodeSwitch-v${VERSION}-macos-arm64.zip release-assets/ + cp artifacts/macos-amd64/CodeSwitch-v${VERSION}-macos-amd64.zip release-assets/ # Windows - cp artifacts/windows-amd64/CodeSwitch-amd64-installer.exe release-assets/ - cp artifacts/windows-amd64/CodeSwitch.exe release-assets/ - cp artifacts/windows-amd64/CodeSwitch.exe.sha256 release-assets/ - cp artifacts/windows-amd64/updater.exe release-assets/ - cp artifacts/windows-amd64/updater.exe.sha256 release-assets/ + cp artifacts/windows-amd64/CodeSwitch-v${VERSION}-amd64-installer.exe release-assets/ + cp artifacts/windows-amd64/CodeSwitch-v${VERSION}.exe release-assets/ + cp artifacts/windows-amd64/CodeSwitch-v${VERSION}.exe.sha256 release-assets/ + cp artifacts/windows-amd64/updater-v${VERSION}.exe release-assets/ + cp artifacts/windows-amd64/updater-v${VERSION}.exe.sha256 release-assets/ # Linux - cp artifacts/linux-amd64/CodeSwitch.AppImage release-assets/ - cp artifacts/linux-amd64/CodeSwitch.AppImage.sha256 release-assets/ + cp artifacts/linux-amd64/CodeSwitch-v${VERSION}.AppImage release-assets/ + cp artifacts/linux-amd64/CodeSwitch-v${VERSION}.AppImage.sha256 release-assets/ cp artifacts/linux-amd64/codeswitch_*.deb release-assets/ 2>/dev/null || true cp artifacts/linux-amd64/codeswitch_*.deb.sha256 release-assets/ 2>/dev/null || true cp artifacts/linux-amd64/codeswitch-*.rpm release-assets/ 2>/dev/null || true @@ -330,12 +358,13 @@ jobs: - name: Generate latest.json run: | VERSION=${GITHUB_REF#refs/tags/} + VERSION_NUM=${GITHUB_REF#refs/tags/v} REPO="${{ github.repository }}" BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}" # Read SHA256 checksums from existing .sha256 files - WIN_SHA=$(cut -d' ' -f1 release-assets/CodeSwitch.exe.sha256) - LINUX_SHA=$(cut -d' ' -f1 release-assets/CodeSwitch.AppImage.sha256) + WIN_SHA=$(cut -d' ' -f1 release-assets/CodeSwitch-${VERSION}.exe.sha256) + LINUX_SHA=$(cut -d' ' -f1 release-assets/CodeSwitch-${VERSION}.AppImage.sha256) cat > release-assets/latest.json << EOF { @@ -343,21 +372,21 @@ jobs: "release_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "files": { "windows": { - "name": "CodeSwitch.exe", - "url": "${BASE_URL}/CodeSwitch.exe", + "name": "CodeSwitch-${VERSION}.exe", + "url": "${BASE_URL}/CodeSwitch-${VERSION}.exe", "sha256": "${WIN_SHA}" }, "darwin-arm64": { - "name": "codeswitch-macos-arm64.zip", - "url": "${BASE_URL}/codeswitch-macos-arm64.zip" + "name": "CodeSwitch-${VERSION}-macos-arm64.zip", + "url": "${BASE_URL}/CodeSwitch-${VERSION}-macos-arm64.zip" }, "darwin-amd64": { - "name": "codeswitch-macos-amd64.zip", - "url": "${BASE_URL}/codeswitch-macos-amd64.zip" + "name": "CodeSwitch-${VERSION}-macos-amd64.zip", + "url": "${BASE_URL}/CodeSwitch-${VERSION}-macos-amd64.zip" }, "linux": { - "name": "CodeSwitch.AppImage", - "url": "${BASE_URL}/CodeSwitch.AppImage", + "name": "CodeSwitch-${VERSION}.AppImage", + "url": "${BASE_URL}/CodeSwitch-${VERSION}.AppImage", "sha256": "${LINUX_SHA}" } } @@ -386,7 +415,7 @@ jobs: # 组合完整的 release body cat /tmp/version_notes.md > /tmp/release_body.md - cat >> /tmp/release_body.md << 'EOF' + cat >> /tmp/release_body.md << EOF --- @@ -394,46 +423,46 @@ jobs: | 平台 | 文件 | 说明 | |------|------|------| - | **Windows (首次)** | `CodeSwitch-amd64-installer.exe` | NSIS 安装器 | - | **Windows (便携)** | `CodeSwitch.exe` | 直接运行 | - | **Windows (更新)** | `updater.exe` | 静默更新辅助 | - | **macOS (ARM)** | `codeswitch-macos-arm64.zip` | Apple Silicon | - | **macOS (Intel)** | `codeswitch-macos-amd64.zip` | Intel 芯片 | - | **Linux (通用)** | `CodeSwitch.AppImage` | 跨发行版便携 | - | **Linux (Debian/Ubuntu)** | `codeswitch_*.deb` | apt 安装 | - | **Linux (RHEL/Fedora)** | `codeswitch-*.rpm` | dnf/yum 安装 | + | **Windows (首次)** | \`CodeSwitch-v${VERSION}-amd64-installer.exe\` | NSIS 安装器 | + | **Windows (便携)** | \`CodeSwitch-v${VERSION}.exe\` | 直接运行 | + | **Windows (更新)** | \`updater-v${VERSION}.exe\` | 静默更新辅助 | + | **macOS (ARM)** | \`CodeSwitch-v${VERSION}-macos-arm64.zip\` | Apple Silicon | + | **macOS (Intel)** | \`CodeSwitch-v${VERSION}-macos-amd64.zip\` | Intel 芯片 | + | **Linux (通用)** | \`CodeSwitch-v${VERSION}.AppImage\` | 跨发行版便携 | + | **Linux (Debian/Ubuntu)** | \`codeswitch_*.deb\` | apt 安装 | + | **Linux (RHEL/Fedora)** | \`codeswitch-*.rpm\` | dnf/yum 安装 | ## Linux 安装 ### AppImage (推荐) - ```bash - chmod +x CodeSwitch.AppImage - ./CodeSwitch.AppImage - ``` - 如遇 FUSE 问题:`./CodeSwitch.AppImage --appimage-extract-and-run` + \`\`\`bash + chmod +x CodeSwitch-v${VERSION}.AppImage + ./CodeSwitch-v${VERSION}.AppImage + \`\`\` + 如遇 FUSE 问题:\`./CodeSwitch-v${VERSION}.AppImage --appimage-extract-and-run\` ### Debian/Ubuntu - ```bash + \`\`\`bash sudo dpkg -i codeswitch_*.deb sudo apt-get install -f # 安装依赖 - ``` + \`\`\` ### RHEL/Fedora - ```bash + \`\`\`bash sudo rpm -i codeswitch-*.rpm # 或 sudo dnf install codeswitch-*.rpm - ``` + \`\`\` ## 文件校验 - 所有平台均提供 SHA256 校验文件(`.sha256`),下载后可验证完整性: - ```bash + 所有平台均提供 SHA256 校验文件(\`.sha256\`),下载后可验证完整性: + \`\`\`bash # Linux/macOS - sha256sum -c CodeSwitch.AppImage.sha256 + sha256sum -c CodeSwitch-v${VERSION}.AppImage.sha256 # Windows PowerShell - Get-FileHash CodeSwitch.exe | Format-List - ``` + Get-FileHash CodeSwitch-v${VERSION}.exe | Format-List + \`\`\` EOF echo "Generated release body:" diff --git a/.gitignore b/.gitignore index 3444dd2..040cb4d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ frontend/dist .task bin CLAUDE.md +.ace-tool/ diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f2be55a..8542662 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,14 @@ +# Code Switch v2.6.14 + +## 新功能 +- **自适应热力图**:新增供应商使用热力图功能,直观展示各供应商的请求分布情况,支持按时间范围筛选 +- **图标搜索**:新增供应商图标搜索功能,方便快速查找和选择合适的图标 + +## 修复 +- 修复控制台日志递归爆炸问题 + +--- + # Code Switch v2.0.0 ## 新功能 diff --git a/frontend/src/components/General/Index.vue b/frontend/src/components/General/Index.vue index 98deea3..a2a11de 100644 --- a/frontend/src/components/General/Index.vue +++ b/frontend/src/components/General/Index.vue @@ -22,6 +22,12 @@ const getCachedValue = (key: string, defaultValue: boolean): boolean => { const cached = localStorage.getItem(`app-settings-${key}`) return cached !== null ? cached === 'true' : defaultValue } +const getCachedNumber = (key: string, defaultValue: number): number => { + const cached = localStorage.getItem(`app-settings-${key}`) + if (cached === null) return defaultValue + const parsed = Number(cached) + return Number.isFinite(parsed) ? parsed : defaultValue +} const heatmapEnabled = ref(getCachedValue('heatmap', true)) const homeTitleVisible = ref(getCachedValue('homeTitle', true)) const autoStartEnabled = ref(getCachedValue('autoStart', false)) @@ -29,6 +35,7 @@ const autoUpdateEnabled = ref(getCachedValue('autoUpdate', true)) const autoConnectivityTestEnabled = ref(getCachedValue('autoConnectivityTest', false)) const switchNotifyEnabled = ref(getCachedValue('switchNotify', true)) // 切换通知开关 const roundRobinEnabled = ref(getCachedValue('roundRobin', false)) // 同 Level 轮询开关 +const budgetTotal = ref(getCachedNumber('budgetTotal', 0)) const settingsLoading = ref(true) const saveBusy = ref(false) @@ -62,6 +69,7 @@ const loadAppSettings = async () => { const data = await fetchAppSettings() heatmapEnabled.value = data?.show_heatmap ?? true homeTitleVisible.value = data?.show_home_title ?? true + budgetTotal.value = Number(data?.budget_total ?? 0) autoStartEnabled.value = data?.auto_start ?? false autoUpdateEnabled.value = data?.auto_update ?? true autoConnectivityTestEnabled.value = data?.auto_connectivity_test ?? false @@ -71,6 +79,7 @@ const loadAppSettings = async () => { // 缓存到 localStorage,下次打开时直接显示正确状态 localStorage.setItem('app-settings-heatmap', String(heatmapEnabled.value)) localStorage.setItem('app-settings-homeTitle', String(homeTitleVisible.value)) + localStorage.setItem('app-settings-budgetTotal', String(budgetTotal.value)) localStorage.setItem('app-settings-autoStart', String(autoStartEnabled.value)) localStorage.setItem('app-settings-autoUpdate', String(autoUpdateEnabled.value)) localStorage.setItem('app-settings-autoConnectivityTest', String(autoConnectivityTestEnabled.value)) @@ -80,6 +89,7 @@ const loadAppSettings = async () => { console.error('failed to load app settings', error) heatmapEnabled.value = true homeTitleVisible.value = true + budgetTotal.value = 0 autoStartEnabled.value = false autoUpdateEnabled.value = true autoConnectivityTestEnabled.value = false @@ -94,9 +104,12 @@ const persistAppSettings = async () => { if (settingsLoading.value || saveBusy.value) return saveBusy.value = true try { + const normalizedBudgetTotal = Number.isFinite(budgetTotal.value) ? Math.max(0, budgetTotal.value) : 0 + budgetTotal.value = normalizedBudgetTotal const payload: AppSettings = { show_heatmap: heatmapEnabled.value, show_home_title: homeTitleVisible.value, + budget_total: normalizedBudgetTotal, auto_start: autoStartEnabled.value, auto_update: autoUpdateEnabled.value, auto_connectivity_test: autoConnectivityTestEnabled.value, @@ -117,6 +130,7 @@ const persistAppSettings = async () => { // 更新缓存 localStorage.setItem('app-settings-heatmap', String(heatmapEnabled.value)) localStorage.setItem('app-settings-homeTitle', String(homeTitleVisible.value)) + localStorage.setItem('app-settings-budgetTotal', String(budgetTotal.value)) localStorage.setItem('app-settings-autoStart', String(autoStartEnabled.value)) localStorage.setItem('app-settings-autoUpdate', String(autoUpdateEnabled.value)) localStorage.setItem('app-settings-autoConnectivityTest', String(autoConnectivityTestEnabled.value)) @@ -416,6 +430,24 @@ onMounted(async () => { + +
+
+ + USD +
+ {{ $t('components.general.label.budgetTotalHint') }} +
+