-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21 from cu8code/dev
- Loading branch information
Showing
48 changed files
with
19,374 additions
and
3,126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": ["next/core-web-vitals", "next/typescript"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,36 @@ | ||
# Logs | ||
logs | ||
*.log | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
.yarn/install-state.gz | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
pnpm-debug.log* | ||
lerna-debug.log* | ||
|
||
node_modules | ||
dist | ||
dist-ssr | ||
*.local | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
.DS_Store | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? | ||
|
||
# local env files | ||
.env*.local | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
Binary file not shown.
Binary file not shown.
Binary file not shown.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import type { Metadata } from "next"; | ||
import localFont from "next/font/local"; | ||
import "./globals.css"; | ||
|
||
const geistSans = localFont({ | ||
src: "./fonts/GeistVF.woff", | ||
variable: "--font-geist-sans", | ||
weight: "100 900", | ||
}); | ||
const geistMono = localFont({ | ||
src: "./fonts/GeistMonoVF.woff", | ||
variable: "--font-geist-mono", | ||
weight: "100 900", | ||
}); | ||
|
||
export const metadata: Metadata = { | ||
title: "Create Next App", | ||
description: "Generated by create next app", | ||
}; | ||
|
||
export default function RootLayout({ | ||
children, | ||
}: Readonly<{ | ||
children: React.ReactNode; | ||
}>) { | ||
return ( | ||
<html lang="en"> | ||
<body | ||
className={`${geistSans.variable} ${geistMono.variable} antialiased`} | ||
> | ||
{children} | ||
</body> | ||
</html> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
"use client" | ||
|
||
import React, { useEffect, useState, useRef } from "react"; | ||
import { WebContainer } from "@webcontainer/api"; | ||
import { useVSCodeStore } from "@/utils/store"; | ||
import FileExplorer from "@/components/FileExplorer"; | ||
import EditorPanel from "@/components/EditorPanel"; | ||
import TerminalPanel from "@/components/TerminalPanel"; | ||
import { | ||
ImperativePanelHandle, | ||
Panel, | ||
PanelGroup, | ||
PanelResizeHandle, | ||
} from "react-resizable-panels"; | ||
import { Sidebar } from "@/components/SideBar"; | ||
import Loading from "@/components/Loading"; | ||
|
||
export default function VSCodeClone() { | ||
const { | ||
selectedFile, | ||
openFiles, | ||
webcontainerInstance, | ||
setSelectedFile, | ||
setOpenFiles, | ||
setWebcontainerInstance, | ||
updateFileSystem, | ||
updateFile, | ||
showExplorer, | ||
showTerminal, | ||
} = useVSCodeStore(); | ||
|
||
const [loading, setLoading] = useState<boolean>(true); | ||
|
||
// Panel refs with the appropriate type that includes collapse/expand | ||
const fileExplorerPanelRef = useRef<ImperativePanelHandle | null>(null); | ||
const terminalPanelRef = useRef<ImperativePanelHandle | null>(null); | ||
|
||
const s = async (ins: WebContainer) => { | ||
const blob = await fetch("/_vite-react-starter-main-bin") | ||
ins.mount(await blob.arrayBuffer() as unknown as ArrayBuffer) | ||
} | ||
|
||
useEffect(() => { | ||
if (!webcontainerInstance) { | ||
bootWebContainer(); | ||
} | ||
|
||
return () => { | ||
if (webcontainerInstance) { | ||
webcontainerInstance.teardown(); | ||
} | ||
}; | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, []); | ||
|
||
const bootWebContainer = async (): Promise<void> => { | ||
if (!webcontainerInstance) { | ||
const instance = await WebContainer.boot(); | ||
setWebcontainerInstance(instance); | ||
await s(instance) | ||
await updateFileSystem(); | ||
instance.fs.watch("/", { recursive: true }, updateFileSystem); | ||
setLoading(false); | ||
} | ||
}; | ||
|
||
const handleEditorChange = async ( | ||
value: string | undefined, | ||
): Promise<void> => { | ||
console.assert( | ||
value !== undefined, | ||
"Editor value should not be undefined.", | ||
); | ||
|
||
if (selectedFile && value !== undefined) { | ||
console.assert( | ||
selectedFile !== null, | ||
"Selected file should not be null.", | ||
); | ||
updateFile(selectedFile, value); // Update Zustand store (in-memory) | ||
|
||
if (webcontainerInstance) { | ||
await webcontainerInstance.fs.writeFile(selectedFile, value); // Write to the WebContainer FS | ||
await updateFileSystem(); // Sync the Zustand store with WebContainer FS | ||
} | ||
} else { | ||
console.warn( | ||
"Editor change detected, but no file is selected or value is undefined.", | ||
); | ||
} | ||
}; | ||
|
||
// Handle file explorer collapse/expand on showExplorer change | ||
useEffect(() => { | ||
if (showExplorer) { | ||
handleExpandFileExplorer(); | ||
} else { | ||
handleCollapseFileExplorer(); | ||
} | ||
}, [showExplorer]); | ||
|
||
// Handle terminal collapse/expand on showTerminal change | ||
useEffect(() => { | ||
if (showTerminal) { | ||
handleExpandTerminal(); | ||
} else { | ||
handleCollapseTerminal(); | ||
} | ||
}, [showTerminal]); | ||
|
||
const handleFileClick = (fileName: string): void => { | ||
console.assert( | ||
fileName !== undefined, | ||
"File name should not be undefined.", | ||
); | ||
|
||
setSelectedFile(fileName); | ||
|
||
if (!openFiles.includes(fileName)) { | ||
setOpenFiles([...openFiles, fileName]); | ||
} | ||
}; | ||
|
||
const handleCollapseFileExplorer = (): void => { | ||
if (fileExplorerPanelRef.current) { | ||
fileExplorerPanelRef.current.collapse(); | ||
} | ||
}; | ||
|
||
const handleExpandFileExplorer = (): void => { | ||
if (fileExplorerPanelRef.current) { | ||
fileExplorerPanelRef.current.expand(); | ||
} | ||
}; | ||
|
||
const handleCollapseTerminal = (): void => { | ||
if (terminalPanelRef.current) { | ||
terminalPanelRef.current.collapse(); | ||
} | ||
}; | ||
|
||
const handleExpandTerminal = (): void => { | ||
if (terminalPanelRef.current) { | ||
terminalPanelRef.current.expand(); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="h-screen w-screen p-0 m-0 overflow-hidden"> | ||
{loading ? ( | ||
<Loading /> | ||
) : ( | ||
<PanelGroup autoSave="primary-layout" direction="horizontal"> | ||
<Panel minSize={3} defaultSize={3} maxSize={3}> | ||
<Sidebar /> | ||
</Panel> | ||
<PanelResizeHandle disabled={true} /> | ||
<Panel defaultSize={97}> | ||
<PanelGroup autoSave="secondary-layout" direction="horizontal"> | ||
<Panel | ||
ref={fileExplorerPanelRef} | ||
defaultSize={20} | ||
collapsible={true} | ||
> | ||
<FileExplorer handleFileClick={handleFileClick} /> | ||
</Panel> | ||
<PanelResizeHandle /> | ||
<Panel defaultSize={80}> | ||
<PanelGroup autoSave="tertiary-layout" direction="vertical"> | ||
<Panel defaultSize={80}> | ||
<EditorPanel handleEditorChange={handleEditorChange} /> | ||
</Panel> | ||
<PanelResizeHandle /> | ||
<Panel | ||
ref={terminalPanelRef} | ||
defaultSize={20} | ||
collapsible={true} | ||
> | ||
<TerminalPanel /> | ||
</Panel> | ||
</PanelGroup> | ||
</Panel> | ||
</PanelGroup> | ||
</Panel> | ||
</PanelGroup> | ||
)} | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
"use client" | ||
|
||
import React from 'react'; | ||
import { useVSCodeStore } from "@/utils/store"; | ||
import { Editor } from "@monaco-editor/react"; | ||
import { getFileContent } from '../utils/fileSystemTree'; | ||
import TopBar from './TopBar'; | ||
|
||
export const EditorPanel: React.FC<{ | ||
handleEditorChange: (value: string | undefined) => void; | ||
}> = ({ handleEditorChange }) => { | ||
const { selectedFile, files, getTheme } = useVSCodeStore(); | ||
const theme = getTheme(); | ||
|
||
return ( | ||
<div className="flex-grow flex flex-col h-full"> | ||
<TopBar /> | ||
{selectedFile ? ( | ||
<Editor | ||
defaultLanguage={getLanguageId(selectedFile)} | ||
theme={theme.main.editor.theme} | ||
onChange={handleEditorChange} | ||
value={getFileContent(selectedFile, files)} | ||
|
||
/> | ||
) : ( | ||
<div className="h-full flex items-center justify-center" style={{ | ||
color: theme.main.topbar.text_color, | ||
backgroundColor: theme.main.editor.backgroundColor | ||
}}> | ||
Select a file to edit | ||
</div> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
function getLanguageId(filePath: string): string { | ||
const extension = filePath.split('.').pop()!.toLowerCase(); | ||
const languageMap: { [key: string]: string } = { | ||
js: 'javascript', | ||
ts: 'typescript', | ||
jsx: 'javascript', | ||
tsx: 'typescript', | ||
json: 'json', | ||
css: 'css', | ||
less: 'less', | ||
scss: 'scss', | ||
sass: 'sass', | ||
html: 'html', | ||
md: 'markdown', | ||
xml: 'xml', | ||
yaml: 'yaml', | ||
yml: 'yaml', | ||
}; | ||
return languageMap[extension] || 'plain text'; | ||
} | ||
|
||
export default EditorPanel; |
Oops, something went wrong.