feat: add create and delete file/folder functionality to editor
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m33s
CI / Lint & Test (pull_request) Successful in 17m8s
CI / Build Linux (pull_request) Successful in 20m45s
CI / Build Windows (cross-compile) (pull_request) Successful in 35m50s

- Add Tauri backend commands: create_file, create_directory, delete_file, delete_directory
- Add editor store functions for create/delete with auto-refresh
- Add FileContextMenu component with right-click support
- Add InputDialog component for file/folder name input
- Add ConfirmDialog component for delete confirmation
- Add Ctrl+N keyboard shortcut for new file
- Update keyboard shortcuts modal with new shortcuts
- Auto-close tabs when their files are deleted
- Auto-refresh file tree after create/delete operations
This commit is contained in:
2026-01-28 16:23:40 -08:00
committed by Naomi Carrigan
parent 392243f54f
commit d6d43a8abe
10 changed files with 660 additions and 26 deletions
@@ -0,0 +1,110 @@
<script lang="ts">
import type { FileEntry } from "$lib/types/editor";
interface Props {
x: number;
y: number;
targetEntry: FileEntry | null;
currentDirectory: string;
onNewFile: (parentPath: string) => void;
onNewFolder: (parentPath: string) => void;
onDelete: (entry: FileEntry) => void;
onClose: () => void;
}
let { x, y, targetEntry, currentDirectory, onNewFile, onNewFolder, onDelete, onClose }: Props =
$props();
function handleNewFile() {
const parentPath = targetEntry?.isDirectory ? targetEntry.path : currentDirectory;
onNewFile(parentPath);
onClose();
}
function handleNewFolder() {
const parentPath = targetEntry?.isDirectory ? targetEntry.path : currentDirectory;
onNewFolder(parentPath);
onClose();
}
function handleDelete() {
if (targetEntry) {
onDelete(targetEntry);
onClose();
}
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === "Escape") {
onClose();
}
}
</script>
<svelte:window onkeydown={handleKeydown} />
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="fixed inset-0 z-50"
onclick={onClose}
oncontextmenu={(e) => {
e.preventDefault();
onClose();
}}
>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="absolute z-50 min-w-[160px] rounded-md border border-gray-700 bg-gray-800 py-1 shadow-lg"
style="left: {x}px; top: {y}px;"
onclick={(e) => e.stopPropagation()}
>
<button
class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm text-gray-200 hover:bg-gray-700"
onclick={handleNewFile}
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
New File
</button>
<button
class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm text-gray-200 hover:bg-gray-700"
onclick={handleNewFolder}
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"
/>
</svg>
New Folder
</button>
{#if targetEntry}
<div class="my-1 border-t border-gray-700"></div>
<button
class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm text-red-400 hover:bg-gray-700"
onclick={handleDelete}
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
Delete {targetEntry.isDirectory ? "Folder" : "File"}
</button>
{/if}
</div>
</div>