"use client";
import { getFileIcon, type FileEntry } from "@/modules/files/components/file-manager-presenters";
import {
FILES_MENU_SHELL,
FILES_PANEL_INSET,
} from "@/modules/files/components/file-manager-surface";
import { cn } from "@/lib/utils";
import { formatBytesCompact } from "@/lib/client/format";
import type { FileInfoResponse } from "@/lib/shared/contracts/files";
import { File, FileArchive, Folder, Trash2, Upload, X } from "@/components/icons/platform-icons";
const cancelBtn = "rounded-lg px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-background/50 hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50";
const inputField = "mt-1 h-8 w-full rounded-lg border border-glass-border bg-background/55 px-2 text-xs text-foreground placeholder:text-muted-foreground/60 outline-none focus:border-primary/40 focus:bg-background/70";
const overlay = "absolute inset-0 z-[205] flex items-center justify-center bg-background/35 px-4 backdrop-blur-[1px]";
export function CreateEntryDialog({
dialog,
isCreatePending,
onClose,
onDialogChange,
onSubmit,
}: {
dialog: { kind: "file" | "folder"; name: string; error: string | null };
isCreatePending: boolean;
onClose: () => void;
onDialogChange: (value: string) => void;
onSubmit: () => void;
}) {
return (
e.stopPropagation()}
>
{dialog.kind === "folder" ? (
) : (
)}
{dialog.kind === "folder" ? "Create New Folder" : "Create New File"}
{dialog.error &&
{dialog.error}
}
);
}
export function RenameEntryDialog({
dialog,
isRenamePending,
onClose,
onDialogChange,
onSubmit,
}: {
dialog: { entry: FileEntry; name: string; error: string | null };
isRenamePending: boolean;
onClose: () => void;
onDialogChange: (value: string) => void;
onSubmit: () => void;
}) {
return (
e.stopPropagation()}
>
{dialog.entry.type === "folder" ? (
) : (
)}
Rename
{dialog.error &&
{dialog.error}
}
);
}
export function EmptyTrashConfirmDialog({
itemCount,
onCancel,
onConfirm,
}: {
itemCount: number;
onCancel: () => void;
onConfirm: () => void;
}) {
return (
e.stopPropagation()}
>
Empty Trash
Permanently delete{" "}
{itemCount} item{itemCount !== 1 ? "s" : ""}
{" "}
from Trash? This cannot be undone.
);
}
function getUploadPercent(progress: { loaded: number; total: number } | null) {
if (!progress || progress.total <= 0) return 0;
return Math.min(100, Math.max(0, Math.round((progress.loaded / progress.total) * 100)));
}
export function UploadProgressDialog({
operation = "upload",
progress,
onCancel,
}: {
operation?: "upload" | "unzip";
progress: { loaded: number; total: number } | null;
onCancel?: () => void;
}) {
const isUpload = operation === "upload";
const progressPercent = getUploadPercent(progress);
const progressText = progress
? `${formatBytesCompact(progress.loaded)} of ${formatBytesCompact(progress.total)}`
: isUpload
? "Preparing upload"
: "Extracting archive";
const title = isUpload ? "Uploading files" : "Unzipping file";
const status = isUpload ? "Uploading" : "Unzipping";
const progressLabel = isUpload ? "Upload progress" : "Unzip progress";
return (
{onCancel ? (
) : (
)}
{isUpload ? (
) : (
)}
{title}
{progressText}
{status}
{progress ? `${progressPercent}%` : isUpload ? "0%" : "Working"}
{onCancel ? (
) : null}
);
}
export function FileInfoDialogOverlay({
fileInfo,
onClose,
}: {
fileInfo: FileInfoResponse;
onClose: () => void;
}) {
const rows = [
{
label: "Type",
value:
fileInfo.type === "folder"
? "Folder"
: fileInfo.ext
? `${fileInfo.ext.toUpperCase()} file`
: "File",
},
{ label: "Size", value: fileInfo.type === "folder" ? "—" : formatBytesCompact(fileInfo.sizeBytes) },
{
label: "Modified",
value: new Date(fileInfo.modifiedAt).toLocaleString("en-US", {
month: "short", day: "numeric", year: "numeric", hour: "2-digit", minute: "2-digit",
}),
},
{
label: "Created",
value: new Date(fileInfo.createdAt).toLocaleString("en-US", {
month: "short", day: "numeric", year: "numeric", hour: "2-digit", minute: "2-digit",
}),
},
{ label: "Path", value: fileInfo.path },
{ label: "Permissions", value: fileInfo.permissions },
{ label: "Starred", value: fileInfo.starred ? "Yes" : "No" },
];
return (
e.stopPropagation()}
>
{getFileIcon({ ...fileInfo, modified: "", modifiedAt: "", mtimeMs: 0 } as FileEntry)}
{fileInfo.name}
{rows.map(({ label, value }) => (
{label}
{value}
))}
);
}
export function PasteConflictDialog({
conflictName,
onReplace,
onKeepBoth,
onSkip,
onSkipAll,
}: {
conflictName: string;
onReplace: () => void;
onKeepBoth: () => void;
onSkip: () => void;
onSkipAll: () => void;
}) {
return (
Item Already Exists
{conflictName} already exists in the destination. What would you like to do?
);
}