feat: instant draft-first worktree creation and multi-run launcher redesign (#741)
## Summary - **Instant worktree creation from chat draft**: selecting "+ New worktree" in the draft branch selector immediately creates a session draft and bootstraps the worktree in the background — no modal interruption - **Redesigned multi-run launcher**: compact 2-column grid layout in a right-sized dialog with scroll shadow, sticky footer, tooltips replacing verbose descriptions, and project icons in the selector - **Branch selector aligned across surfaces**: multi-run and agent manager branch pickers now use the shared git store and match NewWorktreeDialog behavior (same default resolution cascade, no synthetic HEAD option, all branches shown) - **Opaque model multi-select dropdown**: fixes text bleed-through on translucent backgrounds by compositing `--surface-elevated` over `--surface-background` - **"+ New" inline button in sidebar worktree headers** for faster worktree creation ## Why Worktree creation was behind modal flow that interrupted the user's train of thought. The draft-first approach lets users start typing immediately while the worktree bootstraps. The multi-run launcher had an oversized form layout with redundant explanations, and its branch picker behaved differently from the main worktree dialog - causing confusion about which branches were available and what the default was.
This commit is contained in:
committed by
GitHub
parent
c66d480782
commit
53c2a0d919
@@ -66,6 +66,9 @@ export const SessionDialogs: React.FC = () => {
|
||||
archiveSessions,
|
||||
loadSessions,
|
||||
getWorktreeMetadata,
|
||||
newSessionDraft,
|
||||
setNewSessionDraftTarget,
|
||||
setDraftBootstrapPendingDirectory,
|
||||
} = useSessionStore();
|
||||
const showDeletionDialog = useUIStore((state) => state.showDeletionDialog);
|
||||
const setShowDeletionDialog = useUIStore((state) => state.setShowDeletionDialog);
|
||||
@@ -381,12 +384,34 @@ export const SessionDialogs: React.FC = () => {
|
||||
deleteLocalBranch: boolean
|
||||
): Promise<boolean> => {
|
||||
const shouldRemoveRemote = deleteDialogShouldRemoveRemote && canRemoveRemoteBranches;
|
||||
const projectRef = getProjectRefForWorktree(worktree);
|
||||
const normalizedWorktreePath = normalizeProjectDirectory(worktree.path);
|
||||
const normalizedProjectPath = normalizeProjectDirectory(projectRef.path);
|
||||
try {
|
||||
await removeProjectWorktree(
|
||||
getProjectRefForWorktree(worktree),
|
||||
projectRef,
|
||||
worktree,
|
||||
{ deleteRemoteBranch: shouldRemoveRemote, deleteLocalBranch }
|
||||
);
|
||||
|
||||
const draftDirectory = normalizeProjectDirectory(newSessionDraft?.directoryOverride);
|
||||
if (
|
||||
newSessionDraft?.open
|
||||
&& normalizedWorktreePath
|
||||
&& draftDirectory === normalizedWorktreePath
|
||||
&& normalizedProjectPath
|
||||
) {
|
||||
setDraftBootstrapPendingDirectory(null);
|
||||
setNewSessionDraftTarget({
|
||||
projectId: projectRef.id,
|
||||
directoryOverride: normalizedProjectPath,
|
||||
}, { force: true });
|
||||
}
|
||||
|
||||
if (normalizeProjectDirectory(currentDirectory) === normalizedWorktreePath && normalizedProjectPath) {
|
||||
useDirectoryStore.getState().setDirectory(normalizedProjectPath, { showOverlay: false });
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
toast.error('Failed to remove worktree', {
|
||||
@@ -394,7 +419,7 @@ export const SessionDialogs: React.FC = () => {
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}, [canRemoveRemoteBranches, deleteDialogShouldRemoveRemote, getProjectRefForWorktree]);
|
||||
}, [canRemoveRemoteBranches, currentDirectory, deleteDialogShouldRemoveRemote, getProjectRefForWorktree, newSessionDraft?.directoryOverride, newSessionDraft?.open, setDraftBootstrapPendingDirectory, setNewSessionDraftTarget]);
|
||||
|
||||
const handleConfirmDelete = React.useCallback(async () => {
|
||||
if (!deleteDialog) {
|
||||
|
||||
Reference in New Issue
Block a user