├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── new-framework-support.md └── workflows │ └── ci.yaml ├── .gitignore ├── LICENSE ├── README.md ├── all-in-one ├── .eslintrc.json ├── .gitignore ├── .vscode │ └── settings.json ├── README.md ├── bootstrap_projects │ └── react │ │ ├── .gitignore │ │ ├── index.html │ │ ├── package.json │ │ ├── public │ │ └── vite.svg │ │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── assets │ │ │ └── react.svg │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ └── vite.config.ts ├── next.config.js ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public │ ├── next.svg │ ├── thirteen.svg │ └── vercel.svg ├── src │ ├── app │ │ ├── api │ │ │ └── bootfs │ │ │ │ └── route.ts │ │ ├── bootWebContainer.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── page.tsx │ └── components │ │ ├── Editor │ │ ├── Editor.tsx │ │ ├── FileTree.tsx │ │ ├── Terminal.css │ │ ├── Terminal.tsx │ │ ├── svelte-sfc.ts │ │ └── vue-sfc.ts │ │ ├── Select │ │ └── Select.tsx │ │ └── TopPanel │ │ └── TopPanel.tsx ├── tailwind.config.js ├── tsconfig.json ├── typings.d.ts └── vercel.json ├── kotlin-repl ├── .gitignore ├── HELP.md ├── README.md ├── _samples │ ├── .gitignore │ ├── Dependencies.main.kts │ ├── Kotless.main.kts │ ├── KotlessKtor.main.kts │ ├── KotlessKtor2.main.kts │ ├── KotlessSpring.main.kts │ ├── README.md │ ├── TwoClasses.kts │ └── local-test.ipynb ├── build.gradle.kts ├── docs │ └── jupyter-kotlin.md ├── gradle.properties ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── resources │ └── application.properties ├── settings.gradle.kts └── src │ ├── main │ ├── kotlin │ │ └── org │ │ │ └── clickprompt │ │ │ └── unitserver │ │ │ ├── UnitServerApplication.kt │ │ │ ├── interpreter │ │ │ ├── KotlinInterpreter.kt │ │ │ ├── api │ │ │ │ ├── InterpreterContext.kt │ │ │ │ └── InterpreterRequest.kt │ │ │ └── compiler │ │ │ │ ├── CustomLibraryResolver.kt │ │ │ │ ├── ExtendLibraries.kt │ │ │ │ ├── KotlinReplWrapper.kt │ │ │ │ └── SimpleLibraryDefinition.kt │ │ │ ├── magic │ │ │ ├── LangCodeWrapper.kt │ │ │ ├── SimpleMagicMatcher.kt │ │ │ └── lang │ │ │ │ ├── KtorLangBuilder.kt │ │ │ │ ├── LangBuilder.kt │ │ │ │ └── SpringLangBuilder.kt │ │ │ ├── messaging │ │ │ ├── Message.kt │ │ │ └── MessageType.kt │ │ │ └── socket │ │ │ ├── UnitServerSocketHandler.kt │ │ │ └── WebsocketConfig.kt │ └── resources │ │ └── application.properties │ └── test │ └── kotlin │ └── org │ └── clickprompt │ └── unitserver │ ├── UnitServerApplicationTests.kt │ ├── interpreter │ └── KotlinInterpreterTest.kt │ └── magic │ ├── LangCodeWrapperTest.kt │ └── SimpleMagicMatcherTest.kt ├── react-repl ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode │ └── settings.json ├── README.md ├── common │ ├── compile.ts │ └── unit.types.ts ├── next.config.js ├── nodemon.json ├── package-lock.json ├── package.json ├── public │ ├── next.svg │ ├── thirteen.svg │ └── vercel.svg ├── server.ts ├── src │ └── app │ │ ├── api │ │ └── hello │ │ │ └── route.ts │ │ ├── components │ │ └── editor │ │ │ ├── CodeEditor.tsx │ │ │ ├── UseCodeEditor.tsx │ │ │ ├── UseCodeMirror.tsx │ │ │ └── editor.types.ts │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── page.module.css │ │ └── page.tsx ├── tsconfig.json └── tsconfig.server.json ├── rust-wasm ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ └── main.rs └── typescript-repl ├── README.md ├── deno.json ├── deno.lock ├── flowrepl ├── TypescriptInterpreter.ts ├── api │ ├── InterpreterContext.ts │ ├── InterpreterRequest.ts │ └── InterpreterService.ts └── messaging │ └── Message.ts ├── logger.ts ├── main.ts └── ws └── flowReplHander.ts /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Env (please complete the following information):** 14 | - OS: [e.g. iOS] 15 | - Env: Docker compose? Local dev? 16 | - Browser [e.g. chrome, safari] 17 | - Version [e.g. 22] 18 | 19 | 20 | **To Reproduce** 21 | Steps to reproduce the behavior: 22 | 1. Go to '...' 23 | 2. Click on '....' 24 | 3. Scroll down to '....' 25 | 4. See error 26 | 27 | **Expected behavior** 28 | A clear and concise description of what you expected to happen. 29 | 30 | **Screenshots** 31 | If applicable, add screenshots to help explain your problem. 32 | 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new-framework-support.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New framework support 3 | about: new framework support 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Which language would you to support** 11 | xxx 12 | 13 | **Example of this language HTTP API syntax** 14 | xxx 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | os: [ macos-latest, ubuntu-latest, windows-latest ] 10 | runs-on: ${{ matrix.os }} 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-java@v3 15 | with: 16 | distribution: 'zulu' 17 | java-version: '17' 18 | 19 | - name: Setup Gradle 20 | uses: gradle/gradle-build-action@v2 21 | with: 22 | arguments: build 23 | build-root-directory: kotlin-repl 24 | 25 | - name: Execute Gradle build 26 | run: ./gradlew build 27 | working-directory: kotlin-repl 28 | 29 | - name: Execute Gradle Coverage 30 | run: ./gradlew check 31 | working-directory: kotlin-repl 32 | 33 | - name: Execute Gradle Coverage 34 | if: runner.os == 'macOS' 35 | run: bash <(curl -s https://codecov.io/bash) 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022~ ArchGuard Org. 4 | Copyright (c) 2023~ ClickPrompt Org. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unit Runtime 2 | 3 | [![CI](https://github.com/prompt-engineering/unit-runtime/actions/workflows/ci.yaml/badge.svg)](https://github.com/prompt-engineering/unit-runtime/actions/workflows/ci.yaml) 4 | 5 | > Unit Runtime is an efficient and user-friendly AI code execution environment that allows for one-click startup and real-time interaction, helping you quickly build and test AI code. 6 | 7 | Workflow: 8 | 9 | ```mermaid 10 | sequenceDiagram 11 | participant Human 12 | participant LLM/ChatGPT 13 | participant Unit Runtime 14 | participant Language REPL 15 | 16 | Human->>+LLM/ChatGPT: Provide prompt for generated code 17 | LLM/ChatGPT->>+Unit Runtime: Provide code snippets/units 18 | Unit Runtime->>+Language REPL: Compile and return 19 | Language REPL-->>-Unit Runtime: Output result 20 | Unit Runtime-->>-LLM/ChatGPT: Display processing result 21 | LLM/ChatGPT-->>-Human: Verify/modify code 22 | ``` 23 | 24 | For examples: 25 | 26 | 1. generate a "Hello, world" and send to Kotlin Repl 27 | 28 | ```kotlin 29 | @RestController 30 | object Pages { 31 | @GetMapping("/") 32 | fun main() = "Hello World!" 33 | } 34 | ``` 35 | 36 | 2. will start a server like: [http://localhost:10043](http://localhost:10043) , you can test and verify it. 37 | 38 | ## Websocket API 39 | 40 | server: `ws://localhost:8080/repl` 41 | 42 | input: 43 | 44 | ```kotlin 45 | @Serializable 46 | data class InterpreterRequest( 47 | var id: Int = -1, 48 | val code: String, 49 | val language: String = "kotlin", 50 | val framework: String = "spring", 51 | val history: Boolean = false 52 | ) 53 | ``` 54 | 55 | output: 56 | 57 | ```kotlin 58 | @Serializable 59 | data class Message( 60 | var id: Int = -1, 61 | var resultValue: String, 62 | var className: String = "", 63 | var msgType: MessageType = MessageType.NONE, 64 | var content: MessageContent? = null, 65 | ) 66 | ``` 67 | 68 | ## Todos 69 | 70 | - Backend 71 | - [x] Kotlin 72 | - [x] basic REPL 73 | - [ ] Spring Boot framework based on [https://github.com/JetBrains/kotless](https://github.com/JetBrains/kotless) 74 | - [x] Ktor framework based on [https://github.com/JetBrains/kotless](https://github.com/JetBrains/kotless) 75 | - [x] TypeScript 76 | - [x] basic REPL 77 | - [x] Deno with Hono 78 | - [ ] Java 79 | - [ ] Python 80 | - [x] Frontend 81 | - [x] React with TypeScript and Babel 82 | - [ ] Vue 83 | - [ ]Angular 84 | 85 | ## Development 86 | 87 | 1. git clone `https://github.com/prompt-engineering/unit-runtime` 88 | 2. `./gradlew bootRun` 89 | 90 | API: 91 | 92 | 93 | ## LICENSE 94 | 95 | This code is distributed under the MIT license. See [LICENSE](./LICENSE) in this directory. 96 | -------------------------------------------------------------------------------- /all-in-one/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /all-in-one/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /all-in-one/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[typescript]": { 3 | "editor.defaultFormatter": "esbenp.prettier-vscode" 4 | }, 5 | "[typescriptreact]": { 6 | "editor.defaultFormatter": "esbenp.prettier-vscode" 7 | }, 8 | "editor.formatOnSave": true, 9 | "typescript.tsdk": "node_modules\\typescript\\lib", 10 | "typescript.enablePromptUseWorkspaceTsdk": true 11 | } -------------------------------------------------------------------------------- /all-in-one/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 18 | 19 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 20 | 21 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 22 | 23 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 24 | 25 | ## Learn More 26 | 27 | To learn more about Next.js, take a look at the following resources: 28 | 29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 31 | 32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 33 | 34 | ## Deploy on Vercel 35 | 36 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 37 | 38 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 39 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootfs__react", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.28", 17 | "@types/react-dom": "^18.0.11", 18 | "@vitejs/plugin-react": "^3.1.0", 19 | "typescript": "^4.9.3", 20 | "vite": "^4.2.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import reactLogo from "./assets/react.svg"; 3 | import viteLogo from "/vite.svg"; 4 | import "./App.css"; 5 | 6 | function App() { 7 | const [count, setCount] = useState(0); 8 | 9 | return ( 10 |
11 |
12 | 13 | Vite logo 14 | 15 | 16 | React logo 17 | 18 |
19 |

Vite + React

20 |
21 | 24 |

25 | Edit src/App.tsx and save to test HMR 26 |

27 |
28 |

29 | Click on the Vite and React logos to learn more 30 |

31 |
32 | ); 33 | } 34 | 35 | export default App; 36 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | @media (prefers-color-scheme: light) { 59 | :root { 60 | color: #213547; 61 | background-color: #ffffff; 62 | } 63 | a:hover { 64 | color: #747bff; 65 | } 66 | button { 67 | background-color: #f9f9f9; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /all-in-one/bootstrap_projects/react/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /all-in-one/next.config.js: -------------------------------------------------------------------------------- 1 | const { DefinePlugin } = require("webpack"); 2 | 3 | 4 | /** @type {import('next').NextConfig} */ 5 | const nextConfig = { 6 | experimental: { 7 | appDir: true, 8 | }, 9 | headers: async () => { 10 | return[ 11 | { 12 | "source": "/(.*)", 13 | "headers": [ 14 | { 15 | "key": "Cross-Origin-Embedder-Policy", 16 | "value": "require-corp" 17 | }, 18 | { 19 | "key": "Cross-Origin-Opener-Policy", 20 | "value": "same-origin" 21 | } 22 | ] 23 | } 24 | ] 25 | }, 26 | 27 | webpack(config) { 28 | config.plugins.push( 29 | new DefinePlugin({ 30 | "__PROJ_ROOT__": JSON.stringify(__dirname), 31 | }) 32 | ); 33 | 34 | return config; 35 | } 36 | 37 | // https://github.com/vercel/next.js/issues/33863#issuecomment-1140518693 38 | 39 | } 40 | 41 | module.exports = nextConfig 42 | -------------------------------------------------------------------------------- /all-in-one/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "all-in-one", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "preinstall": "npx only-allow pnpm", 7 | "dev": "next dev", 8 | "build": "next build", 9 | "start": "next start", 10 | "lint": "next lint", 11 | "format": "prettier --write -u ./src" 12 | }, 13 | "dependencies": { 14 | "@emotion/react": "^11.10.6", 15 | "@emotion/styled": "^11.10.6", 16 | "@fortawesome/fontawesome-svg-core": "^6.4.0", 17 | "@fortawesome/free-regular-svg-icons": "^6.4.0", 18 | "@fortawesome/free-solid-svg-icons": "^6.4.0", 19 | "@fortawesome/react-fontawesome": "^0.2.0", 20 | "@monaco-editor/react": "^4.5.0-beta.0", 21 | "@mui/lab": "5.0.0-alpha.124", 22 | "@mui/material": "^5.11.15", 23 | "@webcontainer/api": "^1.1.0", 24 | "client-only": "^0.0.1", 25 | "monaco-editor": "^0.36.1", 26 | "next": "13.2.4", 27 | "react": "18.2.0", 28 | "react-dom": "18.2.0", 29 | "server-only": "^0.0.1", 30 | "typescript": "5.0.2", 31 | "webpack": "^5.77.0", 32 | "xterm": "^5.1.0", 33 | "xterm-addon-fit": "^0.7.0" 34 | }, 35 | "devDependencies": { 36 | "@types/node": "18.15.11", 37 | "@types/react": "18.0.31", 38 | "@types/react-dom": "18.0.11", 39 | "autoprefixer": "^10.4.14", 40 | "eslint": "8.37.0", 41 | "eslint-config-next": "13.2.4", 42 | "postcss": "^8.4.21", 43 | "prettier": "^2.8.7", 44 | "tailwindcss": "^3.3.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /all-in-one/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /all-in-one/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /all-in-one/public/thirteen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /all-in-one/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /all-in-one/src/app/api/bootfs/route.ts: -------------------------------------------------------------------------------- 1 | const SUPPORTED_FRAMEWORK = ["react", "vue", "svelte"] as const; 2 | 3 | export async function GET(request: Request) { 4 | const url = new URL(request.url); 5 | const framework = url.searchParams.get("framework"); 6 | if (!SUPPORTED_FRAMEWORK.includes(framework as any)) { 7 | return new Response("wrong param", { status: 400 }); 8 | } 9 | 10 | const result = await getBootstrapContents(framework as any); 11 | return new Response(JSON.stringify(result)); 12 | } 13 | 14 | import { relative, resolve, normalize } from "node:path"; 15 | import { readdir, readFile } from "node:fs/promises"; 16 | 17 | async function getBootstrapContents(ty: (typeof SUPPORTED_FRAMEWORK)[number]) { 18 | if (!SUPPORTED_FRAMEWORK.includes(ty)) { 19 | throw new Error("unsupported framework"); 20 | } 21 | 22 | const contentRoot = resolve(__PROJ_ROOT__, "bootstrap_projects", ty); 23 | 24 | // read the directory recursively and get all the files's content 25 | async function getContentsRecursively(dir: string) { 26 | let result: any = {}; 27 | 28 | const files = await readdir(dir, { encoding: "utf8", withFileTypes: true }); 29 | for (const file of files) { 30 | if (file.isDirectory()) { 31 | result[file.name] = { 32 | ...(await getContentsRecursively(resolve(dir, file.name))), 33 | _$__Ty__: "dir", 34 | path: normalize( 35 | relative(contentRoot, resolve(dir, file.name)) 36 | ).replaceAll("\\", "/"), 37 | }; 38 | } else { 39 | result[file.name] = { 40 | _$__Ty__: "file", 41 | path: normalize( 42 | relative(contentRoot, resolve(dir, file.name)) 43 | ).replaceAll("\\", "/"), 44 | content: await readFile(resolve(dir, file.name), { 45 | encoding: "utf8", 46 | }), 47 | }; 48 | } 49 | } 50 | 51 | return result; 52 | } 53 | 54 | return getContentsRecursively(contentRoot); 55 | } 56 | -------------------------------------------------------------------------------- /all-in-one/src/app/bootWebContainer.ts: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import "client-only"; 4 | 5 | import { WebContainer } from "@webcontainer/api"; 6 | import { resolve } from "path"; 7 | 8 | let __webcontainerInstance: WebContainer | null; 9 | 10 | async function init(container: WebContainer): Promise { 11 | await container.fs.mkdir("/tmp"); 12 | 13 | // init fs 14 | await container.mount({}, { mountPoint: "/tmp" }); 15 | 16 | container.on("server-ready", (port, url) => { 17 | console.log("server ready", port, url); 18 | }); 19 | return container; 20 | } 21 | 22 | export async function initFs( 23 | container: WebContainer, 24 | bootfs: any, 25 | clean = true 26 | ) { 27 | const root = "/tmp/scratch"; 28 | 29 | if (clean) { 30 | try { 31 | await container.fs.rm(root, { recursive: true }); 32 | } catch { 33 | } finally { 34 | await container.fs.mkdir(root, { recursive: true }); 35 | } 36 | } 37 | 38 | if (typeof bootfs !== "object") { 39 | throw new Error("invalid bootfs"); 40 | } 41 | 42 | if (Object.keys(bootfs).length === 0) { 43 | throw new Error("empty bootfs"); 44 | } 45 | 46 | for (const [key, value] of Object.entries(bootfs)) { 47 | if (key === "_$__Ty__") { 48 | continue; 49 | } 50 | 51 | if ((value as any)._$__Ty__ === "file") { 52 | await container.fs.writeFile( 53 | resolve(root, (value as any).path), 54 | (value as any).content 55 | ); 56 | } else if ((value as any)._$__Ty__ === "dir") { 57 | await container.fs.mkdir(resolve(root, (value as any).path), { 58 | recursive: true, 59 | }); 60 | await initFs(container, value, false); 61 | } else if (key === "path") { 62 | /** ignore */ 63 | } else { 64 | throw new Error("invalid bootfs"); 65 | } 66 | } 67 | } 68 | 69 | export const initInstance = async () => { 70 | if (!__webcontainerInstance) { 71 | __webcontainerInstance = await new Promise((resolve, reject) => { 72 | console.log("booting webcontainer"); 73 | WebContainer.boot() 74 | .then((it) => { 75 | console.log("webcontainer booted"); 76 | return resolve(init(it)); 77 | }) 78 | .catch((err) => reject(err)); 79 | }); 80 | } 81 | 82 | return __webcontainerInstance!; 83 | }; 84 | 85 | export const destroyInstance = () => { 86 | __webcontainerInstance?.teardown(); 87 | __webcontainerInstance = null; 88 | }; 89 | 90 | const getInstance = async () => { 91 | return __webcontainerInstance; 92 | }; 93 | export type GetInstance = typeof getInstance; 94 | export default getInstance; 95 | -------------------------------------------------------------------------------- /all-in-one/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unit-mesh/unit-runtime/7768bde4283e09269a0e7475e8144fd19fe3b760/all-in-one/src/app/favicon.ico -------------------------------------------------------------------------------- /all-in-one/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | margin: 0; 10 | padding: 0; 11 | box-sizing: border-box; 12 | } -------------------------------------------------------------------------------- /all-in-one/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | 3 | export const metadata = { 4 | title: "Create Next App", 5 | description: "Generated by create next app", 6 | }; 7 | 8 | export default function RootLayout({ 9 | children, 10 | }: { 11 | children: React.ReactNode; 12 | }) { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /all-in-one/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import "client-only"; 3 | 4 | import TopPanel from "@/components/TopPanel/TopPanel"; 5 | import Editor from "@/components/Editor/Editor"; 6 | import { useEffect, useState } from "react"; 7 | 8 | import getInstance, { 9 | initFs, 10 | destroyInstance, 11 | initInstance, 12 | } from "./bootWebContainer"; 13 | import FileTree from "@/components/Editor/FileTree"; 14 | import { WebContainer, WebContainerProcess } from "@webcontainer/api"; 15 | 16 | const languageMap = new Map([ 17 | ["html", "html"], 18 | ["css", "css"], 19 | ["js", "javascript"], 20 | ["ts", "typescript"], 21 | ["vue", "vue-sfc"], 22 | ["svelte", "svelte-sfc"], 23 | ["jsx", "javascript"], 24 | ["tsx", "typescript"], 25 | ]); 26 | 27 | export default function Home() { 28 | const [webcontainer, setWebcontainer] = useState(null); 29 | const [framework, setFramework] = useState("React"); 30 | 31 | const [files, setFiles] = useState([]); 32 | const [currentFile, setCurrentFile] = useState(null); 33 | const [currentLanguage, setCurrentLanguage] = useState("javascript"); 34 | 35 | useEffect(() => { 36 | initInstance() 37 | .then((webcontainer) => { 38 | setWebcontainer(webcontainer); 39 | }) 40 | .catch((e) => {}); 41 | }, []); 42 | 43 | const [input, setInput] = useState(""); 44 | useEffect(() => { 45 | console.log("input changed", input); 46 | }, [input]); 47 | 48 | useEffect(() => { 49 | (async () => { 50 | if (!webcontainer) return; 51 | 52 | console.log("framework changed", framework); 53 | const result = await fetch( 54 | "/api/bootfs?framework=" + framework.toLowerCase() 55 | ).then((it) => it.json()); 56 | 57 | console.log("??", webcontainer); 58 | if (webcontainer) { 59 | console.log("init fs"); 60 | await initFs(webcontainer, result); 61 | setFiles(await refreshTree()); 62 | } 63 | })(); 64 | }, [framework, webcontainer]); 65 | 66 | return ( 67 |
68 | 69 |
70 | { 73 | refreshTree().then(setFiles); 74 | }} 75 | downloadDeps={() => installDeps()} 76 | onSelected={(item) => { 77 | if (item.type === "file") { 78 | webcontainer?.fs.readFile(item.path, "utf-8").then(setInput); 79 | setCurrentFile(item); 80 | 81 | const file = item.name; 82 | for (const [ext, lang] of languageMap) { 83 | if (file.endsWith("." + ext)) { 84 | setCurrentLanguage(lang); 85 | break; 86 | } 87 | } 88 | } 89 | }} 90 | /> 91 | 98 |
99 |
100 | ); 101 | } 102 | 103 | import type { FileTreeItem } from "@/components/Editor/FileTree"; 104 | async function refreshTree(): Promise { 105 | const webcontainer = await getInstance(); 106 | if (!webcontainer) return []; 107 | 108 | const listDir: (path: string) => Promise = async ( 109 | path: string 110 | ) => { 111 | const r = await webcontainer?.fs.readdir(path, { 112 | withFileTypes: true, 113 | }); 114 | 115 | if (!r) return []; 116 | 117 | return Promise.all( 118 | r.map(async (it) => { 119 | const fullPath = path + "/" + it.name; 120 | if (it.isDirectory()) { 121 | return { 122 | path: fullPath, 123 | name: it.name, 124 | type: "folder", 125 | children: await listDir(fullPath), 126 | }; 127 | } else { 128 | return { 129 | path: fullPath, 130 | name: it.name, 131 | type: "file", 132 | }; 133 | } 134 | }) 135 | ); 136 | }; 137 | const result = await listDir("/tmp/scratch"); 138 | console.log("listDir result", result); 139 | return result; 140 | } 141 | 142 | async function installDeps() { 143 | const webcontainer = await getInstance(); 144 | 145 | const x = async ( 146 | webcontainer: WebContainer, 147 | command: string, 148 | args: string[] = [] 149 | ) => { 150 | console.log("running", command, args); 151 | const process = await webcontainer.spawn(command, args, { 152 | output: true, 153 | terminal: { 154 | cols: 80, 155 | rows: 32, 156 | }, 157 | }); 158 | const reader = process.output.pipeTo( 159 | new WritableStream({ 160 | write(chunk) { 161 | console.log("[Output] ", chunk); 162 | }, 163 | }) 164 | ); 165 | 166 | await process.kill(); 167 | }; 168 | 169 | console.log("starting install deps"); 170 | 171 | if (!webcontainer) return; 172 | 173 | await x(webcontainer, "cd", ["./tmp/scratch"]); 174 | await x(webcontainer, "ls", ["/usr/local/bin"]); 175 | 176 | // await x(webcontainer, "ls"); 177 | // await x(webcontainer, "npm", ["install"]); 178 | } 179 | -------------------------------------------------------------------------------- /all-in-one/src/components/Editor/Editor.tsx: -------------------------------------------------------------------------------- 1 | import MonacoEditor from "@monaco-editor/react"; 2 | import type { editor } from "monaco-editor"; 3 | import type { Monaco } from "@monaco-editor/react"; 4 | 5 | import { useEffect, useRef } from "react"; 6 | 7 | import Terminal from "./Terminal"; 8 | 9 | import { vueSfc } from "./vue-sfc"; 10 | import { svelteSfc } from "./svelte-sfc"; 11 | import getInstance from "@/app/bootWebContainer"; 12 | import { WebContainer } from "@webcontainer/api"; 13 | import type { FileTreeItem } from "./FileTree"; 14 | 15 | export type EditorProps = { 16 | input: string; 17 | language: string; 18 | file: FileTreeItem | null; 19 | webcontainer: WebContainer | null; 20 | onChange: (value: string) => void; 21 | }; 22 | export default function Editor({ 23 | input = "", 24 | language = "javascript", 25 | file, 26 | webcontainer, 27 | onChange = () => {}, 28 | }: EditorProps) { 29 | const monaco$ = useRef(null); 30 | const editor$ = useRef(null); 31 | 32 | useEffect(() => { 33 | if (monaco$.current && editor$.current) { 34 | const model = monaco$.current.editor.createModel( 35 | input, 36 | language, 37 | monaco$.current.Uri.parse(file?.name ?? "") 38 | ); 39 | 40 | editor$.current.setModel(model); 41 | 42 | if (file?.name.endsWith(".jsx") || file?.name.endsWith(".tsx")) { 43 | monaco$.current.languages.typescript.typescriptDefaults.setCompilerOptions( 44 | { 45 | jsx: monaco$.current.languages.typescript.JsxEmit.React, 46 | } 47 | ); 48 | } 49 | } 50 | }, [language, file]); 51 | 52 | useEffect(() => { 53 | if (monaco$.current) { 54 | monaco$.current.languages.register({ id: "vue-sfc" }); 55 | monaco$.current.languages.setLanguageConfiguration("vue-sfc", { 56 | brackets: [["<", ">"]], 57 | autoClosingPairs: [ 58 | { open: "<", close: ">" }, 59 | { open: '"', close: '"' }, 60 | { open: "'", close: "'" }, 61 | { open: "`", close: "`" }, 62 | ], 63 | }); 64 | monaco$.current.languages.setTokensProvider("vue-sfc", vueSfc); 65 | 66 | monaco$.current.languages.register({ id: "svelte-sfc" }); 67 | 68 | monaco$.current.languages.setLanguageConfiguration("svelte-sfc", { 69 | brackets: [["<", ">"]], 70 | autoClosingPairs: [ 71 | { open: "<", close: ">" }, 72 | { open: '"', close: '"' }, 73 | { open: "'", close: "'" }, 74 | { open: "`", close: "`" }, 75 | ], 76 | }); 77 | 78 | monaco$.current.languages.setTokensProvider("svelte-sfc", svelteSfc); 79 | } 80 | }, [monaco$.current !== null]); 81 | 82 | const onInputChange = (value: string | undefined) => { 83 | onChange(value ?? ""); 84 | }; 85 | 86 | return ( 87 |
{ 90 | if (e.key === "s" && e.ctrlKey) { 91 | e.preventDefault(); 92 | if (file) { 93 | console.log("save", file, input, webcontainer); 94 | webcontainer?.fs.writeFile(file.path, input); 95 | } 96 | } 97 | }}> 98 | { 100 | editor$.current = editor; 101 | monaco$.current = monaco; 102 | }} 103 | options={{ 104 | fontSize: 16, 105 | minimap: { enabled: true }, 106 | }} 107 | language={language} 108 | value={input} 109 | onChange={onInputChange} 110 | /> 111 |
112 | 119 | 120 | 121 |
122 |
123 | ); 124 | } 125 | -------------------------------------------------------------------------------- /all-in-one/src/components/Editor/FileTree.tsx: -------------------------------------------------------------------------------- 1 | import { IconButton } from "@mui/material"; 2 | import TreeView from "@mui/lab/TreeView"; 3 | import TreeItem, { TreeItemProps } from "@mui/lab/TreeItem"; 4 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 5 | import { faRefresh, faDownload } from "@fortawesome/free-solid-svg-icons"; 6 | 7 | export type FileTreeItem = { 8 | path: string; 9 | name: string; 10 | type: "file" | "folder"; 11 | children?: FileTreeItem[]; 12 | }; 13 | 14 | export type Props = { 15 | items: FileTreeItem[]; 16 | refresh: () => void; 17 | downloadDeps: () => void; 18 | onSelected: (item: FileTreeItem) => void; 19 | }; 20 | 21 | export default function FileTree({ 22 | items = [], 23 | refresh, 24 | onSelected, 25 | downloadDeps, 26 | }: Props) { 27 | return ( 28 |
29 |
30 | {/* top */} 31 | { 33 | refresh?.(); 34 | }}> 35 | 36 | 37 | { 39 | downloadDeps?.(); 40 | }}> 41 | 42 | 43 |
44 | { 47 | const item = findItem(items, id); 48 | if (item) { 49 | onSelected(item); 50 | } 51 | }}> 52 | {} 53 | 54 |
55 | ); 56 | } 57 | 58 | function findItem(items: FileTreeItem[], path: string): FileTreeItem | null { 59 | for (const item of items) { 60 | if (item.path === path) return item; 61 | if (item.children) { 62 | const r = findItem(item.children, path); 63 | if (r) return r; 64 | } 65 | } 66 | 67 | return null; 68 | } 69 | 70 | function Items({ items = [] }: { items: FileTreeItem[] | undefined }) { 71 | if (!items) return <>; 72 | 73 | return ( 74 | <> 75 | {items.map((item) => { 76 | return ( 77 | } 82 | /> 83 | ); 84 | })} 85 | 86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /all-in-one/src/components/Editor/Terminal.css: -------------------------------------------------------------------------------- 1 | .terminal-container { 2 | /* this is important */ 3 | overflow: hidden; 4 | } 5 | 6 | .xterm .xterm-viewport { 7 | /* see : https://github.com/xtermjs/xterm.js/issues/3564#issuecomment-1004417440 */ 8 | width: initial !important; 9 | } -------------------------------------------------------------------------------- /all-in-one/src/components/Editor/Terminal.tsx: -------------------------------------------------------------------------------- 1 | import { WebContainer, WebContainerProcess } from "@webcontainer/api"; 2 | import { useEffect, useRef, useState } from "react"; 3 | import { Terminal as Xterm } from "xterm"; 4 | import { FitAddon } from "xterm-addon-fit"; 5 | 6 | import "xterm/css/xterm.css"; 7 | import "./Terminal.css"; 8 | 9 | export type TerminalProps = { 10 | className?: string; 11 | webcontainer: WebContainer | null; 12 | }; 13 | 14 | export default function Terminal({ className, webcontainer }: TerminalProps) { 15 | const terminal$ = useRef(null); 16 | const [term, setTerm] = useState(null); 17 | 18 | useEffect(() => { 19 | if (terminal$.current && webcontainer) { 20 | const term = new Xterm({ 21 | convertEol: true, 22 | 23 | scrollOnUserInput: true, 24 | cursorBlink: true, 25 | cursorStyle: "block", 26 | fontFamily: "Fira Code", 27 | fontSize: 14, 28 | lineHeight: 1.5, 29 | theme: { 30 | background: "#fafafa", 31 | foreground: "#18181b", 32 | }, 33 | }); 34 | const fitAddon = new FitAddon(); 35 | term.loadAddon(fitAddon); 36 | 37 | term.open(terminal$.current); 38 | document.body.addEventListener("resize", () => { 39 | fitAddon.fit(); 40 | }); 41 | setTimeout(() => { 42 | fitAddon.fit(); 43 | }, 500); 44 | 45 | let shellProcess: WebContainerProcess | null = null; 46 | 47 | console.log("spawn jsh", webcontainer); 48 | webcontainer.spawn("jsh", {}).then(async (proc) => { 49 | shellProcess = proc; 50 | proc.output.pipeTo( 51 | new WritableStream({ 52 | write(data) { 53 | console.log( 54 | "proc.output", 55 | data 56 | // new TextEncoder() 57 | // .encode(data) 58 | // .reduce((t, x) => t + x.toString(16).padStart(2, "0"), "") 59 | ); 60 | 61 | if (data === "\x1b\x5b\x3f\x32\x30\x30\x34\x68") { 62 | console.log("ignore"); 63 | return; 64 | } 65 | term.write(data); 66 | }, 67 | }) 68 | ); 69 | 70 | const writer = proc.input.getWriter(); 71 | setTimeout(() => { 72 | term.clear(); 73 | writer.write("clear\n"); 74 | }, 2500); 75 | term.onData((data) => { 76 | console.log("term.onData", data); 77 | writer.write(data); 78 | }); 79 | }); 80 | setTerm(term); 81 | 82 | return () => { 83 | term.dispose(); 84 | setTerm(null); 85 | if (terminal$.current) { 86 | terminal$.current.innerHTML = ""; 87 | } 88 | shellProcess?.kill(); 89 | }; 90 | } 91 | }, [webcontainer]); 92 | 93 | return ( 94 |
97 | ); 98 | } 99 | -------------------------------------------------------------------------------- /all-in-one/src/components/Editor/svelte-sfc.ts: -------------------------------------------------------------------------------- 1 | // generated by GPT-4 2 | 3 | class SvelteSFCState { 4 | constructor(public state: string) {} 5 | 6 | clone(): SvelteSFCState { 7 | return new SvelteSFCState(this.state); 8 | } 9 | 10 | equals(other: SvelteSFCState): boolean { 11 | return this.state === other.state; 12 | } 13 | } 14 | const SvelteSFCStateHtml = new SvelteSFCState("html"); 15 | const SvelteSFCStateJavascript = new SvelteSFCState("javascript"); 16 | const SvelteSFCStateCss = new SvelteSFCState("css"); 17 | 18 | export const svelteSfc: any = { 19 | getInitialState: function () { 20 | return SvelteSFCStateHtml.clone(); 21 | }, 22 | tokenize: function (line: any, state: SvelteSFCState) { 23 | let tokens: any = []; 24 | let offset = 0; 25 | 26 | function addToken(type: any, length: any) { 27 | tokens.push({ 28 | startIndex: offset, 29 | scopes: type, 30 | }); 31 | offset += length; 32 | } 33 | 34 | if (state.equals(SvelteSFCStateHtml)) { 35 | if (line.trim().startsWith(" {}, 15 | }: { 16 | options: string[]; 17 | selected: string; 18 | onSelected: (value: string) => void; 19 | }) { 20 | const [isOpen, setIsOpen] = useState(false); 21 | 22 | return ( 23 |
24 |
25 |
26 |
27 |
28 | 33 | 34 |
35 | 42 |
43 |
44 |
45 | {isOpen && ( 46 |
47 |
48 | {options.map((item) => ( 49 |
{ 54 | setIsOpen(false); 55 | onSelected(item); 56 | }}> 57 |
58 |
59 |
{item}
60 |
61 |
62 |
63 | ))} 64 |
65 |
66 | )} 67 |
68 |
69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /all-in-one/src/components/TopPanel/TopPanel.tsx: -------------------------------------------------------------------------------- 1 | import { Select } from "../Select/Select"; 2 | 3 | const SUPPORTED_FRAMEWORKS = ["React", "Vue", "Svelte"]; 4 | 5 | export default function TopPanel({ 6 | framework = "React", 7 | onFrameworkSelected = (framework) => {}, 8 | }: { 9 | framework: string; 10 | onFrameworkSelected: (framework: string) => void; 11 | }) { 12 | return ( 13 |
14 |
Choose the framework
15 |