From df4d334d15cfde10db917f93fc3c7b7055d1b075 Mon Sep 17 00:00:00 2001 From: Shaswat Mishra Date: Fri, 8 Aug 2025 23:53:18 +0530 Subject: [PATCH 1/7] Initial Commit of JAVA IDE --- bun.lock | 10 ++ package.json | 4 + src/app/api/playground/route.ts | 41 ++++++ src/app/layout.config.tsx | 4 + src/app/playground/layout.tsx | 21 +++ src/app/playground/page.tsx | 236 ++++++++++++++++++++++++++++++++ 6 files changed, 316 insertions(+) create mode 100644 src/app/api/playground/route.ts create mode 100644 src/app/playground/layout.tsx create mode 100644 src/app/playground/page.tsx diff --git a/bun.lock b/bun.lock index 67c7af9c..dd7bd04f 100644 --- a/bun.lock +++ b/bun.lock @@ -8,6 +8,7 @@ "@radix-ui/react-slot": "^1.2.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "esbuild": "0.25.8", "fumadocs-core": "15.6.5", "fumadocs-mdx": "11.7.0", "fumadocs-ui": "15.6.5", @@ -18,8 +19,10 @@ "ogl": "^1.0.11", "prettier": "^3.6.2", "prettier-plugin-tailwindcss": "^0.6.14", + "prismjs": "^1.30.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-simple-code-editor": "^0.14.1", "sharp": "^0.34.3", "tailwind-merge": "^3.3.1", }, @@ -27,6 +30,7 @@ "@tailwindcss/postcss": "^4.1.11", "@types/mdx": "^2.0.13", "@types/node": "24.0.15", + "@types/prismjs": "^1.26.5", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "eslint": "^9.31.0", @@ -401,6 +405,8 @@ "@types/node": ["@types/node@24.0.15", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA=="], + "@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="], + "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], "@types/react-dom": ["@types/react-dom@19.1.6", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw=="], @@ -1105,6 +1111,8 @@ "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.14", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg=="], + "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], @@ -1125,6 +1133,8 @@ "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + "react-simple-code-editor": ["react-simple-code-editor@0.14.1", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow=="], + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], diff --git a/package.json b/package.json index 084401f0..8280c894 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@radix-ui/react-slot": "^1.2.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "esbuild": "0.25.8", "fumadocs-core": "15.6.5", "fumadocs-mdx": "11.7.0", "fumadocs-ui": "15.6.5", @@ -24,8 +25,10 @@ "ogl": "^1.0.11", "prettier": "^3.6.2", "prettier-plugin-tailwindcss": "^0.6.14", + "prismjs": "^1.30.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-simple-code-editor": "^0.14.1", "sharp": "^0.34.3", "tailwind-merge": "^3.3.1" }, @@ -33,6 +36,7 @@ "@tailwindcss/postcss": "^4.1.11", "@types/mdx": "^2.0.13", "@types/node": "24.0.15", + "@types/prismjs": "^1.26.5", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "eslint": "^9.31.0", diff --git a/src/app/api/playground/route.ts b/src/app/api/playground/route.ts new file mode 100644 index 00000000..d7a0245e --- /dev/null +++ b/src/app/api/playground/route.ts @@ -0,0 +1,41 @@ +// src/app/api/run-java/route.ts + +import { NextResponse } from 'next/server'; + +export async function POST(req: Request) { + try { + const { code } = await req.json(); + + const response = await fetch('https://api.jdoodle.com/v1/execute', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + script: code, + language: 'java', + versionIndex: '3', // Java 11 + clientId: '6d786cbb494a74d582ff33a3b176849f', // ← replace with your actual clientId + clientSecret: 'd79b0a5a8cb2a1f1f937c3ff9acb5a0989db048390aea95b413894858e965ae2', // ← replace with your actual clientSecret + }), + }); + + const data = await response.json(); + + return new NextResponse(JSON.stringify({ + run: { + output: data.output || 'No output', + }, + }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + + } catch (err) { + console.error(err); + return new NextResponse(JSON.stringify({ error: 'Execution failed' }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } +} diff --git a/src/app/layout.config.tsx b/src/app/layout.config.tsx index c74fca1e..24d67665 100644 --- a/src/app/layout.config.tsx +++ b/src/app/layout.config.tsx @@ -29,6 +29,10 @@ export const baseOptions: BaseLayoutProps = { text: "Sponsors", url: "/sponsors", }, + { + text: "Playground", + url: "/playground", +} ], githubUrl: "https://github.com/javaistic/javaistic", themeSwitch: { diff --git a/src/app/playground/layout.tsx b/src/app/playground/layout.tsx new file mode 100644 index 00000000..25c5b537 --- /dev/null +++ b/src/app/playground/layout.tsx @@ -0,0 +1,21 @@ +import { baseOptions } from "@/app/layout.config"; +import { Footer } from "@/components/footer"; +import { HomeLayout } from "fumadocs-ui/layouts/home"; +import type { ReactNode } from "react"; + +export default function Layout({ children }: { children: ReactNode }) { + return ( + <> + + {children} + + + ); +} diff --git a/src/app/playground/page.tsx b/src/app/playground/page.tsx new file mode 100644 index 00000000..c251ba60 --- /dev/null +++ b/src/app/playground/page.tsx @@ -0,0 +1,236 @@ +'use client'; + +import React, { useEffect, useRef, useState } from 'react'; +import { baseOptions } from '@/app/layout.config'; +import { Footer } from '@/components/footer'; +import { HomeLayout } from 'fumadocs-ui/layouts/home'; + +export default function PlayGround(): JSX.Element { + const DEFAULT_CODE = `public class Main {\n    public static void main(String[] args) {\n        // Write your code here\n    }\n}`; + + const [code, setCode] = useState(() => { + try { + if (typeof window === 'undefined') return DEFAULT_CODE; + return localStorage.getItem('javaistic_code') || DEFAULT_CODE; + } catch { + return DEFAULT_CODE; + } + }); + + const [output, setOutput] = useState(''); + const [loading, setLoading] = useState(false); + const [toast, setToast] = useState(''); + const [examplesOpen, setExamplesOpen] = useState(false); + + const textareaRef = useRef(null); + const lineRef = useRef(null); + + useEffect(() => { + try { + if (typeof window !== 'undefined') { + localStorage.setItem('javaistic_code', code); + } + } catch {} + }, [code]); + + const showToast = (msg: string, ms = 1800): void => { + setToast(msg); + const id = window.setTimeout(() => setToast(''), ms); + return () => clearTimeout(id); + }; + + const runCode = async (): Promise => { + setLoading(true); + setOutput(''); + try { + const res = await fetch('/api/playground', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ code }), + }); + + if (!res.ok) { + const text = await res.text(); + setOutput(`Error: ${res.status} ${text}`); + } else { + const data = await res.json(); + setOutput((data && data.run && (data.run.output as string)) || 'No output'); + } + } catch { + setOutput('Error executing code'); + } finally { + setLoading(false); + } + }; + + const clearCode = (): void => { + setCode(DEFAULT_CODE); + showToast('Reset to default template'); + }; + + const copyCode = async (): Promise => { + try { + await navigator.clipboard.writeText(code); + showToast('Code copied to clipboard'); + } catch { + showToast('Unable to copy'); + } + }; + + const downloadCode = (): void => { + const blob = new Blob([code], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'Main.java'; + document.body.appendChild(a); + a.click(); + a.remove(); + URL.revokeObjectURL(url); + showToast('Downloaded Main.java'); + }; + + const EXAMPLES: Record = { + 'Hello World': `public class Main { + public static void main(String[] args) { + System.out.println("Hello,Welcome to Javaistic PlayGround"); + } +}`, + 'For Loop': `public class Main { + public static void main(String[] args) { + for (int i = 1; i <= 5; i++) { + System.out.println("Count: " + i); + } + } +}`, + 'Simple Calculator': `public class Main { + public static void main(String[] args) { + int a = 10; + int b = 5; + System.out.println("Sum = " + (a + b)); + } +}`, + 'Prime Number Check': `public class Main { + public static void main(String[] args) { + int num = 17; + boolean isPrime = num > 1; + for (int i = 2; i <= Math.sqrt(num); i++) { + if (num % i == 0) { + isPrime = false; + break; + } + } + System.out.println(num + (isPrime ? " is Prime" : " is Not Prime")); + } +}`, + 'Odd Even Check': `public class Main { + public static void main(String[] args) { + int num = 24; + System.out.println(num + (num % 2 == 0 ? " is Even" : " is Odd")); + } +}`, +}; + + const loadExample = (key: string): void => { + setCode(EXAMPLES[key]); + setExamplesOpen(false); + showToast(`${key} loaded`); + }; + + const clearOutput = (): void => setOutput(''); + + const getLineNumbers = (text: string): string => + Array.from({ length: Math.max(1, text.split('\n').length) }, (_, i) => i + 1).join('\n'); + + useEffect(() => { + const t = textareaRef.current; + const l = lineRef.current; + if (!t || !l) return; + const sync = () => { + l.scrollTop = t.scrollTop; + }; + t.addEventListener('scroll', sync); + return () => t.removeEventListener('scroll', sync); + }, []); + + return ( + <> + +
+
+

JAVAISTIC PLAYGROUND

+ +
+
+
+ Main.java +
+ + + + +
+ + {examplesOpen && (
    + {Object.keys(EXAMPLES).map((key) => ( +
  • + +
  • + ))} +
+ )} +
+ +
+
+ + +
+
}className="select-none text-right px-3 py-3 text-sm overflow-hidden"style={{width: 50,}}>{getLineNumbers(code)}
+ +