Skip to content

Commit 5e12909

Browse files
authored
feat(staged): delete project with command shortcut (#832)
Signed-off-by: Matt Toohey <contact@matttoohey.com>
1 parent 281e4fd commit 5e12909

3 files changed

Lines changed: 66 additions & 0 deletions

File tree

apps/staged/src-tauri/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1930,6 +1930,13 @@ pub fn run() {
19301930
true,
19311931
Some("CmdOrCtrl+Shift+G"),
19321932
)?;
1933+
let delete_project_item = MenuItem::with_id(
1934+
handle,
1935+
"delete_project",
1936+
"Delete Project",
1937+
true,
1938+
Some("CmdOrCtrl+Backspace"),
1939+
)?;
19331940
let zoom_in_item =
19341941
MenuItem::with_id(handle, "zoom_in", "Zoom In", true, Some("CmdOrCtrl+="))?;
19351942
let zoom_out_item =
@@ -1982,6 +1989,7 @@ pub fn run() {
19821989
&PredefinedMenuItem::cut(handle, None)?,
19831990
&PredefinedMenuItem::copy(handle, None)?,
19841991
&PredefinedMenuItem::paste(handle, None)?,
1992+
&delete_project_item,
19851993
&PredefinedMenuItem::select_all(handle, None)?,
19861994
&PredefinedMenuItem::separator(handle)?,
19871995
&find_item,
@@ -2169,6 +2177,7 @@ pub fn run() {
21692177
"find" => Some("menu:find"),
21702178
"find_next" => Some("menu:find-next"),
21712179
"find_previous" => Some("menu:find-previous"),
2180+
"delete_project" => Some("menu:delete-project"),
21722181
"zoom_in" => Some("menu:zoom-in"),
21732182
"zoom_out" => Some("menu:zoom-out"),
21742183
"zoom_reset" => Some("menu:zoom-reset"),

apps/staged/src/App.svelte

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
let unlistenFind: UnlistenFn | undefined;
6161
let unlistenFindNext: UnlistenFn | undefined;
6262
let unlistenFindPrevious: UnlistenFn | undefined;
63+
let unlistenDeleteProject: UnlistenFn | undefined;
6364
let unlistenZoomIn: UnlistenFn | undefined;
6465
let unlistenZoomOut: UnlistenFn | undefined;
6566
let unlistenZoomReset: UnlistenFn | undefined;
@@ -138,6 +139,24 @@
138139
return true;
139140
}
140141
142+
function isTextInputActive(): boolean {
143+
const target = document.activeElement;
144+
if (!(target instanceof HTMLElement)) return false;
145+
if (target.isContentEditable) return true;
146+
return (
147+
target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT'
148+
);
149+
}
150+
151+
function requestDeleteCurrentProject(): boolean {
152+
if (isTextInputActive()) return false;
153+
if (navigation.currentRoute.kind !== 'project') return false;
154+
155+
const event = new CustomEvent('staged:delete-current-project', { cancelable: true });
156+
window.dispatchEvent(event);
157+
return event.defaultPrevented;
158+
}
159+
141160
async function logUpdater(message: string) {
142161
console.warn(message);
143162
try {
@@ -261,6 +280,9 @@
261280
unlistenFindPrevious = listenToEvent('menu:find-previous', () => {
262281
if (!triggerShortcut('search-find-previous')) runSearchShortcut('previous');
263282
});
283+
unlistenDeleteProject = listenToEvent('menu:delete-project', () => {
284+
triggerShortcut('app-delete-project');
285+
});
264286
unlistenZoomIn = listenToEvent('menu:zoom-in', () => {
265287
if (!triggerShortcut('view-increase-size')) increaseSize();
266288
});
@@ -340,6 +362,14 @@
340362
modifiers: { meta: true },
341363
handler: navigateBack,
342364
},
365+
{
366+
id: 'app-delete-project',
367+
description: 'Remove current project',
368+
category: 'app',
369+
keys: ['Backspace', 'Delete'],
370+
modifiers: { meta: true },
371+
handler: requestDeleteCurrentProject,
372+
},
343373
{
344374
id: 'search-find',
345375
description: 'Find in open note/session',
@@ -449,6 +479,7 @@
449479
unlistenFind?.();
450480
unlistenFindNext?.();
451481
unlistenFindPrevious?.();
482+
unlistenDeleteProject?.();
452483
unlistenZoomIn?.();
453484
unlistenZoomOut?.();
454485
unlistenZoomReset?.();

apps/staged/src/lib/features/projects/ProjectHome.svelte

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@
115115
let branchToDelete = $state<{ branch: Branch; project: Project } | null>(null);
116116
let deletingBranches = $state<Set<string>>(new Set());
117117
let deletingProjectNames = $state<Map<string, string>>(new Map());
118+
// Guards the delete shortcut while the async safe-to-delete check is in flight,
119+
// before projectToDelete/deletingProjectNames are set, so a held key only deletes once.
120+
let deleteShortcutPending = $state(false);
118121
119122
// Setup errors come from the shared workspace lifecycle orchestrator.
120123
let worktreeErrors = $derived(workspaceLifecycle.getWorktreeErrors());
@@ -138,6 +141,8 @@
138141
const onCacheStale = () => loadData();
139142
window.addEventListener('staged:new-project', onNewProject);
140143
window.addEventListener('cache-stale', onCacheStale);
144+
const onDeleteCurrentProject = (event: Event) => handleDeleteCurrentProjectShortcut(event);
145+
window.addEventListener('staged:delete-current-project', onDeleteCurrentProject);
141146
142147
const unlistenDetection = listenToRepoActionsDetection((event) => {
143148
const matchingProjectIds = projects
@@ -273,6 +278,7 @@
273278
loadGeneration++;
274279
window.removeEventListener('staged:new-project', onNewProject);
275280
window.removeEventListener('cache-stale', onCacheStale);
281+
window.removeEventListener('staged:delete-current-project', onDeleteCurrentProject);
276282
unlistenDetection();
277283
unlistenProjectRepoAdded();
278284
unlistenPrStatus();
@@ -871,6 +877,26 @@
871877
}
872878
}
873879
880+
function handleDeleteCurrentProjectShortcut(event: Event) {
881+
if (
882+
!selectedProject ||
883+
deleteShortcutPending ||
884+
selectedProjectDeleting ||
885+
projectToDelete ||
886+
branchToDelete ||
887+
showNewProjectModal ||
888+
showAddRepoModal
889+
) {
890+
return;
891+
}
892+
893+
event.preventDefault();
894+
deleteShortcutPending = true;
895+
void handleDeleteProjectRequest(selectedProject).finally(() => {
896+
deleteShortcutPending = false;
897+
});
898+
}
899+
874900
async function confirmDeleteProject() {
875901
if (!projectToDelete) return;
876902
const id = projectToDelete.id;

0 commit comments

Comments
 (0)