refactor(settings): scope the settings project selector to settings
Picking a project in Settings called setActiveProject, which relocates the chat, the session list, the file tree and the Git surface. Reading another project's MCP servers or agents moved the user's whole app. It had to, because the configuration stores resolved the directory themselves from the active project and held one flat list. Each of them now takes an explicit directory — omitted still means the active project, so every caller outside Settings is unchanged — and keys loaded data by directory next to a flat mirror of the active project. Chat, autocompletes and pickers keep reading that mirror; a load for another directory writes only the map. A failed load restores that directory's previous list. Settings resolves its own directory through useSettingsDirectory, backed by a session-local settingsProjectPath that follows the active project until the user picks something else.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import type { ProjectEntry } from '@/lib/api/types';
|
||||
import { resolveSettingsDirectory } from './useSettingsDirectory';
|
||||
|
||||
const project = (id: string, path: string): ProjectEntry => ({ id, path } as ProjectEntry);
|
||||
|
||||
const projects = [
|
||||
project('a', '/workspace/alpha'),
|
||||
project('b', '/workspace/beta'),
|
||||
];
|
||||
|
||||
describe('resolveSettingsDirectory', () => {
|
||||
test('follows the active project until Settings picks one', () => {
|
||||
expect(resolveSettingsDirectory(null, projects, 'b')).toBe('/workspace/beta');
|
||||
});
|
||||
|
||||
test('keeps the Settings pick even when the app is on another project', () => {
|
||||
// The whole point: browsing another project's configuration must not depend
|
||||
// on moving the app to it.
|
||||
expect(resolveSettingsDirectory('/workspace/alpha', projects, 'b')).toBe('/workspace/alpha');
|
||||
});
|
||||
|
||||
test('falls back to the active project when the picked one is gone', () => {
|
||||
expect(resolveSettingsDirectory('/workspace/removed', projects, 'b')).toBe('/workspace/beta');
|
||||
});
|
||||
|
||||
test('resolves to nothing when there are no projects', () => {
|
||||
expect(resolveSettingsDirectory('/workspace/alpha', [], null)).toBe(null);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { ProjectEntry } from '@/lib/api/types';
|
||||
import { useProjectsStore } from '@/stores/useProjectsStore';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
|
||||
/**
|
||||
* Resolves which project the Settings pages describe.
|
||||
*
|
||||
* `settingsProjectPath` is the user's pick in the Settings project selector.
|
||||
* Until they make one — or when the project it names is gone — Settings follows
|
||||
* the app's active project, so nothing looks different before it is used.
|
||||
*/
|
||||
export const resolveSettingsDirectory = (
|
||||
settingsProjectPath: string | null,
|
||||
projects: ProjectEntry[],
|
||||
activeProjectId: string | null,
|
||||
): string | null => {
|
||||
if (settingsProjectPath && projects.some((project) => project.path === settingsProjectPath)) {
|
||||
return settingsProjectPath;
|
||||
}
|
||||
|
||||
const activeProject = projects.find((project) => project.id === activeProjectId) ?? projects[0];
|
||||
return activeProject?.path ?? null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Directory the Settings pages read and write configuration for.
|
||||
*
|
||||
* Settings has its own project selector. Picking a project there used to call
|
||||
* `setActiveProject`, which moves the whole app — chat, sessions, files, git —
|
||||
* so reading another project's MCP servers silently relocated the user.
|
||||
*/
|
||||
export const useSettingsDirectory = (): string | null => {
|
||||
const settingsProjectPath = useUIStore((state) => state.settingsProjectPath);
|
||||
const projects = useProjectsStore((state) => state.projects);
|
||||
const activeProjectId = useProjectsStore((state) => state.activeProjectId);
|
||||
|
||||
return resolveSettingsDirectory(settingsProjectPath, projects, activeProjectId);
|
||||
};
|
||||
Reference in New Issue
Block a user