From 592ace93d0d94b9e3b84e3886b18071d0b65f646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Fri, 8 May 2026 12:41:34 +0800 Subject: [PATCH] fix: avoid treating normal folders as repository warnings Non-Git folders are valid launch targets for desktop sessions, so the composer should not render them as an inline repository warning. The branch and worktree controls remain hidden outside Git repositories while server-side repository validation still protects actual branch-launch requests. Constraint: Non-Git directories must remain usable as ordinary session workdirs. Rejected: Keep the inline not-Git warning | it reads like an error even though the user can continue normally. Confidence: high Scope-risk: narrow Directive: Keep dirty-worktree and checked-out-branch warnings visible because those still require a launch-mode decision. Tested: cd desktop && bun run test -- --run src/pages/EmptySession.test.tsx src/components/chat/ChatInput.test.tsx Tested: bun run check:desktop Tested: git diff --check -- desktop/src/components/shared/RepositoryLaunchControls.tsx desktop/src/pages/EmptySession.test.tsx desktop/src/i18n/locales/en.ts desktop/src/i18n/locales/zh.ts Not-tested: Native Tauri packaged window visual check --- .../shared/RepositoryLaunchControls.tsx | 6 +-- desktop/src/i18n/locales/en.ts | 1 - desktop/src/i18n/locales/zh.ts | 1 - desktop/src/pages/EmptySession.test.tsx | 41 +++++++++++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/desktop/src/components/shared/RepositoryLaunchControls.tsx b/desktop/src/components/shared/RepositoryLaunchControls.tsx index d2da78af..7e40fde3 100644 --- a/desktop/src/components/shared/RepositoryLaunchControls.tsx +++ b/desktop/src/components/shared/RepositoryLaunchControls.tsx @@ -37,7 +37,7 @@ const VIEWPORT_GUTTER = 12 function stateMessage(context: RepositoryContextResult | null, error: string | null) { if (error) return error if (!context) return null - if (context.state === 'not_git_repo') return 'not_git' + if (context.state === 'not_git_repo') return null if (context.state === 'missing_workdir') return 'missing' if (context.state === 'error') return context.error || 'error' return null @@ -370,9 +370,7 @@ export function RepositoryLaunchControls({
- {message === 'not_git' - ? t('repoLaunch.notGit') - : message === 'missing' + {message === 'missing' ? t('repoLaunch.missingWorkdir') : message} diff --git a/desktop/src/i18n/locales/en.ts b/desktop/src/i18n/locales/en.ts index f85b59eb..4dc942b2 100644 --- a/desktop/src/i18n/locales/en.ts +++ b/desktop/src/i18n/locales/en.ts @@ -706,7 +706,6 @@ export const en = { 'repoLaunch.worktreeCurrent': 'Current worktree', 'repoLaunch.worktreeIsolated': 'Isolated worktree', 'repoLaunch.selectWorktree': 'Select worktree mode', - 'repoLaunch.notGit': 'Current project is not a Git repository.', 'repoLaunch.missingWorkdir': 'Working directory is missing.', 'repoLaunch.dirtyWarning': 'Uncommitted changes detected. Direct switching will be blocked; enable isolated worktree to continue without touching this folder.', 'repoLaunch.checkedOutWarning': 'This branch is already checked out elsewhere. Enable isolated worktree or choose another branch.', diff --git a/desktop/src/i18n/locales/zh.ts b/desktop/src/i18n/locales/zh.ts index 154b64a4..0c8e205b 100644 --- a/desktop/src/i18n/locales/zh.ts +++ b/desktop/src/i18n/locales/zh.ts @@ -708,7 +708,6 @@ export const zh: Record = { 'repoLaunch.worktreeCurrent': '当前工作树', 'repoLaunch.worktreeIsolated': '独立工作树', 'repoLaunch.selectWorktree': '选择工作树模式', - 'repoLaunch.notGit': '当前项目不是 Git 仓库。', 'repoLaunch.missingWorkdir': '工作目录不存在。', 'repoLaunch.dirtyWarning': '检测到未提交变更,直接切换会被阻止。开启独立工作树即可继续,且不会改动当前目录。', 'repoLaunch.checkedOutWarning': '这个分支已在其他工作树中检出。请开启独立工作树或选择其他分支。', diff --git a/desktop/src/pages/EmptySession.test.tsx b/desktop/src/pages/EmptySession.test.tsx index 52fdcf34..780da8b5 100644 --- a/desktop/src/pages/EmptySession.test.tsx +++ b/desktop/src/pages/EmptySession.test.tsx @@ -103,6 +103,20 @@ function okRepositoryContext(overrides: Partial = {}): } } +function notGitRepositoryContext(): RepositoryContextResult { + return { + state: 'not_git_repo', + workDir: '/workspace/project', + repoRoot: null, + repoName: null, + currentBranch: null, + defaultBranch: null, + dirty: false, + branches: [], + worktrees: [], + } +} + describe('EmptySession', () => { const initialSessionState = useSessionStore.getInitialState() const initialChatState = useChatStore.getInitialState() @@ -199,6 +213,33 @@ describe('EmptySession', () => { expect(mocks.wsConnect).toHaveBeenCalledWith('draft-session') }) + it('starts in a selected non-Git project without showing a repository warning', async () => { + mocks.getRepositoryContext.mockResolvedValueOnce(notGitRepositoryContext()) + + render() + + fireEvent.change(screen.getByRole('textbox'), { + target: { value: 'draft question', selectionStart: 14 }, + }) + fireEvent.click(screen.getByRole('button', { name: 'Pick project' })) + + await waitFor(() => { + expect(screen.getByRole('button', { name: /Run/i })).not.toBeDisabled() + }) + + expect(screen.queryByText('Current project is not a Git repository.')).not.toBeInTheDocument() + expect(screen.queryByRole('button', { name: /Select branch:/ })).not.toBeInTheDocument() + expect(screen.queryByText('Current worktree')).not.toBeInTheDocument() + + fireEvent.click(screen.getByRole('button', { name: /Run/i })) + + await waitFor(() => { + expect(mocks.createSession).toHaveBeenCalledWith({ + workDir: '/workspace/project', + }) + }) + }) + it('shows an actionable repository error when direct branch switching is blocked', async () => { mocks.createSession.mockRejectedValueOnce(new ApiError(400, { error: 'REPOSITORY_DIRTY_WORKTREE',