├── .github ├── FUNDING.yml └── workflows │ └── static.yml ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── RELEASE.md ├── biome.json ├── bun.lockb ├── lib ├── .swcrc ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── brotli_stream.ts │ ├── get_llvm_wasm.ts │ ├── get_rustc_wasm.ts │ ├── get_wasm.ts │ ├── index.ts │ ├── parse_tar.ts │ └── sysroot.ts ├── tsconfig.json └── vite.config.ts ├── package-lock.json ├── package.json └── page ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── src ├── App.tsx ├── btn.tsx ├── cat.ts ├── cmd_parser.ts ├── compile_and_run.ts ├── config.ts ├── ctx.ts ├── index.css ├── index.tsx ├── monaco_worker.ts ├── solid_xterm.tsx ├── sysroot.ts ├── wasm │ ├── lsr.wasm │ └── tre.wasm ├── worker_process │ ├── llvm.ts │ ├── rustc.ts │ ├── thread_spawn.ts │ ├── util_cmd.ts │ └── worker.ts └── xterm.tsx ├── tailwind.config.ts ├── tsconfig.json └── vite.config.ts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., 4 | [oligamiq, bjorn3, whitequark] 5 | # patreon: # Replace with a single Patreon username 6 | # open_collective: # Replace with a single Open Collective username 7 | # ko_fi: # Replace with a single Ko-fi username 8 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | # liberapay: # Replace with a single Liberapay username 11 | # issuehunt: # Replace with a single IssueHunt username 12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | # polar: # Replace with a single Polar username 14 | # buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 15 | # thanks_dev: # Replace with a single thanks.dev username 16 | custom: # Replace with up to 4 custom sponsorship URLs e.g., 17 | [https://www.amazon.co.jp/hz/wishlist/ls/3KDVR70NQ0DRQ?ref_=list_d_wl_lfu_nav_2] 18 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | - name: Setup Pages 35 | uses: actions/configure-pages@v5 36 | - name: Setup Node 37 | uses: actions/setup-node@v3 38 | with: 39 | node-version: 20 40 | - name: install bun 41 | run: | 42 | curl -fsSL https://bun.sh/install | bash 43 | source /home/runner/.bashrc 44 | echo '~/.bun/bin' >> $GITHUB_PATH 45 | 46 | - name: Build 47 | run: | 48 | bun i 49 | cd page 50 | bun run build 51 | bunx mini-coi -sw dist/mini-coi.js 52 | sed -i '//a \ ' dist/index.html 53 | - name: Upload artifact 54 | uses: actions/upload-pages-artifact@v3 55 | with: 56 | # Upload entire repository 57 | path: './page/dist' 58 | - name: Deploy to GitHub Pages 59 | id: deployment 60 | uses: actions/deploy-pages@v4 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/dist 2 | **/node_modules 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "prettier.enable": false, 4 | "cSpell.words": [ 5 | "nanotar", 6 | "Preopen", 7 | "rustc", 8 | "rustlib", 9 | "uuidv4", 10 | "wasip" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rubrc 2 | Rubrc is a rustc that runs in the browser. 3 | 4 | It is a port of the rustc compiler to WebAssembly. It is a work in progress and is not yet ready for general use. 5 | 6 | This have some bottlenecks, like the lack of thread spawn is very slow. 7 | 8 | Currently, the targets for which executable files can be generated are `wasm32-wasip1` and `x86_64-unknown-linux-musl`. Other targets fail during the linking process. If you have any information, we would greatly appreciate it if you could share it in an issue. 9 | 10 | Demo: [Rubrc](https://oligamiq.github.io/rubrc/) 11 | 12 | # Special Thanks 13 | ## Projects 14 | - [rubri](https://github.com/LyonSyonII/rubri) by [LyonSyonII](https://github.com/LyonSyonII) - At first, I was using this project to run it on the browser. 15 | - [browser_wasi_shim](https://github.com/bjorn3/browser_wasi_shim) by [bjorn3](https://github.com/bjorn3) - This project is used to run the WASI on the browser. 16 | - [browser_wasi_shim-threads](https://github.com/bjorn3/browser_wasi_shim/tree/main/threads#README) by [oligamiq](https://github.com/oligamiq) - This project is used to run the WASI with threads on the browser. 17 | - [rust_wasm](https://github.com/oligamiq/rust_wasm) by [oligamiq](https://github.com/oligamiq) - This is a project that hosts files and sysroots compiled from Rustc, supporting from Tier 1 to Tier 2 with host in this project, and compiled to wasm. 18 | 19 | ## People 20 | - [bjorn3](https://github.com/bjorn3) - He created the foundation for compiling Rustc to WASI and managing linker relations. 21 | - [oligamiq](https://github.com/oligamiq) - He created Rustc compiled with LLVM Backend to WASI. 22 | - [whitequark](https://github.com/whitequark) - He created the LLVM to WASI. 23 | - [rust-lang](https://github.com/rust-lang) - They created the Rust language. 24 | 25 | ## Related Page 26 | - https://github.com/rust-lang/miri/issues/722#issuecomment-1960849880 27 | - https://discourse.llvm.org/t/rfc-building-llvm-for-webassembly/79073/27 28 | 29 | # Issues 30 | This has been created in a rather haphazard manner, but as the creator, I will be busy for a while, so it’s been left in this state for now. There are numerous bugs, such as commands throwing errors and subsequently becoming unusable, but feel free to open issues if necessary. Minor pull requests to improve usability are also welcome, so feel free to tweak it as you like. 31 | 32 | # Features 33 | ! This project require coop coep headers to work, so you need to run it on a server or use a browser extension to allow it. 34 | - [x] Run rustc on the browser 35 | - [x] Ctrl+V 36 | 37 | # Funding 38 | The projects that this project depends on, namely [browser_wasi_shim-threads](https://www.npmjs.com/package/@oligami/browser_wasi_shim-threads), [rust_wasm](https://github.com/oligamiq/rust_wasm), and [shared-object](https://www.npmjs.com/package/@oligami/shared-object), are all my projects. The [toolchain-for-building-rustc](https://github.com/oligamiq/toolchain-for-building-rustc) that rust_wasm depends on is also my project. I was the one who enabled the LLVM backend for rustc, and ultimately, I aim to make rustc executable in browsers that support wasm and allow cargo to run seamlessly on the web. 39 | 40 | If you like or want to use this series of projects, I would appreciate it if you could contribute financially via the sponsor button. 41 | 42 | Please note that coding has temporarily stopped due to being busy, and there may be missing or incorrect documentation. Although it works, it is currently in a state with various issues, so I do not recommend using it for production. 43 | 44 | # License 45 | This project is licensed under the MIT OR Apache-2.0 License. 46 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release notes 2 | ## v1.0.0 (2024-11-26) 3 | - Initial release 4 | 5 | ## v1.1.0 (2024-12-8) 6 | - load time optimization 7 | 18s -> 10s 8 | Finer-grained asynchronous fetch. 9 | File size reduction due to brotli compression. 10 | Change wasm compile method from compile to compileStreaming. 11 | - lazy loading monaco editor 12 | 13 | ## v1.1.1 (2024-12-18) 14 | - change default code. 15 | - moved the file with the code that runs on the worker to another directory. 16 | 17 | ## v1.1.2 (2024-12-27) 18 | - enable pasting 19 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.1/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "ignore": [] 11 | }, 12 | "formatter": { 13 | "enabled": true, 14 | "indentStyle": "space", 15 | "indentWidth": 2 16 | }, 17 | "organizeImports": { 18 | "enabled": true 19 | }, 20 | "linter": { 21 | "enabled": true, 22 | "rules": { 23 | "recommended": true 24 | } 25 | }, 26 | "javascript": { 27 | "formatter": { 28 | "quoteStyle": "double" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oligamiq/rubrc/268f73e0c07353624148567c5262e2a66e4fcca4/bun.lockb -------------------------------------------------------------------------------- /lib/.swcrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/swcrc", 3 | "jsc": { 4 | "parser": { 5 | "syntax": "typescript", 6 | "tsx": false, 7 | "dynamicImport": false, 8 | "decorators": false, 9 | "dts": true 10 | }, 11 | "transform": {}, 12 | "target": "esnext", 13 | "loose": false, 14 | "externalHelpers": false, 15 | "keepClassNames": true 16 | }, 17 | "minify": true 18 | } 19 | -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | # A pure javascript shim for WASI Preview 1 threads 2 | 3 | > [!WARNING] 4 | > The code in this directory is less production ready than the main browser_wasi_shim code. 5 | > This code requires `SharedArrayBuffer`, `waitAsync` and `Atomics` to be enabled in the browser, so it may not work in all browsers. 6 | > For example, Firefox failed to run the demo in this directory. 7 | > Chrome worked fine. 8 | > This library require `cross-origin isolation` to be enabled in the browser. 9 | 10 | This project implement threads on browser_wasi_shim 11 | 12 | # Features 13 | - [x] thread creation 14 | - [x] Filesystem wrapper accessible by multiple workers 15 | - [ ] thread pool 16 | 17 | # Building 18 | ```sh 19 | $ npm install 20 | $ npm run build 21 | ``` 22 | 23 | # Running the demo 24 | ```sh 25 | $ git submodule update --init 26 | $ cd examples && npm install && npm run dev 27 | ``` 28 | And visit http://localhost 29 | -------------------------------------------------------------------------------- /lib/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@oligami/rustc-browser-wasi_shim", 3 | "version": "1.1.2", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@oligami/rustc-browser-wasi_shim", 9 | "version": "1.1.2", 10 | "license": "MIT OR Apache-2.0", 11 | "dependencies": { 12 | "@bjorn3/browser_wasi_shim": "^0.3.0", 13 | "@oligami/browser_wasi_shim-threads": "^0.1.1", 14 | "@oligami/rustc-browser-wasi_shim": "file:", 15 | "brotli-dec-wasm": "^2.3.0" 16 | } 17 | }, 18 | "node_modules/@bjorn3/browser_wasi_shim": { 19 | "version": "0.3.0", 20 | "resolved": "https://registry.npmjs.org/@bjorn3/browser_wasi_shim/-/browser_wasi_shim-0.3.0.tgz", 21 | "integrity": "sha512-FlRBYttPRLcWORzBe6g8nmYTafBkOEFeOqMYM4tAHJzFsQy4+xJA94z85a9BCs8S+Uzfh9LrkpII7DXr2iUVFg==", 22 | "license": "MIT OR Apache-2.0" 23 | }, 24 | "node_modules/@oligami/browser_wasi_shim-threads": { 25 | "version": "0.1.1", 26 | "resolved": "https://registry.npmjs.org/@oligami/browser_wasi_shim-threads/-/browser_wasi_shim-threads-0.1.1.tgz", 27 | "integrity": "sha512-j47+NIQr10DojLfJvsMYYP0OQTbsuEa0sZaLLSPMyTgfUfE5eFPUJtf8SrUR6666nYIL0RoES720f9jWk+n7aQ==", 28 | "license": "MIT OR Apache-2.0", 29 | "dependencies": { 30 | "@bjorn3/browser_wasi_shim": "^0.3.0", 31 | "@oligami/browser_wasi_shim-threads": "file:" 32 | }, 33 | "peerDependencies": { 34 | "@bjorn3/browser_wasi_shim": "^0.3.0" 35 | } 36 | }, 37 | "node_modules/@oligami/browser_wasi_shim-threads/node_modules/@oligami/browser_wasi_shim-threads": { 38 | "resolved": "node_modules/@oligami/browser_wasi_shim-threads", 39 | "link": true 40 | }, 41 | "node_modules/@oligami/rustc-browser-wasi_shim": { 42 | "resolved": "", 43 | "link": true 44 | }, 45 | "node_modules/brotli-dec-wasm": { 46 | "version": "2.3.0", 47 | "resolved": "https://registry.npmjs.org/brotli-dec-wasm/-/brotli-dec-wasm-2.3.0.tgz", 48 | "integrity": "sha512-CNck+1A1ofvHk1oyqsKCuoIHLgD2FYy9KTVGHQlV1AKr/v/7N/Owh62nBKEcJxS3YOk+iwWhCi2rcaLhz9VN5g==", 49 | "license": "MIT OR Apache-2.0", 50 | "dependencies": { 51 | "prettier": "^3.2.5" 52 | }, 53 | "peerDependencies": { 54 | "typescript": "^5.0.0" 55 | } 56 | }, 57 | "node_modules/prettier": { 58 | "version": "3.4.2", 59 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", 60 | "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", 61 | "license": "MIT", 62 | "bin": { 63 | "prettier": "bin/prettier.cjs" 64 | }, 65 | "engines": { 66 | "node": ">=14" 67 | }, 68 | "funding": { 69 | "url": "https://github.com/prettier/prettier?sponsor=1" 70 | } 71 | }, 72 | "node_modules/typescript": { 73 | "version": "5.7.2", 74 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", 75 | "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", 76 | "license": "Apache-2.0", 77 | "peer": true, 78 | "bin": { 79 | "tsc": "bin/tsc", 80 | "tsserver": "bin/tsserver" 81 | }, 82 | "engines": { 83 | "node": ">=14.17" 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@oligami/rustc-browser-wasi_shim", 3 | "version": "1.1.2", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "prepare": "npm run build", 9 | "fmt": "biome format --write .", 10 | "lint": "biome lint src examples import-module-test", 11 | "check": "biome check && tsc --noEmit" 12 | }, 13 | "dependencies": { 14 | "@bjorn3/browser_wasi_shim": "^0.3.0", 15 | "@oligami/browser_wasi_shim-threads": "^0.1.1", 16 | "@oligami/rustc-browser-wasi_shim": "file:", 17 | "brotli-dec-wasm": "^2.3.0" 18 | }, 19 | "private": false, 20 | "publishConfig": { 21 | "access": "public" 22 | }, 23 | "author": "oligami (https://github.com/oligamiq)", 24 | "license": "MIT OR Apache-2.0", 25 | "description": "Rust compiler on web", 26 | "homepage": "https://github.com/oligamiq/rubrc", 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/oligamiq/rubrc.git" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/oligamiq/rubrc/issues" 33 | }, 34 | "main": "./dist/rustc-browser-wasi_shim.es.js", 35 | "types": "./dist/index.d.ts", 36 | "exports": { 37 | ".": { 38 | "import": { 39 | "types": "./dist/index.d.ts", 40 | "default": "./dist/rustc-browser-wasi_shim.es.js" 41 | }, 42 | "require": { 43 | "types": "./dist/index.d.ts", 44 | "default": "./dist/rustc-browser-wasi_shim.cjs.js" 45 | }, 46 | "node": { 47 | "types": "./dist/index.d.ts", 48 | "default": "./dist/rustc-browser-wasi_shim.cjs.js" 49 | }, 50 | "types": "./dist/index.d.ts", 51 | "default": "./dist/rustc-browser-wasi_shim.es.js" 52 | } 53 | }, 54 | "files": [ 55 | "dist", 56 | "src" 57 | ], 58 | "keywords": [] 59 | } 60 | -------------------------------------------------------------------------------- /lib/src/brotli_stream.ts: -------------------------------------------------------------------------------- 1 | import init, { 2 | BrotliDecStream, 3 | BrotliStreamResultCode, 4 | } from "brotli-dec-wasm/web"; // Import the default export 5 | // @ts-ignore 6 | import brotli_dec_wasm_bg from "brotli-dec-wasm/web/bg.wasm?wasm&url"; // Import the wasm file 7 | 8 | const promise = init(brotli_dec_wasm_bg); // Import is async in browsers due to wasm requirements! 9 | 10 | // 1MB output buffer 11 | const OUTPUT_SIZE = 1024 * 1024; 12 | 13 | export const get_brotli_decompress_stream = async (): Promise< 14 | TransformStream 15 | > => { 16 | await promise; 17 | 18 | const decompressStream = new BrotliDecStream(); 19 | const decompressionStream = new TransformStream({ 20 | transform(chunk, controller) { 21 | let resultCode: number; 22 | let inputOffset = 0; 23 | 24 | // Decompress this chunk, producing up to OUTPUT_SIZE output bytes at a time, until the 25 | // entire input has been decompressed. 26 | 27 | do { 28 | const input = chunk.slice(inputOffset); 29 | const result = decompressStream.decompress(input, OUTPUT_SIZE); 30 | controller.enqueue(result.buf); 31 | resultCode = result.code; 32 | inputOffset += result.input_offset; 33 | } while (resultCode === BrotliStreamResultCode.NeedsMoreOutput); 34 | if ( 35 | resultCode !== BrotliStreamResultCode.NeedsMoreInput && 36 | resultCode !== BrotliStreamResultCode.ResultSuccess 37 | ) { 38 | controller.error(`Brotli decompression failed with code ${resultCode}`); 39 | } 40 | }, 41 | flush(controller) { 42 | controller.terminate(); 43 | }, 44 | }); 45 | return decompressionStream; 46 | }; 47 | 48 | export const fetch_compressed_stream = async ( 49 | url: string | URL | globalThis.Request, 50 | ): Promise> => { 51 | const compressed_stream = await fetch(url); 52 | if (!compressed_stream.ok) { 53 | throw new Error("Failed to fetch wasm"); 54 | } 55 | if (!compressed_stream.body) { 56 | throw new Error("No body in response"); 57 | } 58 | 59 | return compressed_stream.body.pipeThrough( 60 | await get_brotli_decompress_stream(), 61 | ); 62 | }; 63 | -------------------------------------------------------------------------------- /lib/src/get_llvm_wasm.ts: -------------------------------------------------------------------------------- 1 | import { get_wasm } from "./get_wasm"; 2 | 3 | export const get_llvm_wasm = () => 4 | get_wasm("https://oligamiq.github.io/rust_wasm/v0.2.0/llvm_opt.wasm.br"); 5 | -------------------------------------------------------------------------------- /lib/src/get_rustc_wasm.ts: -------------------------------------------------------------------------------- 1 | import { get_wasm } from "./get_wasm"; 2 | 3 | export const get_rustc_wasm = () => 4 | get_wasm("https://oligamiq.github.io/rust_wasm/v0.2.0/rustc_opt.wasm.br"); 5 | -------------------------------------------------------------------------------- /lib/src/get_wasm.ts: -------------------------------------------------------------------------------- 1 | import { fetch_compressed_stream } from "./brotli_stream"; 2 | 3 | export const get_wasm = async ( 4 | url: string | URL | globalThis.Request, 5 | ): Promise => { 6 | const response = new Response(await fetch_compressed_stream(url), { 7 | headers: { "Content-Type": "application/wasm" }, 8 | }); 9 | 10 | const wasm = await WebAssembly.compileStreaming(response); 11 | 12 | return wasm; 13 | }; 14 | -------------------------------------------------------------------------------- /lib/src/index.ts: -------------------------------------------------------------------------------- 1 | import "./sysroot"; 2 | import "./get_rustc_wasm"; 3 | import "./get_llvm_wasm"; 4 | -------------------------------------------------------------------------------- /lib/src/parse_tar.ts: -------------------------------------------------------------------------------- 1 | import type { TarFileItem } from "nanotar"; 2 | 3 | // https://github.com/unjs/nanotar/blob/c1247bdec97163b487c8ca55003e291dfea755ab/src/parse.ts 4 | // MIT License 5 | 6 | // Copyright (c) Pooya Parsa 7 | 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | 26 | export async function parseTar( 27 | readable_stream: ReadableStream, 28 | callback: (file: TarFileItem) => void, 29 | ) { 30 | const reader = readable_stream.getReader(); 31 | 32 | let buffer = new Uint8Array(0); 33 | let done = false; 34 | 35 | const check_stream = async () => { 36 | const { done: _done, value } = await reader.read(); 37 | 38 | if (value) { 39 | const new_buffer = new Uint8Array(buffer.length + value.length); 40 | 41 | new_buffer.set(buffer); 42 | new_buffer.set(value, buffer.length); 43 | 44 | buffer = new_buffer; 45 | } 46 | 47 | done = _done; 48 | }; 49 | 50 | while (true) { 51 | while (buffer.length < 512 && !done) { 52 | await check_stream(); 53 | } 54 | 55 | // File name (offset: 0 - length: 100) 56 | const name = _readString(buffer, 0, 100); 57 | if (name.length === 0) { 58 | break; 59 | } 60 | 61 | // File mode (offset: 100 - length: 8) 62 | const mode = _readString(buffer, 100, 8); 63 | 64 | // File uid (offset: 108 - length: 8) 65 | const uid = Number.parseInt(_readString(buffer, 108, 8)); 66 | 67 | // File gid (offset: 116 - length: 8) 68 | const gid = Number.parseInt(_readString(buffer, 116, 8)); 69 | 70 | // File size (offset: 124 - length: 12) 71 | const size = _readNumber(buffer, 124, 12); 72 | 73 | // File mtime (offset: 136 - length: 12) 74 | const mtime = _readNumber(buffer, 136, 12); 75 | 76 | // File type (offset: 156 - length: 1) 77 | const _type = _readNumber(buffer, 156, 1); 78 | const type = _type === 0 ? "file" : _type === 5 ? "directory" : _type; // prettier-ignore 79 | 80 | // Ustar indicator (offset: 257 - length: 6) 81 | // Ignore 82 | 83 | // Ustar version (offset: 263 - length: 2) 84 | // Ignore 85 | 86 | // File owner user (offset: 265 - length: 32) 87 | const user = _readString(buffer, 265, 32); 88 | 89 | // File owner group (offset: 297 - length: 32) 90 | const group = _readString(buffer, 297, 32); 91 | 92 | // File data (offset: 512 - length: size) 93 | while (buffer.length < 512 + size) { 94 | await check_stream(); 95 | } 96 | 97 | const data = buffer.slice(512, 512 + size); 98 | 99 | const file = { 100 | name, 101 | type, 102 | size, 103 | data, 104 | get text() { 105 | return new TextDecoder().decode(this.data); 106 | }, 107 | attrs: { 108 | mode, 109 | uid, 110 | gid, 111 | mtime, 112 | user, 113 | group, 114 | }, 115 | }; 116 | 117 | callback(file); 118 | 119 | let adjusted_size = 512 + 512 * Math.trunc(size / 512); 120 | if (size % 512) { 121 | adjusted_size += 512; 122 | } 123 | 124 | while (buffer.length < adjusted_size && !done) { 125 | await check_stream(); 126 | } 127 | 128 | if (done && buffer.length < adjusted_size) { 129 | break; 130 | } 131 | 132 | buffer = buffer.slice(adjusted_size); 133 | } 134 | } 135 | 136 | function _readString(buffer: Uint8Array, offset: number, size: number) { 137 | const view = buffer.slice(offset, offset + size); 138 | const i = view.indexOf(0); 139 | const td = new TextDecoder(); 140 | return td.decode(view.slice(0, i)); 141 | } 142 | 143 | function _readNumber(buffer: Uint8Array, offset: number, size: number) { 144 | const view = buffer.slice(offset, offset + size); 145 | let str = ""; 146 | for (let i = 0; i < size; i++) { 147 | str += String.fromCodePoint(view[i]); 148 | } 149 | return Number.parseInt(str, 8); 150 | } 151 | -------------------------------------------------------------------------------- /lib/src/sysroot.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type Inode, 3 | PreopenDirectory, 4 | File, 5 | Directory, 6 | } from "@bjorn3/browser_wasi_shim"; 7 | import { WASIFarm } from "@oligami/browser_wasi_shim-threads"; 8 | 9 | import { fetch_compressed_stream } from "./brotli_stream"; 10 | import { parseTar } from "./parse_tar"; 11 | 12 | export const load_sysroot_part = async (triple: string): Promise => { 13 | const decompressed_stream = await fetch_compressed_stream( 14 | `https://oligamiq.github.io/rust_wasm/v0.2.0/${triple}.tar.br`, 15 | ); 16 | 17 | const dir = new Map(); 18 | console.group("Loading sysroot"); 19 | 20 | await parseTar(decompressed_stream, (file) => { 21 | if (!file.data) { 22 | throw new Error("File data not found"); 23 | } 24 | if (file.name.includes("/")) { 25 | const parts = file.name.split("/"); 26 | const created_dir = dir.get(parts[0]); 27 | if (created_dir instanceof Directory) { 28 | created_dir.contents.set(parts.slice(1).join("/"), new File(file.data)); 29 | } else { 30 | dir.set( 31 | parts[0], 32 | new Directory([[parts.slice(1).join("/"), new File(file.data)]]), 33 | ); 34 | } 35 | } else { 36 | dir.set(file.name, new File(file.data)); 37 | } 38 | 39 | console.log(file.name); 40 | }); 41 | console.groupEnd(); 42 | return new Directory(dir); 43 | }; 44 | 45 | const toMap = (arr: Array<[string, Inode]>) => { 46 | const map = new Map(); 47 | for (const [key, value] of arr) { 48 | map.set(key, value); 49 | } 50 | return map; 51 | }; 52 | 53 | let rustlib_dir: Directory | undefined; 54 | 55 | export const load_default_sysroot = async (): Promise => { 56 | const sysroot_part = await load_sysroot_part("wasm32-wasip1"); 57 | rustlib_dir = new Directory([ 58 | ["wasm32-wasip1", new Directory([["lib", sysroot_part]])], 59 | ]); 60 | const sysroot = new PreopenDirectory( 61 | "/sysroot", 62 | toMap([["lib", new Directory([["rustlib", rustlib_dir]])]]), 63 | ); 64 | loaded_triples.add("wasm32-wasip1"); 65 | return sysroot; 66 | }; 67 | 68 | const loaded_triples: Set = new Set(); 69 | 70 | export const load_additional_sysroot = async (triple: string) => { 71 | if (loaded_triples.has(triple)) { 72 | return; 73 | } 74 | const sysroot_part = await load_sysroot_part(triple); 75 | if (!rustlib_dir) { 76 | throw new Error("Default sysroot not loaded"); 77 | } 78 | rustlib_dir.contents.set(triple, new Directory([["lib", sysroot_part]])); 79 | loaded_triples.add(triple); 80 | }; 81 | 82 | export const get_default_sysroot_wasi_farm = async (): Promise => { 83 | const fds = [await load_default_sysroot()]; 84 | const farm = new WASIFarm(undefined, undefined, undefined, fds, { 85 | allocator_size: 1024 * 1024 * 1024, 86 | }); 87 | return farm; 88 | }; 89 | -------------------------------------------------------------------------------- /lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ESNext", "DOM"], 7 | "skipLibCheck": true, 8 | /* Bundler mode */ 9 | "incremental": true, 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "emitDeclarationOnly": true, 15 | "declaration": true, 16 | "declarationMap": true, 17 | "outDir": "./dist", 18 | "declarationDir": "./types", 19 | "esModuleInterop": true, 20 | /* Linting */ 21 | "strict": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noFallthroughCasesInSwitch": true 25 | }, 26 | "include": ["./src"] 27 | } 28 | -------------------------------------------------------------------------------- /lib/vite.config.ts: -------------------------------------------------------------------------------- 1 | // https://zenn.dev/seapolis/articles/3605c4befc8465 2 | 3 | import { resolve } from "node:path"; 4 | import { defineConfig } from "vite"; 5 | // import dts from "vite-plugin-dts"; 6 | import swc from "unplugin-swc"; 7 | 8 | export default defineConfig({ 9 | server: { 10 | headers: { 11 | "Cross-Origin-Embedder-Policy": "require-corp", 12 | "Cross-Origin-Opener-Policy": "same-origin", 13 | }, 14 | }, 15 | build: { 16 | lib: { 17 | entry: resolve(__dirname, "src/index.ts"), 18 | name: "wasi-shim-threads", 19 | formats: ["es", "umd", "cjs"], 20 | fileName: (format) => `rustc-browser-wasi-shim.${format}.js`, 21 | }, 22 | sourcemap: true, 23 | minify: true, 24 | copyPublicDir: false, 25 | }, 26 | // plugins: [dts({ rollupTypes: true })], 27 | // plugins: [swc.vite(), swc.rollup(), dts({ rollupTypes: true })], 28 | plugins: [swc.vite(), swc.rollup()], 29 | }); 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rubrc", 3 | "version": "1.1.2", 4 | "description": "rubrc root", 5 | "private": true, 6 | "directories": { 7 | "lib": "lib", 8 | "page": "page" 9 | }, 10 | "author": "oligami (https://github.com/oligamiq)", 11 | "license": "MIT OR Apache-2.0", 12 | "type": "module", 13 | "devDependencies": { 14 | "@biomejs/biome": "1.9.4", 15 | "@swc/cli": "^0.5.2", 16 | "@swc/core": "^1.10.2", 17 | "@types/tar-stream": "^3.1.3", 18 | "autoprefixer": "^10.4.20", 19 | "better-typescript-lib": "^2.10.0", 20 | "npm-watch": "^0.13.0", 21 | "postcss": "^8.4.49", 22 | "solid-devtools": "^0.31.6", 23 | "tailwindcss": "^3.4.17", 24 | "typescript": "^5.7.2", 25 | "unplugin-swc": "^1.5.1", 26 | "vite": "^6.0.6", 27 | "vite-plugin-dts": "^4.4.0", 28 | "vite-plugin-solid": "^2.11.0" 29 | }, 30 | "workspaces": [ 31 | "./lib", 32 | "./page" 33 | ], 34 | "dependencies": { 35 | "rubrc": "^1.1.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /page/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /page/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`. 4 | 5 | This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template. 6 | 7 | ```bash 8 | $ npm install # or pnpm install or yarn install 9 | ``` 10 | 11 | ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) 12 | 13 | ## Available Scripts 14 | 15 | In the project directory, you can run: 16 | 17 | ### `npm run dev` or `npm start` 18 | 19 | Runs the app in the development mode.
20 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 21 | 22 | The page will reload if you make edits.
23 | 24 | ### `npm run build` 25 | 26 | Builds the app for production to the `dist` folder.
27 | It correctly bundles Solid in production mode and optimizes the build for the best performance. 28 | 29 | The build is minified and the filenames include the hashes.
30 | Your app is ready to be deployed! 31 | 32 | ## Deployment 33 | 34 | You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.) 35 | -------------------------------------------------------------------------------- /page/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Rubrc 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /page/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rubrc", 3 | "version": "1.1.2", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "rubrc", 9 | "version": "1.1.2", 10 | "license": "MIT OR Apache-2.0", 11 | "dependencies": { 12 | "@bjorn3/browser_wasi_shim": "^0.3.0", 13 | "@oligami/browser_wasi_shim-threads": "^0.1.1", 14 | "@oligami/shared-object": "0.1.1", 15 | "@thisbeyond/solid-select": "^0.15.0", 16 | "@xterm/addon-fit": "^0.10.0", 17 | "@xterm/addon-search": "^0.15.0", 18 | "@xterm/xterm": "^5.5.0", 19 | "nanotar": "^0.1.1", 20 | "rubrc": "file:", 21 | "solid-js": "^1.9.3", 22 | "solid-monaco": "^0.3.0" 23 | } 24 | }, 25 | "node_modules/@bjorn3/browser_wasi_shim": { 26 | "version": "0.3.0", 27 | "resolved": "https://registry.npmjs.org/@bjorn3/browser_wasi_shim/-/browser_wasi_shim-0.3.0.tgz", 28 | "integrity": "sha512-FlRBYttPRLcWORzBe6g8nmYTafBkOEFeOqMYM4tAHJzFsQy4+xJA94z85a9BCs8S+Uzfh9LrkpII7DXr2iUVFg==", 29 | "license": "MIT OR Apache-2.0" 30 | }, 31 | "node_modules/@monaco-editor/loader": { 32 | "version": "1.4.0", 33 | "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz", 34 | "integrity": "sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==", 35 | "license": "MIT", 36 | "dependencies": { 37 | "state-local": "^1.0.6" 38 | }, 39 | "peerDependencies": { 40 | "monaco-editor": ">= 0.21.0 < 1" 41 | } 42 | }, 43 | "node_modules/@oligami/browser_wasi_shim-threads": { 44 | "version": "0.1.1", 45 | "resolved": "https://registry.npmjs.org/@oligami/browser_wasi_shim-threads/-/browser_wasi_shim-threads-0.1.1.tgz", 46 | "integrity": "sha512-j47+NIQr10DojLfJvsMYYP0OQTbsuEa0sZaLLSPMyTgfUfE5eFPUJtf8SrUR6666nYIL0RoES720f9jWk+n7aQ==", 47 | "license": "MIT OR Apache-2.0", 48 | "dependencies": { 49 | "@bjorn3/browser_wasi_shim": "^0.3.0", 50 | "@oligami/browser_wasi_shim-threads": "file:" 51 | }, 52 | "peerDependencies": { 53 | "@bjorn3/browser_wasi_shim": "^0.3.0" 54 | } 55 | }, 56 | "node_modules/@oligami/browser_wasi_shim-threads/node_modules/@oligami/browser_wasi_shim-threads": { 57 | "resolved": "node_modules/@oligami/browser_wasi_shim-threads", 58 | "link": true 59 | }, 60 | "node_modules/@oligami/shared-object": { 61 | "version": "0.1.1", 62 | "resolved": "https://registry.npmjs.org/@oligami/shared-object/-/shared-object-0.1.1.tgz", 63 | "integrity": "sha512-1N5k8IL+JjD0lPiLR+NXT7BFzvnlPaBioMry9RZOL+4YI54TpW4fmkgys9rlyVo4hZB0DL5t8pgzXxq+IIO7fg==", 64 | "license": "MIT", 65 | "dependencies": { 66 | "shared-object": "file:" 67 | } 68 | }, 69 | "node_modules/@thisbeyond/solid-select": { 70 | "version": "0.15.0", 71 | "resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.15.0.tgz", 72 | "integrity": "sha512-L3QnA5vm09JWNqLR4QqEIi7cgfs9/1sJbFjyEnPskjKpxEvb4G+Iy5cyd1qhZ2vAIZ9vaLd3udts3kefayPsMg==", 73 | "license": "MIT", 74 | "engines": { 75 | "node": ">=18.0.0", 76 | "pnpm": ">=9.0.0" 77 | }, 78 | "peerDependencies": { 79 | "solid-js": "^1.8" 80 | } 81 | }, 82 | "node_modules/@xterm/addon-fit": { 83 | "version": "0.10.0", 84 | "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz", 85 | "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==", 86 | "license": "MIT", 87 | "peerDependencies": { 88 | "@xterm/xterm": "^5.0.0" 89 | } 90 | }, 91 | "node_modules/@xterm/addon-search": { 92 | "version": "0.15.0", 93 | "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.15.0.tgz", 94 | "integrity": "sha512-ZBZKLQ+EuKE83CqCmSSz5y1tx+aNOCUaA7dm6emgOX+8J9H1FWXZyrKfzjwzV+V14TV3xToz1goIeRhXBS5qjg==", 95 | "license": "MIT", 96 | "peerDependencies": { 97 | "@xterm/xterm": "^5.0.0" 98 | } 99 | }, 100 | "node_modules/@xterm/xterm": { 101 | "version": "5.5.0", 102 | "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", 103 | "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", 104 | "license": "MIT" 105 | }, 106 | "node_modules/csstype": { 107 | "version": "3.1.3", 108 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", 109 | "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", 110 | "license": "MIT" 111 | }, 112 | "node_modules/monaco-editor": { 113 | "version": "0.48.0", 114 | "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.48.0.tgz", 115 | "integrity": "sha512-goSDElNqFfw7iDHMg8WDATkfcyeLTNpBHQpO8incK6p5qZt5G/1j41X0xdGzpIkGojGXM+QiRQyLjnfDVvrpwA==", 116 | "license": "MIT", 117 | "peer": true 118 | }, 119 | "node_modules/nanotar": { 120 | "version": "0.1.1", 121 | "resolved": "https://registry.npmjs.org/nanotar/-/nanotar-0.1.1.tgz", 122 | "integrity": "sha512-AiJsGsSF3O0havL1BydvI4+wR76sKT+okKRwWIaK96cZUnXqH0uNBOsHlbwZq3+m2BR1VKqHDVudl3gO4mYjpQ==", 123 | "license": "MIT" 124 | }, 125 | "node_modules/rubrc": { 126 | "resolved": "", 127 | "link": true 128 | }, 129 | "node_modules/seroval": { 130 | "version": "1.1.1", 131 | "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.1.1.tgz", 132 | "integrity": "sha512-rqEO6FZk8mv7Hyv4UCj3FD3b6Waqft605TLfsCe/BiaylRpyyMC0b+uA5TJKawX3KzMrdi3wsLbCaLplrQmBvQ==", 133 | "license": "MIT", 134 | "engines": { 135 | "node": ">=10" 136 | } 137 | }, 138 | "node_modules/seroval-plugins": { 139 | "version": "1.1.1", 140 | "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.1.1.tgz", 141 | "integrity": "sha512-qNSy1+nUj7hsCOon7AO4wdAIo9P0jrzAMp18XhiOzA6/uO5TKtP7ScozVJ8T293oRIvi5wyCHSM4TrJo/c/GJA==", 142 | "license": "MIT", 143 | "engines": { 144 | "node": ">=10" 145 | }, 146 | "peerDependencies": { 147 | "seroval": "^1.0" 148 | } 149 | }, 150 | "node_modules/shared-object": { 151 | "resolved": "node_modules/@oligami/shared-object", 152 | "link": true 153 | }, 154 | "node_modules/solid-js": { 155 | "version": "1.9.3", 156 | "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.3.tgz", 157 | "integrity": "sha512-5ba3taPoZGt9GY3YlsCB24kCg0Lv/rie/HTD4kG6h4daZZz7+yK02xn8Vx8dLYBc9i6Ps5JwAbEiqjmKaLB3Ag==", 158 | "license": "MIT", 159 | "dependencies": { 160 | "csstype": "^3.1.0", 161 | "seroval": "^1.1.0", 162 | "seroval-plugins": "^1.1.0" 163 | } 164 | }, 165 | "node_modules/solid-monaco": { 166 | "version": "0.3.0", 167 | "resolved": "https://registry.npmjs.org/solid-monaco/-/solid-monaco-0.3.0.tgz", 168 | "integrity": "sha512-MsJLrCWysv5ONdOjC4kShNoBXJWuwjP6JplJLQWG7pL00XSfvqBEKUF0wLVdaKfntf50Qz8NAd7Yx7dq67bjPA==", 169 | "license": "MIT", 170 | "dependencies": { 171 | "@monaco-editor/loader": "^1.4.0" 172 | }, 173 | "engines": { 174 | "node": ">=18", 175 | "pnpm": ">=8.6.0" 176 | }, 177 | "peerDependencies": { 178 | "monaco-editor": "^0.48.0", 179 | "solid-js": "^1.8.0" 180 | } 181 | }, 182 | "node_modules/state-local": { 183 | "version": "1.0.7", 184 | "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", 185 | "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", 186 | "license": "MIT" 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /page/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rubrc", 3 | "version": "1.1.2", 4 | "description": "rubrc example page", 5 | "private": true, 6 | "author": "oligami (https://github.com/oligamiq)", 7 | "license": "MIT OR Apache-2.0", 8 | "type": "module", 9 | "scripts": { 10 | "start": "vite", 11 | "dev": "vite", 12 | "build": "vite build", 13 | "serve": "vite preview", 14 | "fmt": "biome format --write ." 15 | }, 16 | "dependencies": { 17 | "@bjorn3/browser_wasi_shim": "^0.3.0", 18 | "@oligami/browser_wasi_shim-threads": "^0.1.1", 19 | "@oligami/shared-object": "0.1.1", 20 | "@thisbeyond/solid-select": "^0.15.0", 21 | "@xterm/addon-fit": "^0.10.0", 22 | "@xterm/addon-search": "^0.15.0", 23 | "@xterm/xterm": "^5.5.0", 24 | "nanotar": "^0.1.1", 25 | "rubrc": "file:", 26 | "solid-js": "^1.9.3", 27 | "solid-monaco": "^0.3.0", 28 | "uuid": "^11.0.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /page/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"], 3 | plugins: { 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /page/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { createSignal, lazy, Suspense, type Component } from "solid-js"; 2 | import { SetupMyTerminal } from "./xterm"; 3 | import type { WASIFarmRef } from "@oligami/browser_wasi_shim-threads"; 4 | import type { Ctx } from "./ctx"; 5 | import { default_value, rust_file } from "./config"; 6 | import { DownloadButton, RunButton } from "./btn"; 7 | import { triples } from "./sysroot"; 8 | 9 | const Select = lazy(async () => { 10 | const selector = import("@thisbeyond/solid-select"); 11 | const css_load = import("@thisbeyond/solid-select/style.css"); 12 | 13 | const [mod] = await Promise.all([selector, css_load]); 14 | 15 | return { default: mod.Select }; 16 | }); 17 | 18 | import { SharedObjectRef } from "@oligami/shared-object"; 19 | const MonacoEditor = lazy(() => 20 | import("solid-monaco").then((mod) => ({ default: mod.MonacoEditor })), 21 | ); 22 | 23 | const App = (props: { 24 | ctx: Ctx; 25 | callback: (wasi_ref: WASIFarmRef) => void; 26 | }) => { 27 | const handleMount = (monaco, editor) => { 28 | // Use monaco and editor instances here 29 | }; 30 | const handleEditorChange = (value) => { 31 | // Handle editor value change 32 | rust_file.data = new TextEncoder().encode(value); 33 | }; 34 | let load_additional_sysroot: (string) => void; 35 | 36 | const [triple, setTriple] = createSignal("wasm32-wasip1"); 37 | 38 | return ( 39 |
40 | 46 |

Loading editor...

47 |
48 | } 49 | > 50 | 57 | 58 | {/*

Hello tailwind!

*/} 59 |
60 | 61 |
62 |
63 |
64 | 65 |
66 |
67 |