delete file ui + logic

This commit is contained in:
Ishaan Dey 2024-04-30 01:56:43 -04:00
parent 268dcb61f2
commit 894de9990a
13 changed files with 421 additions and 44 deletions

View File

@ -129,6 +129,19 @@ io.on("connection", (socket) => __awaiter(void 0, void 0, void 0, function* () {
}); });
yield (0, utils_1.renameFile)(fileId, newFileId, file.data); yield (0, utils_1.renameFile)(fileId, newFileId, file.data);
})); }));
socket.on("deleteFile", (fileId, callback) => __awaiter(void 0, void 0, void 0, function* () {
const file = sandboxFiles.fileData.find((f) => f.id === fileId);
if (!file)
return;
fs_1.default.unlink(path_1.default.join(dirName, fileId), function (err) {
if (err)
throw err;
});
sandboxFiles.fileData = sandboxFiles.fileData.filter((f) => f.id !== fileId);
yield (0, utils_1.deleteFile)(fileId);
const newFiles = yield (0, utils_1.getSandboxFiles)(data.id);
callback(newFiles.files);
}));
socket.on("createTerminal", ({ id }) => { socket.on("createTerminal", ({ id }) => {
console.log("creating terminal, id=" + id); console.log("creating terminal, id=" + id);
const pty = (0, node_pty_1.spawn)(os_1.default.platform() === "win32" ? "cmd.exe" : "bash", [], { const pty = (0, node_pty_1.spawn)(os_1.default.platform() === "win32" ? "cmd.exe" : "bash", [], {

View File

@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.saveFile = exports.renameFile = exports.createFile = exports.getSandboxFiles = void 0; exports.deleteFile = exports.saveFile = exports.renameFile = exports.createFile = exports.getSandboxFiles = void 0;
const getSandboxFiles = (id) => __awaiter(void 0, void 0, void 0, function* () { const getSandboxFiles = (id) => __awaiter(void 0, void 0, void 0, function* () {
const sandboxRes = yield fetch(`https://storage.ishaan1013.workers.dev/api?sandboxId=${id}`); const sandboxRes = yield fetch(`https://storage.ishaan1013.workers.dev/api?sandboxId=${id}`);
const sandboxData = yield sandboxRes.json(); const sandboxData = yield sandboxRes.json();
@ -110,3 +110,14 @@ const saveFile = (fileId, data) => __awaiter(void 0, void 0, void 0, function* (
return res.ok; return res.ok;
}); });
exports.saveFile = saveFile; exports.saveFile = saveFile;
const deleteFile = (fileId) => __awaiter(void 0, void 0, void 0, function* () {
const res = yield fetch(`https://storage.ishaan1013.workers.dev/api`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ fileId }),
});
return res.ok;
});
exports.deleteFile = deleteFile;

View File

@ -8,7 +8,13 @@ import { Server } from "socket.io"
import { z } from "zod" import { z } from "zod"
import { User } from "./types" import { User } from "./types"
import { createFile, getSandboxFiles, renameFile, saveFile } from "./utils" import {
createFile,
deleteFile,
getSandboxFiles,
renameFile,
saveFile,
} from "./utils"
import { IDisposable, IPty, spawn } from "node-pty" import { IDisposable, IPty, spawn } from "node-pty"
dotenv.config() dotenv.config()
@ -148,6 +154,21 @@ io.on("connection", async (socket) => {
await renameFile(fileId, newFileId, file.data) await renameFile(fileId, newFileId, file.data)
}) })
socket.on("deleteFile", async (fileId: string, callback) => {
const file = sandboxFiles.fileData.find((f) => f.id === fileId)
if (!file) return
fs.unlink(path.join(dirName, fileId), function (err) {
if (err) throw err
})
sandboxFiles.fileData = sandboxFiles.fileData.filter((f) => f.id !== fileId)
await deleteFile(fileId)
const newFiles = await getSandboxFiles(data.id)
callback(newFiles.files)
})
socket.on("createTerminal", ({ id }: { id: string }) => { socket.on("createTerminal", ({ id }: { id: string }) => {
console.log("creating terminal, id=" + id) console.log("creating terminal, id=" + id)
const pty = spawn(os.platform() === "win32" ? "cmd.exe" : "bash", [], { const pty = spawn(os.platform() === "win32" ? "cmd.exe" : "bash", [], {

View File

@ -123,3 +123,14 @@ export const saveFile = async (fileId: string, data: string) => {
}) })
return res.ok return res.ok
} }
export const deleteFile = async (fileId: string) => {
const res = await fetch(`https://storage.ishaan1013.workers.dev/api`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ fileId }),
})
return res.ok
}

View File

@ -50,6 +50,17 @@ export default {
await env.R2.put(fileId, ''); await env.R2.put(fileId, '');
return success;
} else if (method === 'DELETE') {
const deleteSchema = z.object({
fileId: z.string(),
});
const body = await request.json();
const { fileId } = deleteSchema.parse(body);
await env.R2.delete(fileId);
return success; return success;
} else return methodNotAllowed; } else return methodNotAllowed;
} else if (path === '/api/rename' && method === 'POST') { } else if (path === '/api/rename' && method === 'POST') {

View File

@ -27,9 +27,6 @@ import { processFileType, validateName } from "@/lib/utils"
import { toast } from "sonner" import { toast } from "sonner"
import EditorTerminal from "./terminal" import EditorTerminal from "./terminal"
import { Terminal } from "@xterm/xterm"
import { FitAddon } from "@xterm/addon-fit"
export default function CodeEditor({ export default function CodeEditor({
userId, userId,
sandboxId, sandboxId,
@ -135,6 +132,9 @@ export default function CodeEditor({
const closeTab = (tab: TFile) => { const closeTab = (tab: TFile) => {
const numTabs = tabs.length const numTabs = tabs.length
const index = tabs.findIndex((t) => t.id === tab.id) const index = tabs.findIndex((t) => t.id === tab.id)
if (index === -1) return
const nextId = const nextId =
activeId === tab.id activeId === tab.id
? numTabs === 1 ? numTabs === 1
@ -156,9 +156,12 @@ export default function CodeEditor({
oldName: string, oldName: string,
type: "file" | "folder" type: "file" | "folder"
) => { ) => {
if (!validateName(newName, oldName, type)) return false if (!validateName(newName, oldName, type)) {
toast.error("Invalid file name.")
console.log("invalid name")
return false
}
// Action
socket.emit("renameFile", id, newName) socket.emit("renameFile", id, newName)
setTabs((prev) => setTabs((prev) =>
prev.map((tab) => (tab.id === id ? { ...tab, name: newName } : tab)) prev.map((tab) => (tab.id === id ? { ...tab, name: newName } : tab))
@ -167,12 +170,27 @@ export default function CodeEditor({
return true return true
} }
const handleDeleteFile = (file: TFile) => {
socket.emit("deleteFile", file.id, (response: (TFolder | TFile)[]) => {
setFiles(response)
})
closeTab(file)
}
const handleDeleteFolder = (folder: TFolder) => {
// socket.emit("deleteFolder", folder.id, (response: (TFolder | TFile)[]) => {
// setFiles(response)
// })
}
return ( return (
<> <>
<Sidebar <Sidebar
files={files} files={files}
selectFile={selectFile} selectFile={selectFile}
handleRename={handleRename} handleRename={handleRename}
handleDeleteFile={handleDeleteFile}
handleDeleteFolder={handleDeleteFolder}
socket={socket} socket={socket}
addNew={(name, type) => { addNew={(name, type) => {
if (type === "file") { if (type === "file") {

View File

@ -4,11 +4,19 @@ import Image from "next/image"
import { getIconForFile } from "vscode-icons-js" import { getIconForFile } from "vscode-icons-js"
import { TFile, TTab } from "./types" import { TFile, TTab } from "./types"
import { useEffect, useRef, useState } from "react" import { useEffect, useRef, useState } from "react"
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
import { Loader2, Pencil, Trash2 } from "lucide-react"
export default function SidebarFile({ export default function SidebarFile({
data, data,
selectFile, selectFile,
handleRename, handleRename,
handleDeleteFile,
}: { }: {
data: TFile data: TFile
selectFile: (file: TTab) => void selectFile: (file: TTab) => void
@ -18,16 +26,21 @@ export default function SidebarFile({
oldName: string, oldName: string,
type: "file" | "folder" type: "file" | "folder"
) => boolean ) => boolean
handleDeleteFile: (file: TFile) => void
}) { }) {
const [imgSrc, setImgSrc] = useState(`/icons/${getIconForFile(data.name)}`) const [imgSrc, setImgSrc] = useState(`/icons/${getIconForFile(data.name)}`)
const [editing, setEditing] = useState(false) const [editing, setEditing] = useState(false)
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
const [pendingDelete, setPendingDelete] = useState(false)
useEffect(() => { useEffect(() => {
if (editing) { if (editing) {
inputRef.current?.focus() setTimeout(() => inputRef.current?.focus(), 0)
} }
}, [editing]) if (!inputRef.current) {
console.log("no input ref")
}
}, [editing, inputRef.current])
const renameFile = () => { const renameFile = () => {
const renamed = handleRename( const renamed = handleRename(
@ -43,12 +56,16 @@ export default function SidebarFile({
} }
return ( return (
<button <ContextMenu>
onClick={() => selectFile({ ...data, saved: true })} <ContextMenuTrigger
onDoubleClick={() => { disabled={pendingDelete}
setEditing(true) onClick={() => {
if (!editing && !pendingDelete) selectFile({ ...data, saved: true })
}} }}
className="w-full flex items-center h-7 px-1 hover:bg-secondary rounded-sm cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" // onDoubleClick={() => {
// setEditing(true)
// }}
className="data-[state=open]:bg-secondary/50 w-full flex items-center h-7 px-1 hover:bg-secondary rounded-sm cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
> >
<Image <Image
src={imgSrc} src={imgSrc}
@ -58,6 +75,12 @@ export default function SidebarFile({
className="mr-2" className="mr-2"
onError={() => setImgSrc("/icons/default_file.svg")} onError={() => setImgSrc("/icons/default_file.svg")}
/> />
{pendingDelete ? (
<>
<Loader2 className="text-muted-foreground w-4 h-4 animate-spin mr-2" />
<div className="text-muted-foreground">Deleting...</div>
</>
) : (
<form <form
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault() e.preventDefault()
@ -74,6 +97,30 @@ export default function SidebarFile({
onBlur={() => renameFile()} onBlur={() => renameFile()}
/> />
</form> </form>
</button> )}
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem
onClick={() => {
console.log("rename")
setEditing(true)
}}
>
<Pencil className="w-4 h-4 mr-2" />
Rename
</ContextMenuItem>
<ContextMenuItem
disabled={pendingDelete}
onClick={() => {
console.log("delete")
setPendingDelete(true)
handleDeleteFile(data)
}}
>
<Trash2 className="w-4 h-4 mr-2" />
Delete
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
) )
} }

View File

@ -10,6 +10,8 @@ export default function SidebarFolder({
data, data,
selectFile, selectFile,
handleRename, handleRename,
handleDeleteFile,
handleDeleteFolder,
}: { }: {
data: TFolder data: TFolder
selectFile: (file: TTab) => void selectFile: (file: TTab) => void
@ -19,6 +21,8 @@ export default function SidebarFolder({
oldName: string, oldName: string,
type: "file" | "folder" type: "file" | "folder"
) => boolean ) => boolean
handleDeleteFile: (file: TFile) => void
handleDeleteFolder: (folder: TFolder) => void
}) { }) {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const folder = isOpen const folder = isOpen
@ -82,6 +86,7 @@ export default function SidebarFolder({
data={child} data={child}
selectFile={selectFile} selectFile={selectFile}
handleRename={handleRename} handleRename={handleRename}
handleDeleteFile={handleDeleteFile}
/> />
) : ( ) : (
<SidebarFolder <SidebarFolder
@ -89,6 +94,8 @@ export default function SidebarFolder({
data={child} data={child}
selectFile={selectFile} selectFile={selectFile}
handleRename={handleRename} handleRename={handleRename}
handleDeleteFile={handleDeleteFile}
handleDeleteFolder={handleDeleteFolder}
/> />
) )
)} )}

View File

@ -12,6 +12,8 @@ export default function Sidebar({
files, files,
selectFile, selectFile,
handleRename, handleRename,
handleDeleteFile,
handleDeleteFolder,
socket, socket,
addNew, addNew,
}: { }: {
@ -23,6 +25,8 @@ export default function Sidebar({
oldName: string, oldName: string,
type: "file" | "folder" type: "file" | "folder"
) => boolean ) => boolean
handleDeleteFile: (file: TFile) => void
handleDeleteFolder: (folder: TFolder) => void
socket: Socket socket: Socket
addNew: (name: string, type: "file" | "folder") => void addNew: (name: string, type: "file" | "folder") => void
}) { }) {
@ -66,6 +70,7 @@ export default function Sidebar({
data={child} data={child}
selectFile={selectFile} selectFile={selectFile}
handleRename={handleRename} handleRename={handleRename}
handleDeleteFile={handleDeleteFile}
/> />
) : ( ) : (
<SidebarFolder <SidebarFolder
@ -73,6 +78,8 @@ export default function Sidebar({
data={child} data={child}
selectFile={selectFile} selectFile={selectFile}
handleRename={handleRename} handleRename={handleRename}
handleDeleteFile={handleDeleteFile}
handleDeleteFolder={handleDeleteFolder}
/> />
) )
)} )}

View File

@ -0,0 +1,204 @@
"use client"
import * as React from "react"
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
import {
CheckIcon,
ChevronRightIcon,
DotFilledIcon,
} from "@radix-ui/react-icons"
import { cn } from "@/lib/utils"
const ContextMenu = ContextMenuPrimitive.Root
const ContextMenuTrigger = ContextMenuPrimitive.Trigger
const ContextMenuGroup = ContextMenuPrimitive.Group
const ContextMenuPortal = ContextMenuPrimitive.Portal
const ContextMenuSub = ContextMenuPrimitive.Sub
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
const ContextMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
<ContextMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
inset && "pl-8",
className
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto h-4 w-4" />
</ContextMenuPrimitive.SubTrigger>
))
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName
const ContextMenuSubContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
))
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName
const ContextMenuContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props}
/>
</ContextMenuPrimitive.Portal>
))
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName
const ContextMenuItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<ContextMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
inset && "pl-8",
className
)}
{...props}
/>
))
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName
const ContextMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<ContextMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4" />
</ContextMenuPrimitive.ItemIndicator>
</span>
{children}
</ContextMenuPrimitive.CheckboxItem>
))
ContextMenuCheckboxItem.displayName =
ContextMenuPrimitive.CheckboxItem.displayName
const ContextMenuRadioItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<ContextMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<DotFilledIcon className="h-4 w-4 fill-current" />
</ContextMenuPrimitive.ItemIndicator>
</span>
{children}
</ContextMenuPrimitive.RadioItem>
))
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName
const ContextMenuLabel = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<ContextMenuPrimitive.Label
ref={ref}
className={cn(
"px-2 py-1.5 text-sm font-semibold text-foreground",
inset && "pl-8",
className
)}
{...props}
/>
))
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName
const ContextMenuSeparator = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-border", className)}
{...props}
/>
))
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName
const ContextMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
className
)}
{...props}
/>
)
}
ContextMenuShortcut.displayName = "ContextMenuShortcut"
export {
ContextMenu,
ContextMenuTrigger,
ContextMenuContent,
ContextMenuItem,
ContextMenuCheckboxItem,
ContextMenuRadioItem,
ContextMenuLabel,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuGroup,
ContextMenuPortal,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuRadioGroup,
}

View File

@ -1,5 +1,5 @@
import { type ClassValue, clsx } from "clsx" import { type ClassValue, clsx } from "clsx"
import { toast } from "sonner" // import { toast } from "sonner"
import { twMerge } from "tailwind-merge" import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
@ -21,18 +21,15 @@ export function validateName(
oldName: string, oldName: string,
type: "file" | "folder" type: "file" | "folder"
) { ) {
if (newName === oldName || newName.length === 0) {
return false
}
if ( if (
newName === oldName ||
newName.length === 0 ||
newName.includes("/") || newName.includes("/") ||
newName.includes("\\") || newName.includes("\\") ||
newName.includes(" ") || newName.includes(" ") ||
(type === "file" && !newName.includes(".")) || (type === "file" && !newName.includes(".")) ||
(type === "folder" && newName.includes(".")) (type === "folder" && newName.includes("."))
) { ) {
toast.error("Invalid file name.")
return false return false
} }
return true return true

View File

@ -13,6 +13,7 @@
"@hookform/resolvers": "^3.3.4", "@hookform/resolvers": "^3.3.4",
"@monaco-editor/react": "^4.6.0", "@monaco-editor/react": "^4.6.0",
"@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-context-menu": "^2.1.5",
"@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
@ -707,6 +708,34 @@
} }
} }
}, },
"node_modules/@radix-ui/react-context-menu": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.1.5.tgz",
"integrity": "sha512-R5XaDj06Xul1KGb+WP8qiOh7tKJNz2durpLBXAGZjSVtctcRFCuEvy2gtMwRJGePwQQE5nV77gs4FwRi8T+r2g==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-menu": "2.0.6",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-callback-ref": "1.0.1",
"@radix-ui/react-use-controllable-state": "1.0.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-dialog": { "node_modules/@radix-ui/react-dialog": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz",

View File

@ -14,6 +14,7 @@
"@hookform/resolvers": "^3.3.4", "@hookform/resolvers": "^3.3.4",
"@monaco-editor/react": "^4.6.0", "@monaco-editor/react": "^4.6.0",
"@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-context-menu": "^2.1.5",
"@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",