mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-27 15:13:37 +08:00
feat(desktop): default workspace panel to 'all' view (#23)
R5 changes the workspace panel's default activeView from 'changed' to
'all' and removes the loadStatus auto-switching logic. Users now see
the all-files tree on first open, regardless of whether the session
has changed files. Explicit setActiveView still wins and survives
subsequent loadStatus calls via hasUserSelectedView.
The downstream WorkspacePanel component tests that asserted the old
"auto-switch to changed view" behavior are updated:
- 4 tests now explicitly setActiveView('changed') when they need to
exercise the changed-files list.
- 2 tests are rewritten/renamed (R5 prefix) to assert the new
"default to all, never auto-flip" contract instead of the old
switch-back behavior.
Tested:
- bun run lint (tsc --noEmit clean)
- bun run test workspacePanelStore.test.ts (24/24 passed)
- bun run test WorkspacePanel.test.tsx (27/27 passed)
- bun run build (clean, 1.21s, no bundle impact)
Note: 3 pre-existing failures in electron/services/{sidecarManager,terminal}.test.ts
exist on origin/main baseline and are unrelated to this change.
Co-authored-by: 你的姓名 <you@example.com>
This commit is contained in:
parent
e6332810ed
commit
95931d498d
@ -329,6 +329,7 @@ describe('WorkspacePanel', () => {
|
||||
|
||||
await act(() => {
|
||||
useWorkspacePanelStore.getState().openPanel('session-changed')
|
||||
useWorkspacePanelStore.getState().setActiveView('session-changed', 'changed')
|
||||
})
|
||||
|
||||
const view = await renderPanel('session-changed')
|
||||
@ -388,7 +389,7 @@ describe('WorkspacePanel', () => {
|
||||
expect(view.getAllByText('Diff').length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it('refreshes status on open and switches back to changed files when new changes exist', async () => {
|
||||
it('R5: refreshes status on open without overriding the all-files view when new changes exist', async () => {
|
||||
getMocks().getWorkspaceStatusMock.mockResolvedValue({
|
||||
state: 'ok',
|
||||
workDir: '/repo',
|
||||
@ -437,10 +438,14 @@ describe('WorkspacePanel', () => {
|
||||
await waitFor(() => {
|
||||
expect(getMocks().getWorkspaceStatusMock).toHaveBeenCalledWith('session-stale-all')
|
||||
})
|
||||
// R5: even when refreshed status reports new changed files, the active view stays on 'all'.
|
||||
await waitFor(() => {
|
||||
expect(view.getByRole('button', { name: 'Changed files' })).toBeTruthy()
|
||||
expect(useWorkspacePanelStore.getState().statusBySession['session-stale-all']?.changedFiles).toEqual([
|
||||
expect.objectContaining({ path: 'src/Fresh.ts' }),
|
||||
])
|
||||
})
|
||||
expect(view.getByText('src/Fresh.ts')).toBeTruthy()
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-stale-all')).toBe('all')
|
||||
expect(view.getByRole('button', { name: 'All files' })).toBeTruthy()
|
||||
})
|
||||
|
||||
it('loads workspace status when opened while the chat is running', async () => {
|
||||
@ -470,6 +475,7 @@ describe('WorkspacePanel', () => {
|
||||
|
||||
await act(() => {
|
||||
useWorkspacePanelStore.getState().openPanel('session-running-open')
|
||||
useWorkspacePanelStore.getState().setActiveView('session-running-open', 'changed')
|
||||
})
|
||||
|
||||
const view = await renderPanel('session-running-open')
|
||||
@ -507,6 +513,7 @@ describe('WorkspacePanel', () => {
|
||||
|
||||
await act(() => {
|
||||
useWorkspacePanelStore.getState().openPanel('session-non-git')
|
||||
useWorkspacePanelStore.getState().setActiveView('session-non-git', 'changed')
|
||||
})
|
||||
|
||||
const view = await renderPanel('session-non-git')
|
||||
@ -526,7 +533,7 @@ describe('WorkspacePanel', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('opens to all files when the current turn has no changed files', async () => {
|
||||
it('R5: opens to all files by default regardless of changed-file count', async () => {
|
||||
const statusRequest = deferred<{
|
||||
state: 'ok'
|
||||
workDir: string
|
||||
@ -549,7 +556,8 @@ describe('WorkspacePanel', () => {
|
||||
})
|
||||
|
||||
const view = await renderPanel('session-empty-tree')
|
||||
expect(view.getByRole('button', { name: 'Changed files' })).toBeTruthy()
|
||||
// R5: default view is 'all' — the toggle button should reflect that immediately.
|
||||
expect(view.getByRole('button', { name: 'All files' })).toBeTruthy()
|
||||
|
||||
await act(async () => {
|
||||
statusRequest.resolve({
|
||||
@ -625,10 +633,8 @@ describe('WorkspacePanel', () => {
|
||||
|
||||
const view = await renderPanel('session-tree')
|
||||
|
||||
expect(view.getByRole('button', { name: 'Changed files' })).toBeTruthy()
|
||||
|
||||
await clickElement(view.getByRole('button', { name: 'Changed files' }))
|
||||
await clickElement(view.getByRole('menuitem', { name: 'All files' }))
|
||||
// R5: default view is 'all', so the all-files tree loads without a manual toggle.
|
||||
expect(view.getByRole('button', { name: 'All files' })).toBeTruthy()
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getMocks().getWorkspaceTreeMock).toHaveBeenCalledWith('session-tree', '')
|
||||
|
||||
@ -67,16 +67,16 @@ describe('workspacePanelStore', () => {
|
||||
const store = useWorkspacePanelStore.getState()
|
||||
|
||||
expect(store.isPanelOpen('session-a')).toBe(false)
|
||||
expect(store.getActiveView('session-a')).toBe('changed')
|
||||
expect(store.getActiveView('session-a')).toBe('all')
|
||||
expect(store.width).toBe(WORKSPACE_PANEL_DEFAULT_WIDTH)
|
||||
|
||||
store.openPanel('session-a')
|
||||
store.setActiveView('session-a', 'all')
|
||||
store.setActiveView('session-a', 'changed')
|
||||
|
||||
expect(useWorkspacePanelStore.getState().isPanelOpen('session-a')).toBe(true)
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-a')).toBe('all')
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-a')).toBe('changed')
|
||||
expect(useWorkspacePanelStore.getState().isPanelOpen('session-b')).toBe(false)
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-b')).toBe('changed')
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-b')).toBe('all')
|
||||
|
||||
store.togglePanel('session-b')
|
||||
expect(useWorkspacePanelStore.getState().isPanelOpen('session-b')).toBe(true)
|
||||
@ -84,7 +84,7 @@ describe('workspacePanelStore', () => {
|
||||
|
||||
store.closePanel('session-a')
|
||||
expect(useWorkspacePanelStore.getState().isPanelOpen('session-a')).toBe(false)
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-a')).toBe('all')
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-a')).toBe('changed')
|
||||
|
||||
store.setWidth(120)
|
||||
expect(useWorkspacePanelStore.getState().width).toBe(WORKSPACE_PANEL_MIN_WIDTH)
|
||||
@ -120,7 +120,11 @@ describe('workspacePanelStore', () => {
|
||||
expect(useWorkspacePanelStore.getState().errors.statusBySession['session-1']).toBeNull()
|
||||
})
|
||||
|
||||
it('defaults an empty changed-files status to the all-files view', async () => {
|
||||
it('R5: defaults activeView to "all" for a fresh session', () => {
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-fresh')).toBe('all')
|
||||
})
|
||||
|
||||
it('R5: loadStatus does not flip activeView when status is empty', async () => {
|
||||
mocks.getWorkspaceStatusMock.mockResolvedValue({
|
||||
state: 'ok',
|
||||
workDir: '/repo',
|
||||
@ -131,7 +135,7 @@ describe('workspacePanelStore', () => {
|
||||
})
|
||||
|
||||
useWorkspacePanelStore.getState().openPanel('session-empty-changes')
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-empty-changes')).toBe('changed')
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-empty-changes')).toBe('all')
|
||||
|
||||
await useWorkspacePanelStore.getState().loadStatus('session-empty-changes')
|
||||
|
||||
@ -139,7 +143,7 @@ describe('workspacePanelStore', () => {
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-empty-changes')).toBe('all')
|
||||
})
|
||||
|
||||
it('keeps the changed-files view when status contains changes', async () => {
|
||||
it('R5: loadStatus does not flip activeView when status has changes', async () => {
|
||||
mocks.getWorkspaceStatusMock.mockResolvedValue({
|
||||
state: 'ok',
|
||||
workDir: '/repo',
|
||||
@ -157,44 +161,31 @@ describe('workspacePanelStore', () => {
|
||||
})
|
||||
|
||||
useWorkspacePanelStore.getState().openPanel('session-has-changes')
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-has-changes')).toBe('all')
|
||||
|
||||
await useWorkspacePanelStore.getState().loadStatus('session-has-changes')
|
||||
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-has-changes')).toBe('changed')
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-has-changes')).toBe('all')
|
||||
})
|
||||
|
||||
it('returns to changed-files when a refreshed default all-files view now has changes', async () => {
|
||||
mocks.getWorkspaceStatusMock
|
||||
.mockResolvedValueOnce({
|
||||
state: 'ok',
|
||||
workDir: '/repo',
|
||||
repoName: 'repo',
|
||||
branch: 'main',
|
||||
isGitRepo: true,
|
||||
changedFiles: [],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
state: 'ok',
|
||||
workDir: '/repo',
|
||||
repoName: 'repo',
|
||||
branch: 'main',
|
||||
isGitRepo: true,
|
||||
changedFiles: [
|
||||
{
|
||||
path: 'src/app.ts',
|
||||
status: 'modified',
|
||||
additions: 2,
|
||||
deletions: 1,
|
||||
},
|
||||
],
|
||||
})
|
||||
it('R5: setActiveView marks hasUserSelectedView and is preserved across loadStatus', async () => {
|
||||
mocks.getWorkspaceStatusMock.mockResolvedValue({
|
||||
state: 'ok',
|
||||
workDir: '/repo',
|
||||
repoName: 'repo',
|
||||
branch: 'main',
|
||||
isGitRepo: true,
|
||||
changedFiles: [],
|
||||
})
|
||||
|
||||
useWorkspacePanelStore.getState().openPanel('session-refresh-changes')
|
||||
await useWorkspacePanelStore.getState().loadStatus('session-refresh-changes')
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-refresh-changes')).toBe('all')
|
||||
useWorkspacePanelStore.getState().openPanel('session-explicit')
|
||||
expect(useWorkspacePanelStore.getState().panelBySession['session-explicit']?.hasUserSelectedView).toBeFalsy()
|
||||
|
||||
await useWorkspacePanelStore.getState().loadStatus('session-refresh-changes')
|
||||
useWorkspacePanelStore.getState().setActiveView('session-explicit', 'changed')
|
||||
expect(useWorkspacePanelStore.getState().panelBySession['session-explicit']?.hasUserSelectedView).toBe(true)
|
||||
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-refresh-changes')).toBe('changed')
|
||||
await useWorkspacePanelStore.getState().loadStatus('session-explicit')
|
||||
expect(useWorkspacePanelStore.getState().getActiveView('session-explicit')).toBe('changed')
|
||||
})
|
||||
|
||||
it('does not override an explicit all-files selection when refreshed status has changes', async () => {
|
||||
|
||||
@ -87,7 +87,7 @@ type WorkspacePanelStore = {
|
||||
|
||||
const DEFAULT_PANEL_STATE: WorkspacePanelSessionState = {
|
||||
isOpen: false,
|
||||
activeView: 'changed',
|
||||
activeView: 'all',
|
||||
}
|
||||
|
||||
const DEFAULT_WORKBENCH_MODE: WorkbenchMode = 'workspace'
|
||||
@ -294,17 +294,12 @@ export const useWorkspacePanelStore = create<WorkspacePanelStore>((set, get) =>
|
||||
|
||||
set((state) => {
|
||||
const panel = getSessionPanelState(state.panelBySession, sessionId)
|
||||
const nextActiveView =
|
||||
!panel.hasUserSelectedView && result.state === 'ok'
|
||||
? result.changedFiles.length > 0 ? 'changed' : 'all'
|
||||
: panel.activeView
|
||||
|
||||
return {
|
||||
panelBySession: {
|
||||
...state.panelBySession,
|
||||
[sessionId]: {
|
||||
...panel,
|
||||
activeView: nextActiveView,
|
||||
},
|
||||
},
|
||||
statusBySession: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user