diff --git a/src/components/DeleteConfirmModal.css b/src/components/DeleteConfirmModal.css
new file mode 100644
index 0000000..117785d
--- /dev/null
+++ b/src/components/DeleteConfirmModal.css
@@ -0,0 +1,30 @@
+.dcm-backdrop {
+ position: fixed; inset: 0;
+ background: rgba(0, 0, 0, 0.55);
+ display: flex; align-items: center; justify-content: center;
+ z-index: 9999;
+}
+.dcm-box {
+ background: #1e2327; border: 1px solid #333;
+ border-radius: 10px; padding: 28px; width: 360px; max-width: 90vw;
+}
+.dcm-title {
+ color: #e0e0e0; font-size: 16px; font-weight: 600;
+ text-align: center; margin: 0 0 12px;
+}
+.dcm-body {
+ color: #aaa; font-size: 13.5px; text-align: center;
+ line-height: 1.6; margin: 0 0 22px;
+}
+.dcm-body strong { color: #e0e0e0; }
+.dcm-note { display: block; margin-top: 8px; color: #e5a820; font-size: 12px; }
+.dcm-actions { display: flex; gap: 10px; }
+.dcm-cancel {
+ flex: 1; padding: 8px 0; background: #2e3338;
+ color: #ccc; border: none; border-radius: 6px; cursor: pointer;
+}
+.dcm-confirm {
+ flex: 1; padding: 8px 0; background: #c0392b;
+ color: #fff; border: none; border-radius: 6px;
+ font-weight: 500; cursor: pointer;
+}
\ No newline at end of file
diff --git a/src/components/DeleteConfirmModal.js b/src/components/DeleteConfirmModal.js
new file mode 100644
index 0000000..0048e18
--- /dev/null
+++ b/src/components/DeleteConfirmModal.js
@@ -0,0 +1,39 @@
+import { useEffect, useRef } from "react";
+import "./DeleteConfirmModal.css";
+
+export default function DeleteConfirmModal({ target, onCancel, onConfirm }) {
+ const confirmRef = useRef(null);
+
+ useEffect(() => {
+ confirmRef.current?.focus();
+ const handler = (e) => {
+ if (e.key === "Escape") onCancel();
+ if (e.key === "Enter" && !confirmRef.current?.contains(document.activeElement)) onConfirm();
+ };
+ window.addEventListener("keydown", handler);
+ return () => window.removeEventListener("keydown", handler);
+ }, [onCancel, onConfirm]);
+ }, [onCancel, onConfirm]);
+
+ const isFolder = target?.type === "folder";
+
+ return (
+
+
e.stopPropagation()}>
+
Delete {isFolder ? "folder" : "file"}?
+
+ {target?.name} will be permanently deleted
+ {isFolder && target.childCount > 0
+ ? ` along with ${target.childCount} item${target.childCount === 1 ? '' : 's'} directly inside`
+ : ""}.
+
+ ⚠️ This affects everyone in the room.
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js
index bce35f8..3f27a55 100644
--- a/src/components/FileExplorer.js
+++ b/src/components/FileExplorer.js
@@ -1,4 +1,4 @@
-import React, { useState, useRef } from 'react';
+import React, { useState, useRef, useEffect } from 'react';
import { v4 as uuid } from 'uuid';
import {
File,
@@ -12,6 +12,7 @@ import {
ChevronRight,
ChevronDown,
} from 'lucide-react';
+import DeleteConfirmModal from "./DeleteConfirmModal";
// File icon by extension — returns a Lucide component
function FileIcon({ name }) {
@@ -28,6 +29,8 @@ function FileNode({ node, fileSystem, depth, activeFileId, onFileClick, onCreate
const [renaming, setRenaming] = useState(false);
const [renameVal, setRenameVal] = useState(node.name);
const [showCreate, setShowCreate] = useState(null); // 'file' | 'folder'
+ const [deleteTarget, setDeleteTarget] = useState(null);
+ const [undoToast, setUndoToast] = useState(null);
const [createName, setCreateName] = useState('');
const renameRef = useRef(null);
const createRef = useRef(null);
@@ -60,6 +63,43 @@ function FileNode({ node, fileSystem, depth, activeFileId, onFileClick, onCreate
setExpanded(true);
};
+ const handleDeleteClick = (nodeId, name, type, childCount = 0) => {
+ setDeleteTarget({ nodeId, name, type, childCount });
+ };
+
+ const handleDeleteConfirm = () => {
+ const { nodeId, name } = deleteTarget;
+ setDeleteTarget(null);
+
+ const timer = setTimeout(() => {
+ onDeleteNode(nodeId);
+ setUndoToast(null);
+ }, 5000);
+
+ setUndoToast({ label: name, timer });
+ };
+
+ const handleUndo = () => {
+ clearTimeout(undoToast.timer);
+ setUndoToast(null);
+ };
+
+ useEffect(() => {
+ return () => {
+ if (undoToast?.timer) {
+ clearTimeout(undoToast.timer);
+ }
+ };
+}, [undoToast]);
+
+ useEffect(() => {
+ return () => {
+ if (undoToast?.timer) {
+ clearTimeout(undoToast.timer);
+ }
+ };
+ }, [undoToast]);
+
const children = (node.children || []).map(id => fileSystem[id]).filter(Boolean);
return (
@@ -115,7 +155,7 @@ function FileNode({ node, fileSystem, depth, activeFileId, onFileClick, onCreate
-