diff --git a/README.md b/README.md index 4b827e3..e4c0a36 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ ccexp --path ~/workspace # Specific directory - **d** - Copy file to current directory (in menu) - **e** - Edit file with $EDITOR (in menu) - **o** - Open file in default application (in menu) +- **x** - Delete file (in menu, requires confirmation) ## Development diff --git a/package.json b/package.json index 5e7c49a..5516208 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "lefthook": "^1.12.2", "publint": "^0.3.12", "tsdown": "^0.12.9", + "typescript": "^5.9.3", "vitest": "^3.2.4" }, "overrides": { diff --git a/src/components/FileList/MenuActions.test.tsx b/src/components/FileList/MenuActions.test.tsx index 2e121c1..56c2137 100644 --- a/src/components/FileList/MenuActions.test.tsx +++ b/src/components/FileList/MenuActions.test.tsx @@ -54,6 +54,7 @@ if (import.meta.vitest) { expect(lastFrame()).toContain('[D] Copy to Current Directory'); expect(lastFrame()).toContain('[E] Edit File'); expect(lastFrame()).toContain('[O] Open File'); + expect(lastFrame()).toContain('[X] Delete File'); }, ); }); @@ -157,13 +158,14 @@ if (import.meta.vitest) { , ); - // Verify 6 actions are present + // Verify 7 actions are present expect(lastFrame()).toContain('[C] Copy Content'); expect(lastFrame()).toContain('[P] Copy Path (Absolute)'); expect(lastFrame()).toContain('[R] Copy Path (Relative)'); expect(lastFrame()).toContain('[D] Copy to Current Directory'); expect(lastFrame()).toContain('[E] Edit File'); expect(lastFrame()).toContain('[O] Open File'); + expect(lastFrame()).toContain('[X] Delete File'); }, ); }); @@ -225,6 +227,7 @@ if (import.meta.vitest) { output?.indexOf('[D] Copy to Current Directory') ?? -1; const editFileIndex = output?.indexOf('[E] Edit File') ?? -1; const openFileIndex = output?.indexOf('[O] Open File') ?? -1; + const deleteFileIndex = output?.indexOf('[X] Delete File') ?? -1; expect(copyContentIndex).toBeGreaterThan(-1); expect(copyAbsoluteIndex).toBeGreaterThan(copyContentIndex); @@ -232,6 +235,7 @@ if (import.meta.vitest) { expect(copyDirIndex).toBeGreaterThan(copyRelativeIndex); expect(editFileIndex).toBeGreaterThan(copyDirIndex); expect(openFileIndex).toBeGreaterThan(editFileIndex); + expect(deleteFileIndex).toBeGreaterThan(openFileIndex); }, ); }); @@ -378,6 +382,7 @@ if (import.meta.vitest) { expect(output).toContain('Copy to Current Directory'); expect(output).toContain('Edit File'); expect(output).toContain('Open File'); + expect(output).toContain('Delete File'); }, ); }); @@ -410,6 +415,7 @@ if (import.meta.vitest) { expect(output).toContain('[D]'); expect(output).toContain('[E]'); expect(output).toContain('[O]'); + expect(output).toContain('[X]'); }, ); }); diff --git a/src/components/FileList/MenuActions/hooks/useMenu.ts b/src/components/FileList/MenuActions/hooks/useMenu.ts index df882a9..8b75f42 100644 --- a/src/components/FileList/MenuActions/hooks/useMenu.ts +++ b/src/components/FileList/MenuActions/hooks/useMenu.ts @@ -227,6 +227,30 @@ export const useMenu = ({ file, onClose }: UseMenuProps) => { return '✅ File opened'; }, }, + { + key: 'x', + label: 'Delete File', + description: 'Delete file (requires confirmation)', + action: async () => { + const fs = await import('node:fs/promises'); + + // The actual deletion operation + const performDelete = async () => { + await fs.unlink(file.path); + return `✅ File deleted: ${basename(file.path)}`; + }; + + // Always require confirmation for deletion + setConfirmMessage( + `Are you sure you want to delete "${basename(file.path)}"? This action cannot be undone.`, + ); + setIsConfirming(true); + setPendingAction(() => performDelete); + + // Return early, action will be executed after confirmation + return ''; + }, + }, ], [file.path, file.type, copyToClipboard, openFile, editFile], );