diff --git a/code-editor/bun.lockb b/code-editor/bun.lockb index a2f63c7..8e46a51 100755 Binary files a/code-editor/bun.lockb and b/code-editor/bun.lockb differ diff --git a/code-editor/package.json b/code-editor/package.json index ace5b1f..6e3f00c 100644 --- a/code-editor/package.json +++ b/code-editor/package.json @@ -10,8 +10,13 @@ "preview": "vite preview" }, "dependencies": { + "@monaco-editor/react": "^4.6.0", + "@radix-ui/react-scroll-area": "^1.2.2", + "@radix-ui/react-select": "^2.1.4", + "@radix-ui/react-tabs": "^1.1.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "devicons-react": "^1.4.0", "lucide-react": "^0.469.0", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -20,6 +25,7 @@ }, "devDependencies": { "@eslint/js": "^9.17.0", + "@shikijs/monaco": "^1.26.1", "@types/node": "^22.10.5", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", @@ -30,6 +36,7 @@ "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", "postcss": "^8.4.49", + "shiki": "^1.26.1", "tailwindcss": "^3.4.17", "typescript": "~5.6.2", "typescript-eslint": "^8.18.2", diff --git a/code-editor/src/App.tsx b/code-editor/src/App.tsx index b56b979..00c96b2 100644 --- a/code-editor/src/App.tsx +++ b/code-editor/src/App.tsx @@ -1,5 +1,80 @@ +import { + DEFAULT_FILES, + DEFAULT_EDITOR_THEME, + DEFAULT_EDITOR_LANGUAGE, +} from "@/config"; +import { useState } from "react"; +import { highlighter } from "@/lib/shiki"; +import { Bot, CodeXml } from "lucide-react"; +import { shikiToMonaco } from "@shikijs/monaco"; +import { DiffEditor, Editor, Monaco } from "@monaco-editor/react"; +import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + function App() { - return null; + const [language, setLanguage] = useState(DEFAULT_EDITOR_LANGUAGE); + const [theme, setTheme] = useState(DEFAULT_EDITOR_THEME); + const file = DEFAULT_FILES[language]; + + return ( +
+ + + + + + + + + + + + { + shikiToMonaco(highlighter, monaco); + }} + /> + + + { + shikiToMonaco(highlighter, monaco); + }} + /> + + +
+ ); } export default App; diff --git a/code-editor/src/components/ui/scroll-area.tsx b/code-editor/src/components/ui/scroll-area.tsx new file mode 100644 index 0000000..cf253cf --- /dev/null +++ b/code-editor/src/components/ui/scroll-area.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" + +import { cn } from "@/lib/utils" + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)) +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + +)) +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName + +export { ScrollArea, ScrollBar } diff --git a/code-editor/src/components/ui/select.tsx b/code-editor/src/components/ui/select.tsx new file mode 100644 index 0000000..a84242c --- /dev/null +++ b/code-editor/src/components/ui/select.tsx @@ -0,0 +1,157 @@ +import * as React from "react" +import * as SelectPrimitive from "@radix-ui/react-select" +import { Check, ChevronDown, ChevronUp } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Select = SelectPrimitive.Root + +const SelectGroup = SelectPrimitive.Group + +const SelectValue = SelectPrimitive.Value + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className + )} + {...props} + > + {children} + + + + +)) +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)) +SelectContent.displayName = SelectPrimitive.Content.displayName + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectLabel.displayName = SelectPrimitive.Label.displayName + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +SelectItem.displayName = SelectPrimitive.Item.displayName + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectSeparator.displayName = SelectPrimitive.Separator.displayName + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +} diff --git a/code-editor/src/components/ui/tabs.tsx b/code-editor/src/components/ui/tabs.tsx new file mode 100644 index 0000000..85d83be --- /dev/null +++ b/code-editor/src/components/ui/tabs.tsx @@ -0,0 +1,53 @@ +import * as React from "react" +import * as TabsPrimitive from "@radix-ui/react-tabs" + +import { cn } from "@/lib/utils" + +const Tabs = TabsPrimitive.Root + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsList.displayName = TabsPrimitive.List.displayName + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsContent.displayName = TabsPrimitive.Content.displayName + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/code-editor/src/config/index.ts b/code-editor/src/config/index.ts new file mode 100644 index 0000000..65077d7 --- /dev/null +++ b/code-editor/src/config/index.ts @@ -0,0 +1,43 @@ +import { SupportedEditorLanguage } from "@/constants/languages"; + +export const DEFAULT_EDITOR_THEME = "vitesse-dark"; + +export const DEFAULT_EDITOR_LANGUAGE: SupportedEditorLanguage = "c"; + +export const DEFAULT_FILES: Record< + SupportedEditorLanguage, + { + path: string; + language: SupportedEditorLanguage; + value: string; + } +> = { + c: { + path: "playground/main.c", + language: "c", + value: `#include +int main() { + printf("Hello, World!"); + return 0; +}`, + }, + cpp: { + path: "playground/main.cpp", + language: "cpp", + value: `#include +using namespace std; +int main() { + cout << "Hello, World!"; + return 0; +}`, + }, + java: { + path: "playground/Main.java", + language: "java", + value: `public class Main { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } +}`, + }, +}; diff --git a/code-editor/src/constants/languages.ts b/code-editor/src/constants/languages.ts new file mode 100644 index 0000000..2ddf2cc --- /dev/null +++ b/code-editor/src/constants/languages.ts @@ -0,0 +1,24 @@ +import { COriginal, CplusplusOriginal, JavaOriginal } from "devicons-react"; + +export const SUPPORTED_EDITOR_LANGUAGES = ["c", "cpp", "java"]; + +export const SUPPORTED_EDITOR_LANGUAGES_CONFIG = { + c: { + id: "c", + label: "C", + icon: COriginal, + }, + cpp: { + id: "cpp", + label: "C++", + icon: CplusplusOriginal, + }, + java: { + id: "java", + label: "Java", + icon: JavaOriginal, + }, +}; + +export type SupportedEditorLanguage = + (typeof SUPPORTED_EDITOR_LANGUAGES)[number]; diff --git a/code-editor/src/constants/themes.ts b/code-editor/src/constants/themes.ts new file mode 100644 index 0000000..3ab0087 --- /dev/null +++ b/code-editor/src/constants/themes.ts @@ -0,0 +1,202 @@ +export const SUPPORTED_EDITOR_THEMES = [ + { + id: "andromeeda", + label: "Andromeeda", + }, + { + id: "aurora-x", + label: "Aurora X", + }, + { + id: "ayu-dark", + label: "Ayu Dark", + }, + { + id: "catppuccin-frappe", + label: "Catppuccin Frappé", + }, + { + id: "catppuccin-latte", + label: "Catppuccin Latte", + }, + { + id: "catppuccin-macchiato", + label: "Catppuccin Macchiato", + }, + { + id: "catppuccin-mocha", + label: "Catppuccin Mocha", + }, + { + id: "dark-plus", + label: "Dark Plus", + }, + { + id: "dracula", + label: "Dracula", + }, + { + id: "dracula-soft", + label: "Dracula Soft", + }, + { + id: "everforest-dark", + label: "Everforest Dark", + }, + { + id: "everforest-light", + label: "Everforest Light", + }, + { + id: "github-dark", + label: "GitHub Dark", + }, + { + id: "github-light", + label: "GitHub Light", + }, + { + id: "github-light-default", + label: "GitHub Light Default", + }, + { + id: "github-light-high-contrast", + label: "GitHub Light High Contrast", + }, + { + id: "houston", + label: "Houston", + }, + { + id: "kanagawa-dragon", + label: "Kanagawa Dragon", + }, + { + id: "kanagawa-lotus", + label: "Kanagawa Lotus", + }, + { + id: "kanagawa-wave", + label: "Kanagawa Wave", + }, + { + id: "laserwave", + label: "Laserwave", + }, + { + id: "light-plus", + label: "Light Plus", + }, + { + id: "material-theme", + label: "Material Theme", + }, + { + id: "material-theme-darker", + label: "Material Theme Darker", + }, + { + id: "material-theme-lighter", + label: "Material Theme Lighter", + }, + { + id: "material-theme-ocean", + label: "Material Theme Ocean", + }, + { + id: "material-theme-palenight", + label: "Material Theme Palenight", + }, + { + id: "min-dark", + label: "Min Dark", + }, + { + id: "min-light", + label: "Min Light", + }, + { + id: "monokai", + label: "Monokai", + }, + { + id: "night-owl", + label: "Night Owl", + }, + { + id: "nord", + label: "Nord", + }, + { + id: "one-dark-pro", + label: "One Dark Pro", + }, + { + id: "one-light", + label: "One Light", + }, + { + id: "plastic", + label: "Plastic", + }, + { + id: "poimandres", + label: "Poimandres", + }, + { + id: "red", + label: "Red", + }, + { + id: "rose-pine", + label: "Rosé Pine", + }, + { + id: "rose-pine-dawn", + label: "Rosé Pine Dawn", + }, + { + id: "rose-pine-moon", + label: "Rosé Pine Moon", + }, + { + id: "slack-dark", + label: "Slack Dark", + }, + { + id: "slack-ochin", + label: "Slack Ochin", + }, + { + id: "snazzy-light", + label: "Snazzy Light", + }, + { + id: "solarized-dark", + label: "Solarized Dark", + }, + { + id: "solarized-light", + label: "Solarized Light", + }, + { + id: "synthwave-84", + label: "Synthwave '84", + }, + { + id: "tokyo-night", + label: "Tokyo Night", + }, + { + id: "vitesse-black", + label: "Vitesse Black", + }, + { + id: "vitesse-dark", + label: "Vitesse Dark", + }, + { + id: "vitesse-light", + label: "Vitesse Light", + }, +]; diff --git a/code-editor/src/lib/shiki.ts b/code-editor/src/lib/shiki.ts new file mode 100644 index 0000000..c57cc31 --- /dev/null +++ b/code-editor/src/lib/shiki.ts @@ -0,0 +1,16 @@ +import { createHighlighter } from "shiki"; +import { SUPPORTED_EDITOR_THEMES } from "@/constants/themes"; +import { SUPPORTED_EDITOR_LANGUAGES_CONFIG } from "@/constants/languages"; + +const SUPPORTED_EDITOR_THEMES_ID = SUPPORTED_EDITOR_THEMES.map( + (theme) => theme.id +); + +const SUPPORTED_EDITOR_LANGUAGES_ID = Object.values( + SUPPORTED_EDITOR_LANGUAGES_CONFIG +).map((language: { id: string }) => language.id); + +export const highlighter = await createHighlighter({ + themes: SUPPORTED_EDITOR_THEMES_ID, + langs: SUPPORTED_EDITOR_LANGUAGES_ID, +});