Merge pull request #11 from Code-Victor/feat/light-theme
Feat/light theme
This commit is contained in:
commit
3b93090d97
@ -224,10 +224,11 @@ io.on("connection", async (socket) => {
|
||||
containers[data.sandboxId],
|
||||
sendLoadedEvent
|
||||
)
|
||||
await fileManagers[data.sandboxId].initialize()
|
||||
terminalManagers[data.sandboxId] = new TerminalManager(
|
||||
containers[data.sandboxId]
|
||||
)
|
||||
console.log(`terminal manager set up for ${data.sandboxId}`)
|
||||
await fileManagers[data.sandboxId].initialize()
|
||||
}
|
||||
|
||||
const fileManager = fileManagers[data.sandboxId]
|
||||
@ -415,6 +416,12 @@ io.on("connection", async (socket) => {
|
||||
socket.on("createTerminal", async (id: string, callback) => {
|
||||
try {
|
||||
await lockManager.acquireLock(data.sandboxId, async () => {
|
||||
let terminalManager = terminalManagers[data.sandboxId]
|
||||
if (!terminalManager) {
|
||||
terminalManager = terminalManagers[data.sandboxId] =
|
||||
new TerminalManager(containers[data.sandboxId])
|
||||
}
|
||||
|
||||
await terminalManager.createTerminal(id, (responseString: string) => {
|
||||
socket.emit("terminalResponse", { id, data: responseString })
|
||||
const port = extractPortNumber(responseString)
|
||||
|
@ -99,6 +99,29 @@
|
||||
); /* violet 900 -> bg */
|
||||
}
|
||||
|
||||
.light .gradient-button-bg {
|
||||
background: radial-gradient(
|
||||
circle at top,
|
||||
#262626 0%,
|
||||
#f5f5f5 50%
|
||||
); /* Dark gray -> Light gray */
|
||||
}
|
||||
|
||||
.light .gradient-button {
|
||||
background: radial-gradient(
|
||||
circle at bottom,
|
||||
hsl(0, 10%, 25%) -10%,
|
||||
#9d9d9d 50%
|
||||
); /* Light gray -> Almost white */
|
||||
}
|
||||
|
||||
.light .gradient-button-bg > div:hover {
|
||||
background: radial-gradient(
|
||||
circle at bottom,
|
||||
hsl(0, 10%, 25%) -10%,
|
||||
#9d9d9d 80%
|
||||
); /* Light gray -> Almost white */
|
||||
}
|
||||
.inline-decoration::before {
|
||||
content: "Generate";
|
||||
color: #525252;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ThemeProvider } from "@/components/layout/themeProvider"
|
||||
import { Toaster } from "@/components/ui/sonner"
|
||||
import { ThemeProvider } from "@/components/ui/theme-provider"
|
||||
import { PreviewProvider } from "@/context/PreviewContext"
|
||||
import { SocketProvider } from "@/context/SocketContext"
|
||||
import { SocketProvider } from '@/context/SocketContext'
|
||||
import { ClerkProvider } from "@clerk/nextjs"
|
||||
import { Analytics } from "@vercel/analytics/react"
|
||||
import { GeistMono } from "geist/font/mono"
|
||||
@ -25,8 +25,7 @@ export default function RootLayout({
|
||||
<body>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="dark"
|
||||
forcedTheme="dark"
|
||||
defaultTheme="system"
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SocketProvider>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Logo from "@/assets/logo.svg"
|
||||
import { ThemeSwitcher } from "@/components/ui/theme-switcher"
|
||||
import { User } from "@/lib/types"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
@ -19,6 +20,7 @@ export default function DashboardNavbar({ userData }: { userData: User }) {
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<DashboardNavbarSearch />
|
||||
<ThemeSwitcher />
|
||||
<UserButton userData={userData} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -288,7 +288,7 @@ function SearchInput({
|
||||
<form {...{ onSubmit }} className="w-40 h-8 ">
|
||||
<label
|
||||
htmlFor="template-search"
|
||||
className="flex gap-2 rounded-sm transition-colors bg-[#2e2e2e] border border-[--s-color] [--s-color:hsl(var(--muted-foreground))] focus-within:[--s-color:#fff] h-full items-center px-2"
|
||||
className="flex gap-2 rounded-sm transition-colors bg-gray-100 dark:bg-[#2e2e2e] border border-[--s-color] [--s-color:hsl(var(--muted-foreground))] focus-within:[--s-color:hsl(var(--muted-foreground),50%)] h-full items-center px-2"
|
||||
>
|
||||
<Search className="size-4 text-[--s-color] transition-colors" />
|
||||
<input
|
||||
@ -298,7 +298,7 @@ function SearchInput({
|
||||
placeholder="Search templates"
|
||||
value={value}
|
||||
onChange={(e) => onValueChange?.(e.target.value)}
|
||||
className="bg-transparent placeholder:text-muted-foreground text-white w-full focus:outline-none text-xs"
|
||||
className="bg-transparent placeholder:text-muted-foreground w-full focus:outline-none text-xs"
|
||||
/>
|
||||
</label>
|
||||
</form>
|
||||
|
@ -39,6 +39,7 @@ import {
|
||||
Sparkles,
|
||||
TerminalSquare,
|
||||
} from "lucide-react"
|
||||
import { useTheme } from "next-themes"
|
||||
import React from "react"
|
||||
import { ImperativePanelHandle } from "react-resizable-panels"
|
||||
import { Button } from "../ui/button"
|
||||
@ -61,7 +62,8 @@ export default function CodeEditor({
|
||||
}) {
|
||||
//SocketContext functions and effects
|
||||
const { socket, setUserAndSandboxId } = useSocket()
|
||||
|
||||
// theme
|
||||
const { theme } = useTheme()
|
||||
useEffect(() => {
|
||||
// Ensure userData.id and sandboxData.id are available before attempting to connect
|
||||
if (userData.id && sandboxData.id) {
|
||||
@ -105,6 +107,7 @@ export default function CodeEditor({
|
||||
|
||||
// Editor state
|
||||
const [editorLanguage, setEditorLanguage] = useState("plaintext")
|
||||
console.log("editor language: ",editorLanguage)
|
||||
const [cursorLine, setCursorLine] = useState(0)
|
||||
const [editorRef, setEditorRef] =
|
||||
useState<monaco.editor.IStandaloneCodeEditor>()
|
||||
@ -1118,7 +1121,7 @@ export default function CodeEditor({
|
||||
fixedOverflowWidgets: true,
|
||||
fontFamily: "var(--font-geist-mono)",
|
||||
}}
|
||||
theme="vs-dark"
|
||||
theme={theme === "light" ? "vs" : "vs-dark"}
|
||||
value={activeFileContent}
|
||||
/>
|
||||
</>
|
||||
@ -1148,7 +1151,7 @@ export default function CodeEditor({
|
||||
defaultSize={isPreviewCollapsed ? 4 : 20}
|
||||
minSize={25}
|
||||
collapsedSize={isHorizontalLayout ? 20 : 4}
|
||||
className="p-2 flex flex-col"
|
||||
className="p-2 flex flex-col gap-2"
|
||||
collapsible
|
||||
onCollapse={() => setIsPreviewCollapsed(true)}
|
||||
onExpand={() => setIsPreviewCollapsed(false)}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
import Logo from "@/assets/logo.svg"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { ThemeSwitcher } from "@/components/ui/theme-switcher"
|
||||
import UserButton from "@/components/ui/userButton"
|
||||
import { Sandbox, User } from "@/lib/types"
|
||||
import { Pencil, Users } from "lucide-react"
|
||||
@ -79,6 +80,7 @@ export default function Navbar({
|
||||
</Button>
|
||||
</>
|
||||
) : null}
|
||||
<ThemeSwitcher />
|
||||
<UserButton userData={userData} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,9 +6,9 @@ import "./xterm.css"
|
||||
|
||||
import { debounce } from "@/lib/utils"
|
||||
import { Loader2 } from "lucide-react"
|
||||
import { useTheme } from "next-themes"
|
||||
import { ElementRef, useEffect, useRef } from "react"
|
||||
import { Socket } from "socket.io-client"
|
||||
|
||||
export default function EditorTerminal({
|
||||
socket,
|
||||
id,
|
||||
@ -22,18 +22,17 @@ export default function EditorTerminal({
|
||||
setTerm: (term: Terminal) => void
|
||||
visible: boolean
|
||||
}) {
|
||||
const terminalRef = useRef<ElementRef<"div">>(null)
|
||||
const { theme } = useTheme()
|
||||
const terminalContainerRef = useRef<ElementRef<"div">>(null)
|
||||
const fitAddonRef = useRef<FitAddon | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!terminalRef.current) return
|
||||
if (!terminalContainerRef.current) return
|
||||
// console.log("new terminal", id, term ? "reusing" : "creating");
|
||||
|
||||
const terminal = new Terminal({
|
||||
cursorBlink: true,
|
||||
theme: {
|
||||
background: "#262626",
|
||||
},
|
||||
theme: theme === "light" ? lightTheme : darkTheme,
|
||||
fontFamily: "var(--font-geist-mono)",
|
||||
fontSize: 14,
|
||||
lineHeight: 1.5,
|
||||
@ -47,14 +46,20 @@ export default function EditorTerminal({
|
||||
return dispose
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (term) {
|
||||
term.options.theme = theme === "light" ? lightTheme : darkTheme
|
||||
}
|
||||
}, [theme])
|
||||
|
||||
useEffect(() => {
|
||||
if (!term) return
|
||||
|
||||
if (!terminalRef.current) return
|
||||
if (!terminalContainerRef.current) return
|
||||
if (!fitAddonRef.current) {
|
||||
const fitAddon = new FitAddon()
|
||||
term.loadAddon(fitAddon)
|
||||
term.open(terminalRef.current)
|
||||
term.open(terminalContainerRef.current)
|
||||
fitAddon.fit()
|
||||
fitAddonRef.current = fitAddon
|
||||
}
|
||||
@ -69,7 +74,7 @@ export default function EditorTerminal({
|
||||
})
|
||||
const resizeObserver = new ResizeObserver(
|
||||
debounce((entries) => {
|
||||
if (!fitAddonRef.current || !terminalRef.current) return
|
||||
if (!fitAddonRef.current || !terminalContainerRef.current) return
|
||||
|
||||
const entry = entries[0]
|
||||
if (!entry) return
|
||||
@ -78,8 +83,8 @@ export default function EditorTerminal({
|
||||
|
||||
// Only call fit if the size has actually changed
|
||||
if (
|
||||
width !== terminalRef.current.offsetWidth ||
|
||||
height !== terminalRef.current.offsetHeight
|
||||
width !== terminalContainerRef.current.offsetWidth ||
|
||||
height !== terminalContainerRef.current.offsetHeight
|
||||
) {
|
||||
try {
|
||||
fitAddonRef.current.fit()
|
||||
@ -91,13 +96,13 @@ export default function EditorTerminal({
|
||||
)
|
||||
|
||||
// start observing for resize
|
||||
resizeObserver.observe(terminalRef.current)
|
||||
resizeObserver.observe(terminalContainerRef.current)
|
||||
return () => {
|
||||
disposableOnData.dispose()
|
||||
disposableOnResize.dispose()
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}, [term, terminalRef.current])
|
||||
}, [term, terminalContainerRef.current])
|
||||
|
||||
useEffect(() => {
|
||||
if (!term) return
|
||||
@ -116,7 +121,7 @@ export default function EditorTerminal({
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
ref={terminalRef}
|
||||
ref={terminalContainerRef}
|
||||
style={{ display: visible ? "block" : "none" }}
|
||||
className="w-full h-full text-left"
|
||||
>
|
||||
@ -130,3 +135,56 @@ export default function EditorTerminal({
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const lightTheme = {
|
||||
foreground: "#2e3436",
|
||||
background: "#ffffff",
|
||||
black: "#2e3436",
|
||||
brightBlack: "#555753",
|
||||
red: "#cc0000",
|
||||
brightRed: "#ef2929",
|
||||
green: "#4e9a06",
|
||||
brightGreen: "#8ae234",
|
||||
yellow: "#c4a000",
|
||||
brightYellow: "#fce94f",
|
||||
blue: "#3465a4",
|
||||
brightBlue: "#729fcf",
|
||||
magenta: "#75507b",
|
||||
brightMagenta: "#ad7fa8",
|
||||
cyan: "#06989a",
|
||||
brightCyan: "#34e2e2",
|
||||
white: "#d3d7cf",
|
||||
brightWhite: "#eeeeec",
|
||||
cursor: "#2e3436",
|
||||
cursorAccent: "#ffffff",
|
||||
selectionBackground: "#3465a4",
|
||||
selectionForeground: "#ffffff",
|
||||
selectionInactiveBackground: "#264973",
|
||||
}
|
||||
|
||||
// Dark Theme
|
||||
const darkTheme = {
|
||||
foreground: "#f8f8f2",
|
||||
background: "#0a0a0a",
|
||||
black: "#21222c",
|
||||
brightBlack: "#6272a4",
|
||||
red: "#ff5555",
|
||||
brightRed: "#ff6e6e",
|
||||
green: "#50fa7b",
|
||||
brightGreen: "#69ff94",
|
||||
yellow: "#f1fa8c",
|
||||
brightYellow: "#ffffa5",
|
||||
blue: "#bd93f9",
|
||||
brightBlue: "#d6acff",
|
||||
magenta: "#ff79c6",
|
||||
brightMagenta: "#ff92df",
|
||||
cyan: "#8be9fd",
|
||||
brightCyan: "#a4ffff",
|
||||
white: "#f8f8f2",
|
||||
brightWhite: "#ffffff",
|
||||
cursor: "#f8f8f2",
|
||||
cursorAccent: "#0a0a0a",
|
||||
selectionBackground: "#264973",
|
||||
selectionForeground: "#ffffff",
|
||||
selectionInactiveBackground: "#1a3151",
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import Logo from "@/assets/logo.svg"
|
||||
import XLogo from "@/assets/x.svg"
|
||||
import Button from "@/components/ui/customButton"
|
||||
import CustomButton from "@/components/ui/customButton"
|
||||
import { ChevronRight } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { Button } from "../ui/button"
|
||||
import { ThemeSwitcher } from "../ui/theme-switcher"
|
||||
|
||||
export default function Landing() {
|
||||
return (
|
||||
@ -20,21 +21,37 @@ export default function Landing() {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<Button variant="outline" size="icon" asChild>
|
||||
<a href="https://www.x.com/ishaandey_" target="_blank">
|
||||
<Image src={XLogo} alt="X Logo" width={18} height={18} />
|
||||
<svg
|
||||
width="1200"
|
||||
height="1227"
|
||||
viewBox="0 0 1200 1227"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="size-[1.125rem] text-muted-foreground"
|
||||
>
|
||||
<path
|
||||
d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</Button>
|
||||
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
<h1 className="text-2xl font-medium text-center mt-16">
|
||||
A Collaborative + AI-Powered Code Environment
|
||||
</h1>
|
||||
<div className="text-muted-foreground mt-4 text-center ">
|
||||
<p className="text-muted-foreground mt-4 text-center ">
|
||||
Sandbox is an open-source cloud-based code editing environment with
|
||||
custom AI code autocompletion and real-time collaboration.
|
||||
</div>
|
||||
</p>
|
||||
<div className="mt-8 flex space-x-4">
|
||||
<Link href="/sign-up">
|
||||
<Button>Go To App</Button>
|
||||
<CustomButton>Go To App</CustomButton>
|
||||
</Link>
|
||||
<a
|
||||
href="https://github.com/ishaan1013/sandbox"
|
||||
|
@ -24,7 +24,7 @@ const Button = ({
|
||||
`gradient-button-bg p-[1px] inline-flex group rounded-md text-sm font-medium focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50`
|
||||
)}
|
||||
>
|
||||
<div className="rounded-[6px] w-full gradient-button flex items-center justify-center whitespace-nowrap px-4 py-2 h-9">
|
||||
<div className="rounded-[6px] w-full gradient-button transition-colors flex items-center justify-center whitespace-nowrap px-4 py-2 h-9">
|
||||
{children}
|
||||
</div>
|
||||
</button>
|
||||
|
@ -28,7 +28,7 @@ export default function Tab({
|
||||
variant={"secondary"}
|
||||
className={`font-normal select-none ${
|
||||
selected
|
||||
? "bg-neutral-700 hover:bg-neutral-600 text-foreground"
|
||||
? "bg-muted-foreground/50 hover:bg-muted-foreground/40 text-foreground"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
|
@ -6,3 +6,4 @@ import { type ThemeProviderProps } from "next-themes/dist/types"
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
|
||||
}
|
||||
|
39
frontend/components/ui/theme-switcher.tsx
Normal file
39
frontend/components/ui/theme-switcher.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
"use client"
|
||||
|
||||
import { Moon, Sun } from "lucide-react"
|
||||
import { useTheme } from "next-themes"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
export function ThemeSwitcher() {
|
||||
const { setTheme } = useTheme()
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="text-muted-foreground">
|
||||
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => setTheme("light")}>
|
||||
Light
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
||||
Dark
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setTheme("system")}>
|
||||
System
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
@ -38,6 +38,7 @@
|
||||
"csl": "xml",
|
||||
"cson": "coffeescript",
|
||||
"csproj": "xml",
|
||||
"css":"css",
|
||||
"ct": "xml",
|
||||
"ctp": "php",
|
||||
"cxx": "cpp",
|
||||
|
Loading…
x
Reference in New Issue
Block a user