fix(projects): open draft after adding project

This commit is contained in:
Bohdan Triapitsyn
2026-08-10 16:10:03 +03:00
parent bd4e7668fb
commit f9595cb80b
7 changed files with 94 additions and 10 deletions
@@ -11,7 +11,9 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useDirectoryStore } from '@/stores/useDirectoryStore';
import { useProjectsStore } from '@/stores/useProjectsStore';
import { useUIStore } from '@/stores/useUIStore';
import { useGitIdentitiesStore } from '@/stores/useGitIdentitiesStore';
import { useSessionUIStore } from '@/sync/session-ui-store';
import { useFileSystemAccess } from '@/hooks/useFileSystemAccess';
import { cn } from '@/lib/utils';
import { toast } from '@/components/ui';
@@ -146,6 +148,9 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
const homeDirectory = useDirectoryStore((s) => s.homeDirectory);
const projects = useProjectsStore((s) => s.projects);
const addProject = useProjectsStore((s) => s.addProject);
const setActiveMainTab = useUIStore((s) => s.setActiveMainTab);
const setSessionSwitcherOpen = useUIStore((s) => s.setSessionSwitcherOpen);
const openNewSessionDraft = useSessionUIStore((s) => s.openNewSessionDraft);
const gitIdentityProfiles = useGitIdentitiesStore((s) => s.profiles);
const globalGitIdentity = useGitIdentitiesStore((s) => s.globalIdentity);
const defaultGitIdentityId = useGitIdentitiesStore((s) => s.defaultGitIdentityId);
@@ -405,17 +410,26 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
onOpenChange(false);
}, [onOpenChange]);
const openProjectDraft = React.useCallback((projectId: string, projectPath: string) => {
setActiveMainTab('chat');
if (isMobile) setSessionSwitcherOpen(false);
openNewSessionDraft({ selectedProjectId: projectId, directoryOverride: projectPath });
handleClose();
}, [handleClose, isMobile, openNewSessionDraft, setActiveMainTab, setSessionSwitcherOpen]);
const handleQuickAdd = React.useCallback((event: React.MouseEvent, path: string) => {
event.stopPropagation();
const normalized = normalizeDirectoryPath(path);
if (normalized && addedProjectPaths.has(normalized)) return;
const added = addProject(path);
if (!added) {
const project = addProject(path);
if (!project) {
toast.error(t('directoryExplorerDialog.toast.failedToAddProject'), {
description: t('directoryExplorerDialog.toast.selectValidDirectoryPath'),
});
return;
}
}, [addProject, addedProjectPaths, t]);
openProjectDraft(project.id, project.path);
}, [addProject, addedProjectPaths, openProjectDraft, t]);
const finalizeSelection = React.useCallback(async (target: string) => {
if (!target || isConfirming) return;
@@ -439,16 +453,16 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
});
selectedTarget = result.path;
} else if (shouldCreateSelection) {
await opencodeClient.createDirectory(target);
await opencodeClient.createDirectory(target, { asProject: true });
}
const added = addProject(selectedTarget);
if (!added) {
const project = addProject(selectedTarget);
if (!project) {
toast.error(t('directoryExplorerDialog.toast.failedToAddProject'), {
description: t('directoryExplorerDialog.toast.selectValidDirectoryPath'),
});
return;
}
handleClose();
openProjectDraft(project.id, project.path);
} catch (error) {
toast.error(t('directoryExplorerDialog.toast.failedToSelectDirectory'), {
description: error instanceof Error ? error.message : t('directoryExplorerDialog.toast.unknownError'),
@@ -456,7 +470,7 @@ export const DirectoryExplorerDialog: React.FC<DirectoryExplorerDialogProps> = (
} finally {
setIsConfirming(false);
}
}, [addProject, addedProjectPaths, cloneRemoteUrl, handleClose, isCloneMode, isConfirming, selectedGitIdentity?.id, shouldCreateTarget, targetPath, t]);
}, [addProject, addedProjectPaths, cloneRemoteUrl, isCloneMode, isConfirming, openProjectDraft, selectedGitIdentity?.id, shouldCreateTarget, targetPath, t]);
const browseToDisplayPath = React.useCallback((displayPath: string) => {
setQuery(ensureBrowseDirectoryPath(displayPath));
@@ -27,6 +27,7 @@
### Components
- `SidebarHeader.tsx`: Top header UI for add-project, session search, selection mode, project sort, and the display menu (recent toggle, collapse/expand all).
- A successful add/create/clone from the project-directory dialog transitions to a new-session draft targeted at that project, matching the project's `+` action; changing project metadata alone must not leave the visible session or draft on a different directory.
- `SidebarNav.tsx`: Text navigation rows above the tree (New session, Scheduled, Multi-run, Archive); hidden in VS Code.
- `SidebarActivitySections.tsx`: Global top section renderer; currently used for the `recent` section only, styled as a zone header.
- `SidebarFooter.tsx`: Static footer with icon-only settings, shortcuts, and about actions.
+19 -1
View File
@@ -1701,7 +1701,7 @@ class OpencodeService {
// File System Operations
async createDirectory(
dirPath: string,
options?: { allowOutsideWorkspace?: boolean }
options?: { allowOutsideWorkspace?: boolean; asProject?: boolean }
): Promise<{ success: boolean; path: string }> {
const desktopFiles = getDesktopFilesApi();
if (desktopFiles?.createDirectory) {
@@ -1713,6 +1713,24 @@ class OpencodeService {
}
}
if (options?.asProject) {
const response = await runtimeFetch(`${this.baseUrl}/opencode/directory`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ path: dirPath, create: true }),
});
if (!response.ok) {
const error = await response.json().catch(() => ({ error: 'Failed to create project directory' }));
throw new Error(error.error || 'Failed to create project directory');
}
const result = await response.json();
return { success: true, path: result.path };
}
const payload = {
path: dirPath,
...(options?.allowOutsideWorkspace ? { allowOutsideWorkspace: true } : {}),