diff --git a/desktop/scripts/build-macos-arm64.sh b/desktop/scripts/build-macos-arm64.sh
index 407d7710..8c146770 100755
--- a/desktop/scripts/build-macos-arm64.sh
+++ b/desktop/scripts/build-macos-arm64.sh
@@ -23,6 +23,10 @@ Environment:
SKIP_INSTALL=1 Skip `bun install` in the repo root and desktop app.
SIGN_BUILD=1 Remove the default `--no-sign` flag and allow signed builds.
OPEN_OUTPUT=1 Open the canonical artifact output directory in Finder after a successful build.
+ PRESERVE_TAURI_TARGET=1
+ Keep Tauri/Rust target cache for a faster incremental build.
+ By default this script removes the macOS target cache so
+ packaged WebView assets cannot be silently reused.
Examples:
./desktop/scripts/build-macos-arm64.sh
@@ -62,27 +66,47 @@ if [[ "${SKIP_INSTALL:-0}" != "1" ]]; then
fi
# ── 清理 + 显式预热前端 / sidecar ────────────────────────────
-# 之前遇到过"改了 src/ 代码,build 的 .app 里 sidecar 还是旧的"的诡异
-# case —— 根因是 Bun.build / Tauri bundler 某一层的缓存把旧 sidecar
-# binary 复用进了新 .app(.app 被删干净了也救不回来,因为 binary 源在
-# desktop/src-tauri/binaries/)。
+# 之前遇到过两类"改了源码,build 出来的 .app 还是旧行为"的诡异 case:
+# 1) Bun.build / Tauri bundler 某一层缓存把旧 sidecar binary 复用进新 .app
+# 2) Tauri/Rust target 缓存复用旧 claude-code-desktop,导致新 dist 没被嵌进去
+# 第二类尤其隐蔽: dist 是新的,sidecar 也是新的,但 WebView 运行的还是旧前端。
#
-# 这里做三件事强制 fresh build:
-# 1) 硬删 sidecar 源 binary + Tauri bundle 目录 + 前端 dist
+# 默认做四件事强制 fresh build:
+# 1) 硬删 sidecar 源 binary + Tauri target/bundle 目录 + 前端 dist
# 2) 显式跑 bun run build + bun run build:sidecars
# 3) tauri build 用 --config 覆盖 beforeBuildCommand 为 true(no-op),
# 避免 sidecar 被重复编译浪费 ~10s
+# 4) 复制到 canonical output 前再次清空输出目录
# 任一步失败,整个脚本立即退出(set -e)。
-echo "[build-macos-arm64] Cleaning stale sidecar binaries and bundle output..."
+echo "[build-macos-arm64] Cleaning stale sidecar binaries, frontend output, and Tauri bundle cache..."
rm -rf "${DESKTOP_DIR}/src-tauri/binaries/claude-sidecar-"*
-rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/bundle"
-rm -rf "${DESKTOP_DIR}/src-tauri/target/release/bundle"
rm -rf "${DESKTOP_DIR}/dist"
rm -f "${DESKTOP_DIR}/tsconfig.tsbuildinfo"
-rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/build/claude-code-desktop-"*
-rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/.fingerprint/claude-code-desktop-"*
-rm -f "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/deps/claude_code_desktop-"*
-rm -f "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/deps/libclaude_code_desktop-"*
+
+if [[ "${PRESERVE_TAURI_TARGET:-0}" == "1" ]]; then
+ echo "[build-macos-arm64] PRESERVE_TAURI_TARGET=1: keeping Rust dependency cache, clearing app-specific artifacts only..."
+ rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/bundle"
+ rm -rf "${DESKTOP_DIR}/src-tauri/target/release/bundle"
+ rm -f "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/claude-code-desktop"
+ rm -f "${DESKTOP_DIR}/src-tauri/target/release/claude-code-desktop"
+ find "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/build" \
+ -maxdepth 1 -name 'claude-code-desktop-*' -exec rm -rf {} + 2>/dev/null || true
+ find "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/.fingerprint" \
+ -maxdepth 1 -name 'claude-code-desktop-*' -exec rm -rf {} + 2>/dev/null || true
+ find "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}/release/deps" \
+ -maxdepth 1 \( -name 'claude_code_desktop-*' -o -name 'libclaude_code_desktop-*' \) -exec rm -f {} + 2>/dev/null || true
+else
+ echo "[build-macos-arm64] Removing Tauri target cache for ${TARGET_TRIPLE} to force fresh embedded frontend assets..."
+ rm -rf "${DESKTOP_DIR}/src-tauri/target/${TARGET_TRIPLE}"
+ rm -rf "${DESKTOP_DIR}/src-tauri/target/release/bundle"
+ rm -f "${DESKTOP_DIR}/src-tauri/target/release/claude-code-desktop"
+ find "${DESKTOP_DIR}/src-tauri/target/release/build" \
+ -maxdepth 1 -name 'claude-code-desktop-*' -exec rm -rf {} + 2>/dev/null || true
+ find "${DESKTOP_DIR}/src-tauri/target/release/.fingerprint" \
+ -maxdepth 1 -name 'claude-code-desktop-*' -exec rm -rf {} + 2>/dev/null || true
+ find "${DESKTOP_DIR}/src-tauri/target/release/deps" \
+ -maxdepth 1 \( -name 'claude_code_desktop-*' -o -name 'libclaude_code_desktop-*' \) -exec rm -f {} + 2>/dev/null || true
+fi
echo "[build-macos-arm64] Rebuilding frontend (tsc + vite)..."
(cd "${DESKTOP_DIR}" && bun run build)
diff --git a/desktop/src/components/shared/DirectoryPicker.test.tsx b/desktop/src/components/shared/DirectoryPicker.test.tsx
index 231a1a1d..fa638391 100644
--- a/desktop/src/components/shared/DirectoryPicker.test.tsx
+++ b/desktop/src/components/shared/DirectoryPicker.test.tsx
@@ -1,4 +1,4 @@
-import { render, screen } from '@testing-library/react'
+import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import '@testing-library/jest-dom'
@@ -15,6 +15,7 @@ vi.mock('../../api/filesystem', () => ({
}))
import { DirectoryPicker } from './DirectoryPicker'
+import { sessionsApi } from '../../api/sessions'
describe('DirectoryPicker', () => {
it('uses the source repository name as the fallback label for desktop worktree paths', () => {
@@ -28,4 +29,32 @@ describe('DirectoryPicker', () => {
expect(screen.getByRole('button')).toHaveTextContent('checkout')
expect(screen.getByRole('button')).not.toHaveTextContent('desktop-feature-rail-12345678')
})
+
+ it('does not duplicate the branch in the selected project chip', async () => {
+ vi.mocked(sessionsApi.getRecentProjects).mockResolvedValue({
+ projects: [{
+ projectPath: '/workspace/project',
+ realPath: '/workspace/project',
+ projectName: 'project',
+ repoName: 'NanmiCoder/OpenCutSkill',
+ branch: 'main',
+ isGit: true,
+ modifiedAt: '2026-05-07T00:00:00.000Z',
+ sessionCount: 1,
+ }],
+ })
+
+ render(
+ ,
+ )
+
+ fireEvent.click(screen.getByRole('button'))
+
+ const trigger = await waitFor(() => screen.getAllByRole('button', { name: /NanmiCoder\/OpenCutSkill/ })[0])
+ expect(trigger).toHaveTextContent('NanmiCoder/OpenCutSkill')
+ expect(trigger).not.toHaveTextContent('main')
+ })
})
diff --git a/desktop/src/components/shared/DirectoryPicker.tsx b/desktop/src/components/shared/DirectoryPicker.tsx
index ae65e4eb..1b68bc81 100644
--- a/desktop/src/components/shared/DirectoryPicker.tsx
+++ b/desktop/src/components/shared/DirectoryPicker.tsx
@@ -164,15 +164,6 @@ export function DirectoryPicker({ value, onChange }: Props) {
{selectedProject?.repoName || selectedProject?.projectName || projectNameFromPath(value)}
- {selectedProject?.branch && (
- <>
-
- {selectedProject.branch}
- >
- )}
expand_more
) : (