the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add sandbox settings pages and routes

Introduce settings sidebar and individual pages (Files, Integrations,
Repository, Secrets, SSH Keys, Variables, Volumes) and add routes with
beforeLoad auth redirects. Update Main and Navbar to accept sidebar,
root
and rootLink props; pass sandbox uri into ContextMenu and import Link.

+826 -26
+11 -6
apps/web/src/components/contextmenu/ContextMenu.tsx
··· 4 4 import AddVolumeModal from "./AddVolumeModal"; 5 5 import DeleteSandboxModal from "./DeleteSandboxModal"; 6 6 import AddSecretModal from "./AddSecretModal"; 7 + import { Link } from "@tanstack/react-router"; 7 8 8 9 type ContextMenuProps = { 9 10 sandboxId: string; 11 + uri?: string; 10 12 }; 11 13 12 - function ContextMenu({ sandboxId }: ContextMenuProps) { 14 + function ContextMenu({ sandboxId, uri }: ContextMenuProps) { 13 15 const dropdownRef = useRef<HTMLDivElement>(null); 14 16 const [open, setOpen] = useState(false); 15 17 const [ ··· 123 125 </div> 124 126 </li> 125 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> 126 136 <div className="dropdown-item cursor-pointer" onClick={onDelete}> 127 137 Delete 128 138 </div> 129 139 </li> 130 - {/*<li> 131 - <a href="/settings" className="dropdown-item cursor-pointer"> 132 - Settings 133 - </a>* 134 - </li>*/} 135 140 </ul> 136 141 </div> 137 142 <AddEnvironmentVariableModal
+27 -4
apps/web/src/components/navbar/Navbar.tsx
··· 13 13 title: string; 14 14 project?: string; 15 15 withLogo?: boolean; 16 + root?: string; 17 + rootLink?: string; 16 18 }; 17 19 18 - function Navbar({ title, project, withLogo }: NavbarProps) { 20 + function Navbar({ title, project, withLogo, root, rootLink }: NavbarProps) { 19 21 const [, setProfile] = useAtom(profileAtom); 20 22 const [open, setOpen] = useState(false); 21 23 const [modalOpen, setModalOpen] = useState(false); ··· 94 96 <img src={Logo} className="max-h-[40px] mr-[15px] flex-shrink-0" /> 95 97 </Link> 96 98 )} 97 - <div className="text-base-content font-semibold no-underline text-[23px] truncate"> 98 - {title} 99 - </div> 99 + {root && ( 100 + <div className="breadcrumbs"> 101 + <ul> 102 + <li> 103 + <Link to="/projects">Projects</Link> 104 + </li> 105 + <li className="breadcrumbs-separator rtl:rotate-180"> 106 + <span className="icon-[tabler--chevron-right]"></span> 107 + </li> 108 + <li> 109 + <Link to={rootLink!}>{root}</Link> 110 + </li> 111 + <li className="breadcrumbs-separator rtl:rotate-180"> 112 + <span className="icon-[tabler--chevron-right]"></span> 113 + </li> 114 + <li aria-current="page">{title}</li> 115 + </ul> 116 + </div> 117 + )} 118 + {!root && ( 119 + <div className="text-base-content font-semibold no-underline text-[23px] truncate"> 120 + {title} 121 + </div> 122 + )} 100 123 {project && ( 101 124 <div className="text-[15px] ml-1 truncate hidden sm:block"> 102 125 {project}
+15 -3
apps/web/src/layouts/Main.tsx
··· 6 6 import { sidebarCollapsedAtom } from "../atoms/sidebar"; 7 7 8 8 type MainProps = { 9 + sidebar?: React.ReactNode; 9 10 children: React.ReactNode; 11 + root?: string; 12 + rootLink?: string; 10 13 }; 11 14 12 - function Main({ children }: MainProps) { 15 + function Main({ children, sidebar, root, rootLink }: MainProps) { 13 16 const routerState = useRouterState(); 14 17 const pathname = routerState.location.pathname; 15 18 const isCollapsed = useAtomValue(sidebarCollapsedAtom); ··· 21 24 if (path === "/volumes") return "Volumes"; 22 25 if (path === "/secrets") return "Secrets"; 23 26 if (path === "/settings") return "Settings"; 27 + if ( 28 + /^\/did:plc:[a-z0-9]+\/sandbox\/[a-z0-9]+\/settings$/.test(path) || 29 + /^\/did:plc:[a-z0-9]+\/sandbox\/[a-z0-9]+\/ssh-keys$/.test(path) || 30 + /^\/did:plc:[a-z0-9]+\/sandbox\/[a-z0-9]+\/variables$/.test(path) || 31 + /^\/did:plc:[a-z0-9]+\/sandbox\/[a-z0-9]+\/volumes$/.test(path) || 32 + /^\/did:plc:[a-z0-9]+\/sandbox\/[a-z0-9]+\/files$/.test(path) || 33 + /^\/did:plc:[a-z0-9]+\/sandbox\/[a-z0-9]+\/secrets$/.test(path) 34 + ) 35 + return "Settings"; 24 36 25 37 return ""; 26 38 }; ··· 29 41 30 42 return ( 31 43 <div className="flex min-h-screen bg-base-100"> 32 - <Sidebar /> 44 + {sidebar ? sidebar : <Sidebar />} 33 45 <div 34 46 className={`flex flex-col flex-1 bg-base-100 ${ 35 47 isCollapsed ? "sm:ml-[72px]" : "sm:ml-64" 36 48 }`} 37 49 > 38 - <Navbar title={title} /> 50 + <Navbar title={title} root={root} rootLink={rootLink} /> 39 51 <main className="flex-1 p-4 bg-base-100">{children}</main> 40 52 </div> 41 53 </div>
+1 -1
apps/web/src/pages/projects/Project/Project.tsx
··· 110 110 className={`icon-[mingcute--terminal-fill] size-5 ${sandbox.status !== "RUNNING" ? "" : "hover:text-white"}`} 111 111 ></span> 112 112 </button> 113 - <ContextMenu sandboxId={sandbox.id} /> 113 + <ContextMenu sandboxId={sandbox.id} uri={sandbox.uri} /> 114 114 </div> 115 115 116 116 <TerminalModal
+4 -1
apps/web/src/pages/sandbox/Sandbox.tsx
··· 119 119 </span> 120 120 {((profile && data?.sandbox?.owner?.did === profile.did) || 121 121 !data?.sandbox?.owner) && ( 122 - <ContextMenu sandboxId={data?.sandbox?.id} /> 122 + <ContextMenu 123 + sandboxId={data?.sandbox?.id} 124 + uri={data?.sandbox?.uri} 125 + /> 123 126 )} 124 127 </div> 125 128
+59 -3
apps/web/src/pages/settings/Settings.tsx
··· 1 + import { useRouterState } from "@tanstack/react-router"; 1 2 import Main from "../../layouts/Main"; 3 + import Sidebar from "./sidebar/Sidebar"; 4 + import { useSandboxQuery } from "../../hooks/useSandbox"; 2 5 3 6 function Settings() { 7 + const routerState = useRouterState(); 8 + const pathname = routerState.location.pathname; 9 + const { data } = useSandboxQuery( 10 + `at:/${pathname.replace("/settings", "").replace("sandbox", "io.pocketenv.sandbox")}`, 11 + ); 12 + 4 13 return ( 5 - <Main> 6 - {/* Your page content goes here */} 7 - <></> 14 + <Main 15 + sidebar={<Sidebar />} 16 + root={data?.sandbox?.name} 17 + rootLink={pathname.replace("/settings", "")} 18 + > 19 + <> 20 + <div className="form-control w-[95%] m-auto"> 21 + <label className="label"> 22 + <span className="label-text font-bold mb-1 text-[14px]">Name</span> 23 + </label> 24 + <div className="input input-bordered w-md input-lg text-[15px] font-semibold bg-transparent"> 25 + <input 26 + type="text" 27 + className={`grow`} 28 + autoComplete="off" 29 + data-1p-ignore 30 + data-lpignore="true" 31 + data-form-type="other" 32 + /> 33 + </div> 34 + <div className="mt-8"> 35 + <label className="label"> 36 + <span className="label-text font-bold mb-1 text-[14px]"> 37 + Description 38 + </span> 39 + </label> 40 + <textarea 41 + className={`textarea max-w-full h-[150px] text-[14px] font-semibold`} 42 + aria-label="Textarea" 43 + ></textarea> 44 + </div> 45 + <div className="mt-8"> 46 + <label className="label"> 47 + <span className="label-text font-bold mb-1 text-[14px]"> 48 + Topics 49 + </span> 50 + </label> 51 + <span className="ml-1.25 opacity-50"> 52 + List of topics separated by spaces. 53 + </span> 54 + <textarea 55 + className={`textarea max-w-full h-25 text-[14px] font-semibold mt-2`} 56 + aria-label="Textarea" 57 + ></textarea> 58 + </div> 59 + <div className="mt-8"> 60 + <button className="btn btn-primary w-25">Save</button> 61 + </div> 62 + </div> 63 + </> 8 64 </Main> 9 65 ); 10 66 }
+25
apps/web/src/pages/settings/files/Files.tsx
··· 1 + import { useRouterState } from "@tanstack/react-router"; 2 + import { useSandboxQuery } from "../../../hooks/useSandbox"; 3 + import Main from "../../../layouts/Main"; 4 + import Sidebar from "../sidebar/Sidebar"; 5 + 6 + function Files() { 7 + const routerState = useRouterState(); 8 + const pathname = routerState.location.pathname; 9 + const { data } = useSandboxQuery( 10 + `at:/${pathname.replace("/files", "").replace("sandbox", "io.pocketenv.sandbox")}`, 11 + ); 12 + 13 + return ( 14 + <Main 15 + sidebar={<Sidebar />} 16 + root={data?.sandbox?.name} 17 + rootLink={pathname.replace("/files", "")} 18 + > 19 + {/* Your page content goes here */} 20 + <></> 21 + </Main> 22 + ); 23 + } 24 + 25 + export default Files;
+3
apps/web/src/pages/settings/files/index.tsx
··· 1 + import Files from "./Files"; 2 + 3 + export default Files;
+25
apps/web/src/pages/settings/integrations/Integrations.tsx
··· 1 + import { useRouterState } from "@tanstack/react-router"; 2 + import { useSandboxQuery } from "../../../hooks/useSandbox"; 3 + import Main from "../../../layouts/Main"; 4 + import Sidebar from "../sidebar/Sidebar"; 5 + 6 + function Integrations() { 7 + const routerState = useRouterState(); 8 + const pathname = routerState.location.pathname; 9 + const { data } = useSandboxQuery( 10 + `at:/${pathname.replace("/integrations", "").replace("sandbox", "io.pocketenv.sandbox")}`, 11 + ); 12 + 13 + return ( 14 + <Main 15 + sidebar={<Sidebar />} 16 + root={data?.sandbox?.name} 17 + rootLink={pathname.replace("/integrations", "")} 18 + > 19 + {/* Your page content goes here */} 20 + <></> 21 + </Main> 22 + ); 23 + } 24 + 25 + export default Integrations;
+3
apps/web/src/pages/settings/integrations/index.tsx
··· 1 + import Integrations from "./Integrations"; 2 + 3 + export default Integrations;
+25
apps/web/src/pages/settings/repository/Repository.tsx
··· 1 + import { useRouterState } from "@tanstack/react-router"; 2 + import { useSandboxQuery } from "../../../hooks/useSandbox"; 3 + import Main from "../../../layouts/Main"; 4 + import Sidebar from "../sidebar/Sidebar"; 5 + 6 + function Repository() { 7 + const routerState = useRouterState(); 8 + const pathname = routerState.location.pathname; 9 + const { data } = useSandboxQuery( 10 + `at:/${pathname.replace("/repository", "").replace("sandbox", "io.pocketenv.sandbox")}`, 11 + ); 12 + 13 + return ( 14 + <Main 15 + sidebar={<Sidebar />} 16 + root={data?.sandbox?.name} 17 + rootLink={pathname.replace("/repository", "")} 18 + > 19 + {/* Your page content goes here */} 20 + <></> 21 + </Main> 22 + ); 23 + } 24 + 25 + export default Repository;
+3
apps/web/src/pages/settings/repository/index.tsx
··· 1 + import Repository from "./Repository"; 2 + 3 + export default Repository;
+25
apps/web/src/pages/settings/secrets/Secrets.tsx
··· 1 + import { useRouterState } from "@tanstack/react-router"; 2 + import { useSandboxQuery } from "../../../hooks/useSandbox"; 3 + import Main from "../../../layouts/Main"; 4 + import Sidebar from "../sidebar/Sidebar"; 5 + 6 + function Secrets() { 7 + const routerState = useRouterState(); 8 + const pathname = routerState.location.pathname; 9 + const { data } = useSandboxQuery( 10 + `at:/${pathname.replace("/secrets", "").replace("sandbox", "io.pocketenv.sandbox")}`, 11 + ); 12 + 13 + return ( 14 + <Main 15 + sidebar={<Sidebar />} 16 + root={data?.sandbox?.name} 17 + rootLink={pathname.replace("/secrets", "")} 18 + > 19 + {/* Your page content goes here */} 20 + <></> 21 + </Main> 22 + ); 23 + } 24 + 25 + export default Secrets;
+3
apps/web/src/pages/settings/secrets/index.tsx
··· 1 + import Secrets from "./Secrets"; 2 + 3 + export default Secrets;
+193
apps/web/src/pages/settings/sidebar/Sidebar.tsx
··· 1 + import { Link, useRouterState } from "@tanstack/react-router"; 2 + import { useAtom } from "jotai"; 3 + import { sidebarCollapsedAtom } from "../../../atoms/sidebar"; 4 + import Logo from "../../../assets/logo.png"; 5 + 6 + function Sidebar() { 7 + const routerState = useRouterState(); 8 + const pathname = routerState.location.pathname; 9 + const [isCollapsed, setIsCollapsed] = useAtom(sidebarCollapsedAtom); 10 + const did = pathname.split("/")[1]; 11 + const rkey = pathname.split("/")[3]; 12 + 13 + const toggleSidebar = () => { 14 + setIsCollapsed((prev) => !prev); 15 + }; 16 + 17 + // Check if a route is active 18 + const isActive = (path: string): boolean => { 19 + return pathname.split("/")[4] === path.split("/")[1]; 20 + }; 21 + 22 + return ( 23 + <div> 24 + <aside 25 + className={`fixed z-50 h-screen bg-base-100 ${ 26 + isCollapsed ? "w-[72px]" : "w-64" 27 + }`} 28 + role="dialog" 29 + tabIndex={-1} 30 + > 31 + <div className="drawer-body px-2 pt-4 bg-base-100 overflow-hidden"> 32 + <div className="flex items-center"> 33 + {!isCollapsed && ( 34 + <div className="flex-1"> 35 + <Link to="/projects"> 36 + <div className="mb-[30px] ml-[5px]"> 37 + <img src={Logo} className="max-h-[40px] mr-[15px]" /> 38 + </div> 39 + </Link> 40 + </div> 41 + )} 42 + <button 43 + className={`mb-[25px] opacity-70 hover:opacity-100 ${ 44 + isCollapsed ? "mx-auto mt-2" : "" 45 + }`} 46 + onClick={toggleSidebar} 47 + aria-label={isCollapsed ? "Expand sidebar" : "Collapse sidebar"} 48 + > 49 + <span 50 + className={`icon-[hugeicons--panel-left] size-5.5 text-white ${ 51 + isCollapsed ? "rotate-180" : "" 52 + }`} 53 + ></span> 54 + </button> 55 + </div> 56 + <ul className="menu p-0"> 57 + <li> 58 + <Link 59 + to={`/${did}/sandbox/${rkey}/settings`} 60 + className={`${ 61 + isActive("/settings") 62 + ? "active bg-white/7 text-[#00e8c6]! font-semibold rounded-full" 63 + : "rounded-full hover:text-white" 64 + } ${isCollapsed ? "justify-center px-2" : ""}`} 65 + title={isCollapsed ? "General" : undefined} 66 + > 67 + <span 68 + className={`icon-[pepicons-pencil--file] size-6 ${isCollapsed ? "" : "mr-2"}`} 69 + ></span> 70 + {!isCollapsed && "General"} 71 + </Link> 72 + </li> 73 + <li> 74 + <Link 75 + to={`/${did}/sandbox/${rkey}/repository`} 76 + className={`${ 77 + isActive("/repository") 78 + ? "active bg-white/7 text-[#00e8c6]! font-semibold rounded-full" 79 + : "rounded-full hover:text-white" 80 + } ${isCollapsed ? "justify-center px-2" : ""}`} 81 + title={isCollapsed ? "Git Repository" : undefined} 82 + > 83 + <span 84 + className={`icon-[lucide--folder-git] size-6 ${isCollapsed ? "" : "mr-2"}`} 85 + ></span> 86 + {!isCollapsed && "Git Repository"} 87 + </Link> 88 + </li> 89 + <li> 90 + <Link 91 + to={`/${did}/sandbox/${rkey}/integrations`} 92 + className={`${ 93 + isActive("/integrations") 94 + ? "active bg-white/7 text-[#00e8c6]! font-semibold rounded-full" 95 + : "rounded-full hover:text-white" 96 + } ${isCollapsed ? "justify-center px-2" : ""}`} 97 + title={isCollapsed ? "Integrations" : undefined} 98 + > 99 + <span 100 + className={`icon-[hugeicons--webhook] size-6 ${isCollapsed ? "" : "mr-2"}`} 101 + ></span> 102 + {!isCollapsed && "Integrations"} 103 + </Link> 104 + </li> 105 + <li> 106 + <Link 107 + to={`/${did}/sandbox/${rkey}/variables`} 108 + className={`${ 109 + isActive("/variables") 110 + ? "active bg-white/7 text-[#00e8c6]! font-semibold rounded-full" 111 + : "rounded-full hover:text-white" 112 + } ${isCollapsed ? "justify-center px-2" : ""}`} 113 + title={isCollapsed ? "Secrets" : undefined} 114 + > 115 + <span 116 + className={`icon-[hugeicons--bash] size-6 ${isCollapsed ? "" : "mr-2"}`} 117 + ></span> 118 + {!isCollapsed && "Variables"} 119 + </Link> 120 + </li> 121 + <li> 122 + <Link 123 + to={`/${did}/sandbox/${rkey}/secrets`} 124 + className={`${ 125 + isActive("/secrets") 126 + ? "active bg-white/7 text-[#00e8c6]! font-semibold rounded-full" 127 + : "rounded-full hover:text-white" 128 + } ${isCollapsed ? "justify-center px-2" : ""}`} 129 + title={isCollapsed ? "Secrets" : undefined} 130 + > 131 + <span 132 + className={`icon-[material-symbols--lock-outline] size-6 ${isCollapsed ? "" : "mr-2"}`} 133 + ></span> 134 + {!isCollapsed && "Secrets"} 135 + </Link> 136 + </li> 137 + <li> 138 + <Link 139 + to={`/${did}/sandbox/${rkey}/files`} 140 + className={`${ 141 + isActive("/files") 142 + ? "active bg-white/7 text-[#00e8c6]! font-semibold rounded-full" 143 + : "rounded-full hover:text-white" 144 + } ${isCollapsed ? "justify-center px-2" : ""}`} 145 + title={isCollapsed ? "Files" : undefined} 146 + > 147 + <span 148 + className={`icon-[pepicons-pencil--file] size-6 ${isCollapsed ? "" : "mr-2"}`} 149 + ></span> 150 + {!isCollapsed && "Files"} 151 + </Link> 152 + </li> 153 + <li> 154 + <Link 155 + to={`/${did}/sandbox/${rkey}/volumes`} 156 + className={`${ 157 + isActive("/volumes") 158 + ? "active bg-white/7 text-[#00e8c6]! font-semibold rounded-full" 159 + : "rounded-full hover:text-white" 160 + } ${isCollapsed ? "justify-center px-2" : ""}`} 161 + title={isCollapsed ? "Volumes" : undefined} 162 + > 163 + <span 164 + className={`icon-[icon-park-outline--hard-disk] size-5 ${isCollapsed ? "" : "mr-2"}`} 165 + ></span> 166 + {!isCollapsed && "Volumes"} 167 + </Link> 168 + </li> 169 + <li> 170 + <Link 171 + to={`/${did}/sandbox/${rkey}/ssh-keys`} 172 + className={`${ 173 + isActive("/ssh-keys") 174 + ? "active bg-white/7 text-[#00e8c6]! font-semibold rounded-full" 175 + : "rounded-full hover:text-white" 176 + } ${isCollapsed ? "justify-center px-2" : ""}`} 177 + title={isCollapsed ? "SSH Keys" : undefined} 178 + > 179 + <span 180 + className={`icon-[tabler--key] size-6 ${isCollapsed ? "" : "mr-2"}`} 181 + ></span> 182 + {!isCollapsed && "SSH Keys"} 183 + </Link> 184 + </li> 185 + </ul> 186 + </div> 187 + </aside> 188 + <div id="custom-backdrop-container"></div> 189 + </div> 190 + ); 191 + } 192 + 193 + export default Sidebar;
apps/web/src/pages/settings/sidebar/index.tsx

This is a binary file and will not be displayed.

+25
apps/web/src/pages/settings/sshkeys/SshKeys.tsx
··· 1 + import { useRouterState } from "@tanstack/react-router"; 2 + import { useSandboxQuery } from "../../../hooks/useSandbox"; 3 + import Main from "../../../layouts/Main"; 4 + import Sidebar from "../sidebar/Sidebar"; 5 + 6 + function SshKeys() { 7 + const routerState = useRouterState(); 8 + const pathname = routerState.location.pathname; 9 + const { data } = useSandboxQuery( 10 + `at:/${pathname.replace("/ssh-keys", "").replace("sandbox", "io.pocketenv.sandbox")}`, 11 + ); 12 + 13 + return ( 14 + <Main 15 + sidebar={<Sidebar />} 16 + root={data?.sandbox?.name} 17 + rootLink={pathname.replace("/ssh-keys", "")} 18 + > 19 + {/* Your page content goes here */} 20 + <></> 21 + </Main> 22 + ); 23 + } 24 + 25 + export default SshKeys;
+3
apps/web/src/pages/settings/sshkeys/index.tsx
··· 1 + import SSHKeys from "./SSHKeys"; 2 + 3 + export default SSHKeys;
+25
apps/web/src/pages/settings/variables/Variables.tsx
··· 1 + import { useRouterState } from "@tanstack/react-router"; 2 + import { useSandboxQuery } from "../../../hooks/useSandbox"; 3 + import Main from "../../../layouts/Main"; 4 + import Sidebar from "../sidebar/Sidebar"; 5 + 6 + function Variables() { 7 + const routerState = useRouterState(); 8 + const pathname = routerState.location.pathname; 9 + const { data } = useSandboxQuery( 10 + `at:/${pathname.replace("/variables", "").replace("sandbox", "io.pocketenv.sandbox")}`, 11 + ); 12 + 13 + return ( 14 + <Main 15 + sidebar={<Sidebar />} 16 + root={data?.sandbox?.name} 17 + rootLink={pathname.replace("/variables", "")} 18 + > 19 + {/* Your page content goes here */} 20 + <></> 21 + </Main> 22 + ); 23 + } 24 + 25 + export default Variables;
+3
apps/web/src/pages/settings/variables/index.tsx
··· 1 + import Variables from "./Variables"; 2 + 3 + export default Variables;
+25
apps/web/src/pages/settings/volumes/Volumes.tsx
··· 1 + import { useRouterState } from "@tanstack/react-router"; 2 + import { useSandboxQuery } from "../../../hooks/useSandbox"; 3 + import Main from "../../../layouts/Main"; 4 + import Sidebar from "../sidebar/Sidebar"; 5 + 6 + function Volumes() { 7 + const routerState = useRouterState(); 8 + const pathname = routerState.location.pathname; 9 + const { data } = useSandboxQuery( 10 + `at:/${pathname.replace("/volumes", "").replace("sandbox", "io.pocketenv.sandbox")}`, 11 + ); 12 + 13 + return ( 14 + <Main 15 + sidebar={<Sidebar />} 16 + root={data?.sandbox?.name} 17 + rootLink={pathname.replace("/files", "")} 18 + > 19 + {/* Your page content goes here */} 20 + <></> 21 + </Main> 22 + ); 23 + } 24 + 25 + export default Volumes;
+3
apps/web/src/pages/settings/volumes/index.tsx
··· 1 + import Volumes from "./Volumes"; 2 + 3 + export default Volumes;
+204 -5
apps/web/src/routeTree.gen.ts
··· 18 18 import { Route as IndexRouteImport } from './routes/index' 19 19 import { Route as SandboxIdRouteImport } from './routes/sandbox/$id' 20 20 import { Route as DidSandboxRkeyRouteImport } from './routes/$did.sandbox.$rkey' 21 + import { Route as DidSandboxRkeyIndexRouteImport } from './routes/$did.sandbox.$rkey/index' 22 + import { Route as DidSandboxRkeyVolumesRouteImport } from './routes/$did.sandbox.$rkey/volumes' 23 + import { Route as DidSandboxRkeyVariablesRouteImport } from './routes/$did.sandbox.$rkey/variables' 24 + import { Route as DidSandboxRkeySshKeysRouteImport } from './routes/$did.sandbox.$rkey/ssh-keys' 25 + import { Route as DidSandboxRkeySettingsRouteImport } from './routes/$did.sandbox.$rkey/settings' 26 + import { Route as DidSandboxRkeySecretsRouteImport } from './routes/$did.sandbox.$rkey/secrets' 27 + import { Route as DidSandboxRkeyRepositoryRouteImport } from './routes/$did.sandbox.$rkey/repository' 28 + import { Route as DidSandboxRkeyIntegrationsRouteImport } from './routes/$did.sandbox.$rkey/integrations' 29 + import { Route as DidSandboxRkeyFilesRouteImport } from './routes/$did.sandbox.$rkey/files' 21 30 22 31 const VolumesRoute = VolumesRouteImport.update({ 23 32 id: '/volumes', ··· 64 73 path: '/$did/sandbox/$rkey', 65 74 getParentRoute: () => rootRouteImport, 66 75 } as any) 76 + const DidSandboxRkeyIndexRoute = DidSandboxRkeyIndexRouteImport.update({ 77 + id: '/', 78 + path: '/', 79 + getParentRoute: () => DidSandboxRkeyRoute, 80 + } as any) 81 + const DidSandboxRkeyVolumesRoute = DidSandboxRkeyVolumesRouteImport.update({ 82 + id: '/volumes', 83 + path: '/volumes', 84 + getParentRoute: () => DidSandboxRkeyRoute, 85 + } as any) 86 + const DidSandboxRkeyVariablesRoute = DidSandboxRkeyVariablesRouteImport.update({ 87 + id: '/variables', 88 + path: '/variables', 89 + getParentRoute: () => DidSandboxRkeyRoute, 90 + } as any) 91 + const DidSandboxRkeySshKeysRoute = DidSandboxRkeySshKeysRouteImport.update({ 92 + id: '/ssh-keys', 93 + path: '/ssh-keys', 94 + getParentRoute: () => DidSandboxRkeyRoute, 95 + } as any) 96 + const DidSandboxRkeySettingsRoute = DidSandboxRkeySettingsRouteImport.update({ 97 + id: '/settings', 98 + path: '/settings', 99 + getParentRoute: () => DidSandboxRkeyRoute, 100 + } as any) 101 + const DidSandboxRkeySecretsRoute = DidSandboxRkeySecretsRouteImport.update({ 102 + id: '/secrets', 103 + path: '/secrets', 104 + getParentRoute: () => DidSandboxRkeyRoute, 105 + } as any) 106 + const DidSandboxRkeyRepositoryRoute = 107 + DidSandboxRkeyRepositoryRouteImport.update({ 108 + id: '/repository', 109 + path: '/repository', 110 + getParentRoute: () => DidSandboxRkeyRoute, 111 + } as any) 112 + const DidSandboxRkeyIntegrationsRoute = 113 + DidSandboxRkeyIntegrationsRouteImport.update({ 114 + id: '/integrations', 115 + path: '/integrations', 116 + getParentRoute: () => DidSandboxRkeyRoute, 117 + } as any) 118 + const DidSandboxRkeyFilesRoute = DidSandboxRkeyFilesRouteImport.update({ 119 + id: '/files', 120 + path: '/files', 121 + getParentRoute: () => DidSandboxRkeyRoute, 122 + } as any) 67 123 68 124 export interface FileRoutesByFullPath { 69 125 '/': typeof IndexRoute ··· 74 130 '/snapshots': typeof SnapshotsRoute 75 131 '/volumes': typeof VolumesRoute 76 132 '/sandbox/$id': typeof SandboxIdRoute 77 - '/$did/sandbox/$rkey': typeof DidSandboxRkeyRoute 133 + '/$did/sandbox/$rkey': typeof DidSandboxRkeyRouteWithChildren 134 + '/$did/sandbox/$rkey/files': typeof DidSandboxRkeyFilesRoute 135 + '/$did/sandbox/$rkey/integrations': typeof DidSandboxRkeyIntegrationsRoute 136 + '/$did/sandbox/$rkey/repository': typeof DidSandboxRkeyRepositoryRoute 137 + '/$did/sandbox/$rkey/secrets': typeof DidSandboxRkeySecretsRoute 138 + '/$did/sandbox/$rkey/settings': typeof DidSandboxRkeySettingsRoute 139 + '/$did/sandbox/$rkey/ssh-keys': typeof DidSandboxRkeySshKeysRoute 140 + '/$did/sandbox/$rkey/variables': typeof DidSandboxRkeyVariablesRoute 141 + '/$did/sandbox/$rkey/volumes': typeof DidSandboxRkeyVolumesRoute 142 + '/$did/sandbox/$rkey/': typeof DidSandboxRkeyIndexRoute 78 143 } 79 144 export interface FileRoutesByTo { 80 145 '/': typeof IndexRoute ··· 85 150 '/snapshots': typeof SnapshotsRoute 86 151 '/volumes': typeof VolumesRoute 87 152 '/sandbox/$id': typeof SandboxIdRoute 88 - '/$did/sandbox/$rkey': typeof DidSandboxRkeyRoute 153 + '/$did/sandbox/$rkey/files': typeof DidSandboxRkeyFilesRoute 154 + '/$did/sandbox/$rkey/integrations': typeof DidSandboxRkeyIntegrationsRoute 155 + '/$did/sandbox/$rkey/repository': typeof DidSandboxRkeyRepositoryRoute 156 + '/$did/sandbox/$rkey/secrets': typeof DidSandboxRkeySecretsRoute 157 + '/$did/sandbox/$rkey/settings': typeof DidSandboxRkeySettingsRoute 158 + '/$did/sandbox/$rkey/ssh-keys': typeof DidSandboxRkeySshKeysRoute 159 + '/$did/sandbox/$rkey/variables': typeof DidSandboxRkeyVariablesRoute 160 + '/$did/sandbox/$rkey/volumes': typeof DidSandboxRkeyVolumesRoute 161 + '/$did/sandbox/$rkey': typeof DidSandboxRkeyIndexRoute 89 162 } 90 163 export interface FileRoutesById { 91 164 __root__: typeof rootRouteImport ··· 97 170 '/snapshots': typeof SnapshotsRoute 98 171 '/volumes': typeof VolumesRoute 99 172 '/sandbox/$id': typeof SandboxIdRoute 100 - '/$did/sandbox/$rkey': typeof DidSandboxRkeyRoute 173 + '/$did/sandbox/$rkey': typeof DidSandboxRkeyRouteWithChildren 174 + '/$did/sandbox/$rkey/files': typeof DidSandboxRkeyFilesRoute 175 + '/$did/sandbox/$rkey/integrations': typeof DidSandboxRkeyIntegrationsRoute 176 + '/$did/sandbox/$rkey/repository': typeof DidSandboxRkeyRepositoryRoute 177 + '/$did/sandbox/$rkey/secrets': typeof DidSandboxRkeySecretsRoute 178 + '/$did/sandbox/$rkey/settings': typeof DidSandboxRkeySettingsRoute 179 + '/$did/sandbox/$rkey/ssh-keys': typeof DidSandboxRkeySshKeysRoute 180 + '/$did/sandbox/$rkey/variables': typeof DidSandboxRkeyVariablesRoute 181 + '/$did/sandbox/$rkey/volumes': typeof DidSandboxRkeyVolumesRoute 182 + '/$did/sandbox/$rkey/': typeof DidSandboxRkeyIndexRoute 101 183 } 102 184 export interface FileRouteTypes { 103 185 fileRoutesByFullPath: FileRoutesByFullPath ··· 111 193 | '/volumes' 112 194 | '/sandbox/$id' 113 195 | '/$did/sandbox/$rkey' 196 + | '/$did/sandbox/$rkey/files' 197 + | '/$did/sandbox/$rkey/integrations' 198 + | '/$did/sandbox/$rkey/repository' 199 + | '/$did/sandbox/$rkey/secrets' 200 + | '/$did/sandbox/$rkey/settings' 201 + | '/$did/sandbox/$rkey/ssh-keys' 202 + | '/$did/sandbox/$rkey/variables' 203 + | '/$did/sandbox/$rkey/volumes' 204 + | '/$did/sandbox/$rkey/' 114 205 fileRoutesByTo: FileRoutesByTo 115 206 to: 116 207 | '/' ··· 121 212 | '/snapshots' 122 213 | '/volumes' 123 214 | '/sandbox/$id' 215 + | '/$did/sandbox/$rkey/files' 216 + | '/$did/sandbox/$rkey/integrations' 217 + | '/$did/sandbox/$rkey/repository' 218 + | '/$did/sandbox/$rkey/secrets' 219 + | '/$did/sandbox/$rkey/settings' 220 + | '/$did/sandbox/$rkey/ssh-keys' 221 + | '/$did/sandbox/$rkey/variables' 222 + | '/$did/sandbox/$rkey/volumes' 124 223 | '/$did/sandbox/$rkey' 125 224 id: 126 225 | '__root__' ··· 133 232 | '/volumes' 134 233 | '/sandbox/$id' 135 234 | '/$did/sandbox/$rkey' 235 + | '/$did/sandbox/$rkey/files' 236 + | '/$did/sandbox/$rkey/integrations' 237 + | '/$did/sandbox/$rkey/repository' 238 + | '/$did/sandbox/$rkey/secrets' 239 + | '/$did/sandbox/$rkey/settings' 240 + | '/$did/sandbox/$rkey/ssh-keys' 241 + | '/$did/sandbox/$rkey/variables' 242 + | '/$did/sandbox/$rkey/volumes' 243 + | '/$did/sandbox/$rkey/' 136 244 fileRoutesById: FileRoutesById 137 245 } 138 246 export interface RootRouteChildren { ··· 144 252 SnapshotsRoute: typeof SnapshotsRoute 145 253 VolumesRoute: typeof VolumesRoute 146 254 SandboxIdRoute: typeof SandboxIdRoute 147 - DidSandboxRkeyRoute: typeof DidSandboxRkeyRoute 255 + DidSandboxRkeyRoute: typeof DidSandboxRkeyRouteWithChildren 148 256 } 149 257 150 258 declare module '@tanstack/react-router' { ··· 212 320 preLoaderRoute: typeof DidSandboxRkeyRouteImport 213 321 parentRoute: typeof rootRouteImport 214 322 } 323 + '/$did/sandbox/$rkey/': { 324 + id: '/$did/sandbox/$rkey/' 325 + path: '/' 326 + fullPath: '/$did/sandbox/$rkey/' 327 + preLoaderRoute: typeof DidSandboxRkeyIndexRouteImport 328 + parentRoute: typeof DidSandboxRkeyRoute 329 + } 330 + '/$did/sandbox/$rkey/volumes': { 331 + id: '/$did/sandbox/$rkey/volumes' 332 + path: '/volumes' 333 + fullPath: '/$did/sandbox/$rkey/volumes' 334 + preLoaderRoute: typeof DidSandboxRkeyVolumesRouteImport 335 + parentRoute: typeof DidSandboxRkeyRoute 336 + } 337 + '/$did/sandbox/$rkey/variables': { 338 + id: '/$did/sandbox/$rkey/variables' 339 + path: '/variables' 340 + fullPath: '/$did/sandbox/$rkey/variables' 341 + preLoaderRoute: typeof DidSandboxRkeyVariablesRouteImport 342 + parentRoute: typeof DidSandboxRkeyRoute 343 + } 344 + '/$did/sandbox/$rkey/ssh-keys': { 345 + id: '/$did/sandbox/$rkey/ssh-keys' 346 + path: '/ssh-keys' 347 + fullPath: '/$did/sandbox/$rkey/ssh-keys' 348 + preLoaderRoute: typeof DidSandboxRkeySshKeysRouteImport 349 + parentRoute: typeof DidSandboxRkeyRoute 350 + } 351 + '/$did/sandbox/$rkey/settings': { 352 + id: '/$did/sandbox/$rkey/settings' 353 + path: '/settings' 354 + fullPath: '/$did/sandbox/$rkey/settings' 355 + preLoaderRoute: typeof DidSandboxRkeySettingsRouteImport 356 + parentRoute: typeof DidSandboxRkeyRoute 357 + } 358 + '/$did/sandbox/$rkey/secrets': { 359 + id: '/$did/sandbox/$rkey/secrets' 360 + path: '/secrets' 361 + fullPath: '/$did/sandbox/$rkey/secrets' 362 + preLoaderRoute: typeof DidSandboxRkeySecretsRouteImport 363 + parentRoute: typeof DidSandboxRkeyRoute 364 + } 365 + '/$did/sandbox/$rkey/repository': { 366 + id: '/$did/sandbox/$rkey/repository' 367 + path: '/repository' 368 + fullPath: '/$did/sandbox/$rkey/repository' 369 + preLoaderRoute: typeof DidSandboxRkeyRepositoryRouteImport 370 + parentRoute: typeof DidSandboxRkeyRoute 371 + } 372 + '/$did/sandbox/$rkey/integrations': { 373 + id: '/$did/sandbox/$rkey/integrations' 374 + path: '/integrations' 375 + fullPath: '/$did/sandbox/$rkey/integrations' 376 + preLoaderRoute: typeof DidSandboxRkeyIntegrationsRouteImport 377 + parentRoute: typeof DidSandboxRkeyRoute 378 + } 379 + '/$did/sandbox/$rkey/files': { 380 + id: '/$did/sandbox/$rkey/files' 381 + path: '/files' 382 + fullPath: '/$did/sandbox/$rkey/files' 383 + preLoaderRoute: typeof DidSandboxRkeyFilesRouteImport 384 + parentRoute: typeof DidSandboxRkeyRoute 385 + } 215 386 } 216 387 } 217 388 389 + interface DidSandboxRkeyRouteChildren { 390 + DidSandboxRkeyFilesRoute: typeof DidSandboxRkeyFilesRoute 391 + DidSandboxRkeyIntegrationsRoute: typeof DidSandboxRkeyIntegrationsRoute 392 + DidSandboxRkeyRepositoryRoute: typeof DidSandboxRkeyRepositoryRoute 393 + DidSandboxRkeySecretsRoute: typeof DidSandboxRkeySecretsRoute 394 + DidSandboxRkeySettingsRoute: typeof DidSandboxRkeySettingsRoute 395 + DidSandboxRkeySshKeysRoute: typeof DidSandboxRkeySshKeysRoute 396 + DidSandboxRkeyVariablesRoute: typeof DidSandboxRkeyVariablesRoute 397 + DidSandboxRkeyVolumesRoute: typeof DidSandboxRkeyVolumesRoute 398 + DidSandboxRkeyIndexRoute: typeof DidSandboxRkeyIndexRoute 399 + } 400 + 401 + const DidSandboxRkeyRouteChildren: DidSandboxRkeyRouteChildren = { 402 + DidSandboxRkeyFilesRoute: DidSandboxRkeyFilesRoute, 403 + DidSandboxRkeyIntegrationsRoute: DidSandboxRkeyIntegrationsRoute, 404 + DidSandboxRkeyRepositoryRoute: DidSandboxRkeyRepositoryRoute, 405 + DidSandboxRkeySecretsRoute: DidSandboxRkeySecretsRoute, 406 + DidSandboxRkeySettingsRoute: DidSandboxRkeySettingsRoute, 407 + DidSandboxRkeySshKeysRoute: DidSandboxRkeySshKeysRoute, 408 + DidSandboxRkeyVariablesRoute: DidSandboxRkeyVariablesRoute, 409 + DidSandboxRkeyVolumesRoute: DidSandboxRkeyVolumesRoute, 410 + DidSandboxRkeyIndexRoute: DidSandboxRkeyIndexRoute, 411 + } 412 + 413 + const DidSandboxRkeyRouteWithChildren = DidSandboxRkeyRoute._addFileChildren( 414 + DidSandboxRkeyRouteChildren, 415 + ) 416 + 218 417 const rootRouteChildren: RootRouteChildren = { 219 418 IndexRoute: IndexRoute, 220 419 ProjectsRoute: ProjectsRoute, ··· 224 423 SnapshotsRoute: SnapshotsRoute, 225 424 VolumesRoute: VolumesRoute, 226 425 SandboxIdRoute: SandboxIdRoute, 227 - DidSandboxRkeyRoute: DidSandboxRkeyRoute, 426 + DidSandboxRkeyRoute: DidSandboxRkeyRouteWithChildren, 228 427 } 229 428 export const routeTree = rootRouteImport 230 429 ._addFileChildren(rootRouteChildren)
+6 -3
apps/web/src/routes/$did.sandbox.$rkey.tsx
··· 1 - import { createFileRoute } from "@tanstack/react-router"; 2 - import Sandbox from "../pages/sandbox"; 1 + import { createFileRoute, Outlet } from "@tanstack/react-router"; 3 2 4 3 export const Route = createFileRoute("/$did/sandbox/$rkey")({ 5 - component: Sandbox, 4 + component: SandboxLayout, 6 5 }); 6 + 7 + function SandboxLayout() { 8 + return <Outlet />; 9 + }
+13
apps/web/src/routes/$did.sandbox.$rkey/files.tsx
··· 1 + import { createFileRoute, redirect } from "@tanstack/react-router"; 2 + import FilesPage from "../../pages/settings/files"; 3 + 4 + export const Route = createFileRoute("/$did/sandbox/$rkey/files")({ 5 + beforeLoad: () => { 6 + const isAuthenticated = !!localStorage.getItem("token"); 7 + 8 + if (!isAuthenticated) { 9 + throw redirect({ to: "/" }); 10 + } 11 + }, 12 + component: FilesPage, 13 + });
+6
apps/web/src/routes/$did.sandbox.$rkey/index.tsx
··· 1 + import { createFileRoute } from "@tanstack/react-router"; 2 + import Sandbox from "../../pages/sandbox"; 3 + 4 + export const Route = createFileRoute("/$did/sandbox/$rkey/")({ 5 + component: Sandbox, 6 + });
+13
apps/web/src/routes/$did.sandbox.$rkey/integrations.tsx
··· 1 + import { createFileRoute, redirect } from "@tanstack/react-router"; 2 + import IntegrationsPage from "../../pages/settings/integrations"; 3 + 4 + export const Route = createFileRoute("/$did/sandbox/$rkey/integrations")({ 5 + beforeLoad: () => { 6 + const isAuthenticated = !!localStorage.getItem("token"); 7 + 8 + if (!isAuthenticated) { 9 + throw redirect({ to: "/" }); 10 + } 11 + }, 12 + component: IntegrationsPage, 13 + });
+13
apps/web/src/routes/$did.sandbox.$rkey/repository.tsx
··· 1 + import { createFileRoute, redirect } from "@tanstack/react-router"; 2 + import RepositoryPage from "../../pages/settings/repository"; 3 + 4 + export const Route = createFileRoute("/$did/sandbox/$rkey/repository")({ 5 + beforeLoad: () => { 6 + const isAuthenticated = !!localStorage.getItem("token"); 7 + 8 + if (!isAuthenticated) { 9 + throw redirect({ to: "/" }); 10 + } 11 + }, 12 + component: RepositoryPage, 13 + });
+13
apps/web/src/routes/$did.sandbox.$rkey/secrets.tsx
··· 1 + import { createFileRoute, redirect } from "@tanstack/react-router"; 2 + import SecretsPage from "../../pages/settings/secrets"; 3 + 4 + export const Route = createFileRoute("/$did/sandbox/$rkey/secrets")({ 5 + beforeLoad: () => { 6 + const isAuthenticated = !!localStorage.getItem("token"); 7 + 8 + if (!isAuthenticated) { 9 + throw redirect({ to: "/" }); 10 + } 11 + }, 12 + component: SecretsPage, 13 + });
+13
apps/web/src/routes/$did.sandbox.$rkey/settings.tsx
··· 1 + import { createFileRoute, redirect } from "@tanstack/react-router"; 2 + import SettingsPage from "../../pages/settings"; 3 + 4 + export const Route = createFileRoute("/$did/sandbox/$rkey/settings")({ 5 + beforeLoad: () => { 6 + const isAuthenticated = !!localStorage.getItem("token"); 7 + 8 + if (!isAuthenticated) { 9 + throw redirect({ to: "/" }); 10 + } 11 + }, 12 + component: SettingsPage, 13 + });
+13
apps/web/src/routes/$did.sandbox.$rkey/ssh-keys.tsx
··· 1 + import { createFileRoute, redirect } from "@tanstack/react-router"; 2 + import SshKeysPage from "../../pages/settings/sshkeys/SshKeys"; 3 + 4 + export const Route = createFileRoute("/$did/sandbox/$rkey/ssh-keys")({ 5 + beforeLoad: () => { 6 + const isAuthenticated = !!localStorage.getItem("token"); 7 + 8 + if (!isAuthenticated) { 9 + throw redirect({ to: "/" }); 10 + } 11 + }, 12 + component: SshKeysPage, 13 + });
+13
apps/web/src/routes/$did.sandbox.$rkey/variables.tsx
··· 1 + import { createFileRoute, redirect } from "@tanstack/react-router"; 2 + import VariablesPage from "../../pages/settings/variables"; 3 + 4 + export const Route = createFileRoute("/$did/sandbox/$rkey/variables")({ 5 + beforeLoad: () => { 6 + const isAuthenticated = !!localStorage.getItem("token"); 7 + 8 + if (!isAuthenticated) { 9 + throw redirect({ to: "/" }); 10 + } 11 + }, 12 + component: VariablesPage, 13 + });
+13
apps/web/src/routes/$did.sandbox.$rkey/volumes.tsx
··· 1 + import { createFileRoute, redirect } from "@tanstack/react-router"; 2 + import VolumesPage from "../../pages/settings/volumes"; 3 + 4 + export const Route = createFileRoute("/$did/sandbox/$rkey/volumes")({ 5 + beforeLoad: () => { 6 + const isAuthenticated = !!localStorage.getItem("token"); 7 + 8 + if (!isAuthenticated) { 9 + throw redirect({ to: "/" }); 10 + } 11 + }, 12 + component: VolumesPage, 13 + });