ui improvements

This commit is contained in:
Ishaan Dey 2024-05-05 00:06:10 -07:00
parent 159e7b62e2
commit 5ae5c226ba
7 changed files with 105 additions and 26 deletions

View File

@ -52,7 +52,7 @@ export default async function CodePage({ params }: { params: { id: string } }) {
<Room id={sandboxId}> <Room id={sandboxId}>
<Navbar userData={userData} sandboxData={sandboxData} shared={shared} /> <Navbar userData={userData} sandboxData={sandboxData} shared={shared} />
<div className="w-screen flex grow"> <div className="w-screen flex grow">
<CodeEditor userData={userData} sandboxId={sandboxId} /> <CodeEditor userData={userData} sandboxData={sandboxData} />
</div> </div>
</Room> </Room>
</div> </div>

View File

@ -0,0 +1,39 @@
"use client"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import Image from "next/image"
import { useState } from "react"
import { Button } from "../ui/button"
import { ChevronRight } from "lucide-react"
export default function AboutModal({
open,
setOpen,
}: {
open: boolean
setOpen: (open: boolean) => void
}) {
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>About this project</DialogTitle>
</DialogHeader>
<div className="text-sm text-muted-foreground">
Sandbox is an open-source cloud-based code editing environment with
custom AI code autocompletion and real-time collaboration. The
infrastructure runs on Docker and Kubernetes to scale automatically
based on resource usage.
</div>
</DialogContent>
</Dialog>
)
}

View File

@ -17,6 +17,7 @@ import DashboardSharedWithMe from "./shared"
import NewProjectModal from "./newProject" import NewProjectModal from "./newProject"
import Link from "next/link" import Link from "next/link"
import { useSearchParams } from "next/navigation" import { useSearchParams } from "next/navigation"
import AboutModal from "./about"
type TScreen = "projects" | "shared" | "settings" | "search" type TScreen = "projects" | "shared" | "settings" | "search"
@ -36,6 +37,7 @@ export default function Dashboard({
const [screen, setScreen] = useState<TScreen>("projects") const [screen, setScreen] = useState<TScreen>("projects")
const [newProjectModalOpen, setNewProjectModalOpen] = useState(false) const [newProjectModalOpen, setNewProjectModalOpen] = useState(false)
const [aboutModalOpen, setAboutModalOpen] = useState(false)
const activeScreen = (s: TScreen) => { const activeScreen = (s: TScreen) => {
if (screen === s) return "justify-start" if (screen === s) return "justify-start"
@ -51,6 +53,7 @@ export default function Dashboard({
open={newProjectModalOpen} open={newProjectModalOpen}
setOpen={setNewProjectModalOpen} setOpen={setNewProjectModalOpen}
/> />
<AboutModal open={aboutModalOpen} setOpen={setAboutModalOpen} />
<div className="flex grow w-full"> <div className="flex grow w-full">
<div className="w-56 shrink-0 border-r border-border p-4 justify-between flex flex-col"> <div className="w-56 shrink-0 border-r border-border p-4 justify-between flex flex-col">
<div className="flex flex-col"> <div className="flex flex-col">
@ -97,6 +100,7 @@ export default function Dashboard({
</Button> </Button>
</Link> </Link>
<Button <Button
onClick={() => setAboutModalOpen(true)}
variant="ghost" variant="ghost"
className="justify-start font-normal text-muted-foreground" className="justify-start font-normal text-muted-foreground"
> >

View File

@ -12,6 +12,7 @@ import Image from "next/image"
import Button from "../ui/customButton" import Button from "../ui/customButton"
import { ChevronRight } from "lucide-react" import { ChevronRight } from "lucide-react"
import Avatar from "../ui/avatar" import Avatar from "../ui/avatar"
import Link from "next/link"
export default function DashboardSharedWithMe({ export default function DashboardSharedWithMe({
shared, shared,
@ -66,9 +67,11 @@ export default function DashboardSharedWithMe({
{new Date(sandbox.sharedOn).toLocaleDateString()} {new Date(sandbox.sharedOn).toLocaleDateString()}
</TableCell> </TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<Button> <Link href={`/code/${sandbox.id}`}>
Open <ChevronRight className="w-4 h-4 ml-2" /> <Button>
</Button> Open <ChevronRight className="w-4 h-4 ml-2" />
</Button>
</Link>
</TableCell> </TableCell>
</TableRow> </TableRow>
))} ))}

View File

@ -39,16 +39,16 @@ import EditorTerminal from "./terminal"
import { Button } from "../ui/button" import { Button } from "../ui/button"
import GenerateInput from "./generate" import GenerateInput from "./generate"
import { TFile, TFileData, TFolder, TTab } from "./sidebar/types" import { TFile, TFileData, TFolder, TTab } from "./sidebar/types"
import { User } from "@/lib/types" import { Sandbox, User } from "@/lib/types"
import { processFileType, validateName } from "@/lib/utils" import { processFileType, validateName } from "@/lib/utils"
import { Cursors } from "./live/cursors" import { Cursors } from "./live/cursors"
export default function CodeEditor({ export default function CodeEditor({
userData, userData,
sandboxId, sandboxData,
}: { }: {
userData: User userData: User
sandboxId: string sandboxData: Sandbox
}) { }) {
const [files, setFiles] = useState<(TFolder | TFile)[]>([]) const [files, setFiles] = useState<(TFolder | TFile)[]>([])
const [tabs, setTabs] = useState<TTab[]>([]) const [tabs, setTabs] = useState<TTab[]>([])
@ -72,6 +72,7 @@ export default function CodeEditor({
const [provider, setProvider] = useState<TypedLiveblocksProvider>() const [provider, setProvider] = useState<TypedLiveblocksProvider>()
const [ai, setAi] = useState(false) const [ai, setAi] = useState(false)
const isOwner = sandboxData.userId === userData.id
const clerk = useClerk() const clerk = useClerk()
const room = useRoom() const room = useRoom()
@ -247,7 +248,7 @@ export default function CodeEditor({
}, [decorations.options]) }, [decorations.options])
const socket = io( const socket = io(
`http://localhost:4000?userId=${userData.id}&sandboxId=${sandboxId}` `http://localhost:4000?userId=${userData.id}&sandboxId=${sandboxData.id}`
) )
useEffect(() => { useEffect(() => {
@ -506,7 +507,7 @@ export default function CodeEditor({
if (type === "file") { if (type === "file") {
setFiles((prev) => [ setFiles((prev) => [
...prev, ...prev,
{ id: `projects/${sandboxId}/${name}`, name, type: "file" }, { id: `projects/${sandboxData.id}/${name}`, name, type: "file" },
]) ])
} else { } else {
console.log("adding folder") console.log("adding folder")

View File

@ -27,6 +27,8 @@ export default function Navbar({
const [isEditOpen, setIsEditOpen] = useState(false) const [isEditOpen, setIsEditOpen] = useState(false)
const [isShareOpen, setIsShareOpen] = useState(false) const [isShareOpen, setIsShareOpen] = useState(false)
const isOwner = sandboxData.userId === userData.id
return ( return (
<> <>
<EditSandboxModal <EditSandboxModal
@ -50,21 +52,25 @@ export default function Navbar({
</Link> </Link>
<div className="text-sm font-medium flex items-center"> <div className="text-sm font-medium flex items-center">
{sandboxData.name} {sandboxData.name}
<button {isOwner ? (
onClick={() => setIsEditOpen(true)} <button
className="h-7 w-7 ml-2 flex items-center justify-center bg-transparent hover:bg-muted-foreground/25 cursor-pointer rounded-md ring-offset-2 transition-all ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" onClick={() => setIsEditOpen(true)}
> className="h-7 w-7 ml-2 flex items-center justify-center bg-transparent hover:bg-muted-foreground/25 cursor-pointer rounded-md ring-offset-2 transition-all ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
<Pencil className="w-4 h-4" /> >
</button> <Pencil className="w-4 h-4" />
</button>
) : null}
</div> </div>
</div> </div>
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
<Avatars /> <Avatars />
<Button variant="outline" onClick={() => setIsShareOpen(true)}> {isOwner ? (
<Users className="w-4 h-4 mr-2" /> <Button variant="outline" onClick={() => setIsShareOpen(true)}>
Share <Users className="w-4 h-4 mr-2" />
</Button> Share
</Button>
) : null}
<UserButton userData={userData} /> <UserButton userData={userData} />
</div> </div>
</div> </div>

View File

@ -5,6 +5,13 @@ import { useEffect, useRef, useState } from "react"
import { getIconForFolder, getIconForOpenFolder } from "vscode-icons-js" import { getIconForFolder, getIconForOpenFolder } from "vscode-icons-js"
import { TFile, TFolder, TTab } from "./types" import { TFile, TFolder, TTab } from "./types"
import SidebarFile from "./file" import SidebarFile from "./file"
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "@/components/ui/context-menu"
import { Pencil, Trash2 } from "lucide-react"
export default function SidebarFolder({ export default function SidebarFolder({
data, data,
@ -39,12 +46,9 @@ export default function SidebarFolder({
}, [editing]) }, [editing])
return ( return (
<> <ContextMenu>
<div <ContextMenuTrigger
onClick={() => setIsOpen((prev) => !prev)} onClick={() => setIsOpen((prev) => !prev)}
onDoubleClick={() => {
setEditing(true)
}}
className="w-full flex items-center h-7 px-1 transition-colors hover:bg-secondary rounded-sm cursor-pointer" className="w-full flex items-center h-7 px-1 transition-colors hover:bg-secondary rounded-sm cursor-pointer"
> >
<Image <Image
@ -74,7 +78,29 @@ export default function SidebarFolder({
}} }}
/> />
</form> </form>
</div> </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>
{isOpen ? ( {isOpen ? (
<div className="flex w-full items-stretch"> <div className="flex w-full items-stretch">
<div className="w-[1px] bg-border mx-2 h-full"></div> <div className="w-[1px] bg-border mx-2 h-full"></div>
@ -102,6 +128,6 @@ export default function SidebarFolder({
</div> </div>
</div> </div>
) : null} ) : null}
</> </ContextMenu>
) )
} }