open, interoperable sandbox platform for agents and humans 📦 ✨ pocketenv.io
claude-code atproto sandbox openclaw agent
at main 171 lines 5.1 kB view raw
1import { useEffect, useRef, useState } from "react"; 2import AddVariableModal from "./AddVariableModal"; 3import AddFileModal from "./AddFileModal"; 4import AddVolumeModal from "./AddVolumeModal"; 5import DeleteSandboxModal from "./DeleteSandboxModal"; 6import AddSecretModal from "./AddSecretModal"; 7import { Link } from "@tanstack/react-router"; 8 9type ContextMenuProps = { 10 sandboxId: string; 11 uri?: string; 12}; 13 14function ContextMenu({ sandboxId, uri }: ContextMenuProps) { 15 const dropdownRef = useRef<HTMLDivElement>(null); 16 const [open, setOpen] = useState(false); 17 const [ 18 isAddEnvironmentVariableModalOpen, 19 setIsAddEnvironmentVariableModalOpen, 20 ] = useState(false); 21 const [isDeleteSandboxModalOpen, setIsDeleteSandboxModalOpen] = 22 useState(false); 23 const [isAddFileModalOpen, setIsAddFileModalOpen] = useState(false); 24 const [isAddSecretModalOpen, setIsAddSecretModalOpen] = useState(false); 25 const [isAddVolumeModalOpen, setIsAddVolumeModalOpen] = useState(false); 26 27 useEffect(() => { 28 if (!open) return; 29 function handleClickOutside(event: MouseEvent) { 30 if ( 31 dropdownRef.current && 32 !dropdownRef.current.contains(event.target as Node) 33 ) { 34 setOpen(false); 35 } 36 } 37 document.addEventListener("mousedown", handleClickOutside); 38 return () => { 39 document.removeEventListener("mousedown", handleClickOutside); 40 }; 41 }, [open]); 42 43 const onOpenContextMenu = (e: React.MouseEvent) => { 44 e.stopPropagation(); 45 setOpen(!open); 46 }; 47 48 const onAddFile = (e: React.MouseEvent) => { 49 e.stopPropagation(); 50 setOpen(false); 51 setIsAddFileModalOpen(true); 52 }; 53 54 const onAddSecret = (e: React.MouseEvent) => { 55 e.stopPropagation(); 56 setOpen(false); 57 setIsAddSecretModalOpen(true); 58 }; 59 60 const onAddVariable = (e: React.MouseEvent) => { 61 e.stopPropagation(); 62 setOpen(false); 63 setIsAddEnvironmentVariableModalOpen(true); 64 }; 65 66 const onAddVolume = (e: React.MouseEvent) => { 67 e.stopPropagation(); 68 setOpen(false); 69 setIsAddVolumeModalOpen(true); 70 }; 71 72 const onDelete = (e: React.MouseEvent) => { 73 e.stopPropagation(); 74 setOpen(false); 75 setIsDeleteSandboxModalOpen(true); 76 }; 77 78 return ( 79 <> 80 <div 81 ref={dropdownRef} 82 className={`dropdown relative inline-flex items-center [--auto-close:inside] [--offset:8] [--placement:bottom-end] ${ 83 open ? "open" : "" 84 }`} 85 > 86 <button 87 type="button" 88 aria-haspopup="menu" 89 aria-expanded={open ? "true" : "false"} 90 aria-label="Dropdown" 91 className="dropdown-toggle btn btn-circle btn-text btn-sm bg-transparent outline-0 flex items-center" 92 onClick={onOpenContextMenu} 93 > 94 <span className="icon-[tabler--dots-vertical] size-5 hover:text-white block mx-auto"></span> 95 </button> 96 <ul 97 className={`dropdown-menu dropdown-open:opacity-100 absolute right-0 top-full mt-2 min-w-60 z-50 ${ 98 open ? "" : "hidden" 99 }`} 100 role="menu" 101 aria-orientation="vertical" 102 aria-labelledby="dropdown-avatar" 103 > 104 <li> 105 <div className="dropdown-item cursor-pointer" onClick={onAddFile}> 106 Add File 107 </div> 108 </li> 109 <li> 110 <div className="dropdown-item cursor-pointer" onClick={onAddSecret}> 111 Add Secret 112 </div> 113 </li> 114 <li> 115 <div 116 className="dropdown-item cursor-pointer" 117 onClick={onAddVariable} 118 > 119 Add Variable 120 </div> 121 </li> 122 <li> 123 <div className="dropdown-item cursor-pointer" onClick={onAddVolume}> 124 Add Volume 125 </div> 126 </li> 127 <li> 128 <Link 129 to={`/${uri?.split("at://")[1]?.replace("io.pocketenv.", "")}/settings`} 130 className="dropdown-item cursor-pointer" 131 > 132 Settings 133 </Link> 134 </li> 135 <li> 136 <div className="dropdown-item cursor-pointer" onClick={onDelete}> 137 Delete 138 </div> 139 </li> 140 </ul> 141 </div> 142 <AddVariableModal 143 isOpen={isAddEnvironmentVariableModalOpen} 144 onClose={() => setIsAddEnvironmentVariableModalOpen(false)} 145 sandboxId={sandboxId} 146 /> 147 <AddFileModal 148 isOpen={isAddFileModalOpen} 149 onClose={() => setIsAddFileModalOpen(false)} 150 sandboxId={sandboxId} 151 /> 152 <AddVolumeModal 153 isOpen={isAddVolumeModalOpen} 154 onClose={() => setIsAddVolumeModalOpen(false)} 155 sandboxId={sandboxId} 156 /> 157 <AddSecretModal 158 isOpen={isAddSecretModalOpen} 159 onClose={() => setIsAddSecretModalOpen(false)} 160 sandboxId={sandboxId} 161 /> 162 <DeleteSandboxModal 163 isOpen={isDeleteSandboxModalOpen} 164 onClose={() => setIsDeleteSandboxModalOpen(false)} 165 sandboxId={sandboxId} 166 /> 167 </> 168 ); 169} 170 171export default ContextMenu;