From 30e24d36dbd336af94d403db329ea5782e0bd85a Mon Sep 17 00:00:00 2001 From: codingfrog27 Date: Mon, 22 Jun 2026 12:01:32 +0200 Subject: [PATCH 01/13] first iteration untested --- .prettierignore | 1 + CLAUDE.md | 218 + README.md | 12 +- docs/codebase-guide.md | 108 + docs/pi-integration.md | 140 + eslint.config.js | 2 + package-lock.json | 315 +- package.json | 12 +- src/hooks.server.ts | 9 +- src/lib/components/IosUnsupportedModal.svelte | 4 +- src/lib/components/Sidebar.svelte | 11 + src/lib/components/Stepper.svelte | 61 +- src/lib/components/ide/EditorPane.svelte | 139 + src/lib/components/ide/FileTreePanel.svelte | 83 + src/lib/components/ide/FrameworkPanel.svelte | 24 + src/lib/components/ide/TerminalTabs.svelte | 71 + src/lib/config/frameworks.ts | 99 + src/lib/ide/pod-fs.ts | 38 + src/lib/ide/session.svelte.ts | 208 + src/lib/utils/main.ts | 16 +- src/lib/utils/useLazyTracking.js | 20 - src/lib/utils/useLazyTracking.ts | 13 + src/routes/+layout.svelte | 34 +- src/routes/+page.ts | 2 +- src/routes/[tool]/+page.ts | 11 + src/routes/{ => agents}/[tool]/+page.svelte | 57 +- src/routes/ide/+page.svelte | 593 ++ src/routes/layout.css | 7 +- static/templates/express/manifest.txt | 5 + static/templates/express/package-lock.json | 827 ++ static/templates/express/package.json | 11 + static/templates/express/public/index.html | 50 + static/templates/express/server.js | 20 + static/templates/nextjs/app/globals.css | 31 + static/templates/nextjs/app/layout.jsx | 14 + static/templates/nextjs/app/page.jsx | 8 + static/templates/nextjs/manifest.txt | 7 + static/templates/nextjs/next.config.js | 4 + static/templates/nextjs/package-lock.json | 450 + static/templates/nextjs/package.json | 17 + static/templates/nuxt/app/app.vue | 6 + static/templates/nuxt/manifest.txt | 8 + static/templates/nuxt/nuxt.config.ts | 8 + static/templates/nuxt/package-lock.json | 8799 +++++++++++++++++ static/templates/nuxt/package.json | 22 + static/templates/nuxt/public/favicon.ico | Bin 0 -> 4286 bytes static/templates/nuxt/public/robots.txt | 2 + static/templates/nuxt/tsconfig.json | 18 + static/templates/vite-react/index.html | 13 + static/templates/vite-react/manifest.txt | 15 + static/templates/vite-react/package-lock.json | 932 ++ static/templates/vite-react/package.json | 23 + .../templates/vite-react/public/favicon.svg | 1 + static/templates/vite-react/public/icons.svg | 24 + static/templates/vite-react/src/App.jsx | 121 + static/templates/vite-react/src/app.css | 184 + .../templates/vite-react/src/assets/hero.png | Bin 0 -> 44919 bytes .../templates/vite-react/src/assets/react.svg | 1 + .../templates/vite-react/src/assets/vite.svg | 1 + static/templates/vite-react/src/index.css | 111 + static/templates/vite-react/src/main.jsx | 10 + static/templates/vite-react/start-vite.mjs | 13 + static/templates/vite-react/vite.config.js | 6 + static/templates/vite-svelte/.npmrc | 1 + static/templates/vite-svelte/README.md | 47 + static/templates/vite-svelte/index.html | 13 + static/templates/vite-svelte/jsconfig.json | 32 + static/templates/vite-svelte/manifest.txt | 18 + .../templates/vite-svelte/package-lock.json | 547 + static/templates/vite-svelte/package.json | 20 + .../templates/vite-svelte/public/favicon.svg | 1 + static/templates/vite-svelte/public/icons.svg | 24 + static/templates/vite-svelte/src/App.svelte | 89 + static/templates/vite-svelte/src/app.css | 296 + .../templates/vite-svelte/src/assets/hero.png | Bin 0 -> 44919 bytes .../vite-svelte/src/assets/svelte.svg | 1 + .../templates/vite-svelte/src/assets/vite.svg | 1 + .../vite-svelte/src/lib/Counter.svelte | 5 + static/templates/vite-svelte/src/main.js | 9 + static/templates/vite-svelte/svelte.config.js | 7 + static/templates/vite-svelte/vite.config.js | 22 + static/templates/vite-vue/index.html | 13 + static/templates/vite-vue/manifest.txt | 15 + static/templates/vite-vue/package-lock.json | 490 + static/templates/vite-vue/package.json | 22 + static/templates/vite-vue/public/favicon.svg | 1 + static/templates/vite-vue/public/icons.svg | 24 + static/templates/vite-vue/src/App.vue | 7 + static/templates/vite-vue/src/assets/hero.png | Bin 0 -> 44919 bytes static/templates/vite-vue/src/assets/vite.svg | 1 + static/templates/vite-vue/src/assets/vue.svg | 1 + .../vite-vue/src/components/HelloWorld.vue | 93 + static/templates/vite-vue/src/main.js | 5 + static/templates/vite-vue/src/style.css | 296 + static/templates/vite-vue/start-vite.mjs | 13 + static/templates/vite-vue/vite.config.js | 14 + static/templates/vite/index.html | 13 + static/templates/vite/manifest.txt | 14 + static/templates/vite/package-lock.json | 260 + static/templates/vite/package.json | 18 + static/templates/vite/public/favicon.svg | 1 + static/templates/vite/public/icons.svg | 24 + static/templates/vite/src/assets/hero.png | Bin 0 -> 44919 bytes .../templates/vite/src/assets/javascript.svg | 1 + static/templates/vite/src/assets/vite.svg | 1 + static/templates/vite/src/counter.js | 9 + static/templates/vite/src/main.js | 60 + static/templates/vite/src/style.css | 296 + static/templates/vite/start-vite.mjs | 11 + static/templates/vite/vite.config.js | 3 + 110 files changed, 16805 insertions(+), 104 deletions(-) create mode 100644 CLAUDE.md create mode 100644 docs/codebase-guide.md create mode 100644 docs/pi-integration.md create mode 100644 src/lib/components/ide/EditorPane.svelte create mode 100644 src/lib/components/ide/FileTreePanel.svelte create mode 100644 src/lib/components/ide/FrameworkPanel.svelte create mode 100644 src/lib/components/ide/TerminalTabs.svelte create mode 100644 src/lib/config/frameworks.ts create mode 100644 src/lib/ide/pod-fs.ts create mode 100644 src/lib/ide/session.svelte.ts delete mode 100644 src/lib/utils/useLazyTracking.js create mode 100644 src/lib/utils/useLazyTracking.ts create mode 100644 src/routes/[tool]/+page.ts rename src/routes/{ => agents}/[tool]/+page.svelte (87%) create mode 100644 src/routes/ide/+page.svelte create mode 100644 static/templates/express/manifest.txt create mode 100644 static/templates/express/package-lock.json create mode 100644 static/templates/express/package.json create mode 100644 static/templates/express/public/index.html create mode 100644 static/templates/express/server.js create mode 100644 static/templates/nextjs/app/globals.css create mode 100644 static/templates/nextjs/app/layout.jsx create mode 100644 static/templates/nextjs/app/page.jsx create mode 100644 static/templates/nextjs/manifest.txt create mode 100644 static/templates/nextjs/next.config.js create mode 100644 static/templates/nextjs/package-lock.json create mode 100644 static/templates/nextjs/package.json create mode 100644 static/templates/nuxt/app/app.vue create mode 100644 static/templates/nuxt/manifest.txt create mode 100644 static/templates/nuxt/nuxt.config.ts create mode 100644 static/templates/nuxt/package-lock.json create mode 100644 static/templates/nuxt/package.json create mode 100644 static/templates/nuxt/public/favicon.ico create mode 100644 static/templates/nuxt/public/robots.txt create mode 100644 static/templates/nuxt/tsconfig.json create mode 100644 static/templates/vite-react/index.html create mode 100644 static/templates/vite-react/manifest.txt create mode 100644 static/templates/vite-react/package-lock.json create mode 100644 static/templates/vite-react/package.json create mode 100644 static/templates/vite-react/public/favicon.svg create mode 100644 static/templates/vite-react/public/icons.svg create mode 100644 static/templates/vite-react/src/App.jsx create mode 100644 static/templates/vite-react/src/app.css create mode 100644 static/templates/vite-react/src/assets/hero.png create mode 100644 static/templates/vite-react/src/assets/react.svg create mode 100644 static/templates/vite-react/src/assets/vite.svg create mode 100644 static/templates/vite-react/src/index.css create mode 100644 static/templates/vite-react/src/main.jsx create mode 100644 static/templates/vite-react/start-vite.mjs create mode 100644 static/templates/vite-react/vite.config.js create mode 100644 static/templates/vite-svelte/.npmrc create mode 100644 static/templates/vite-svelte/README.md create mode 100644 static/templates/vite-svelte/index.html create mode 100644 static/templates/vite-svelte/jsconfig.json create mode 100644 static/templates/vite-svelte/manifest.txt create mode 100644 static/templates/vite-svelte/package-lock.json create mode 100644 static/templates/vite-svelte/package.json create mode 100644 static/templates/vite-svelte/public/favicon.svg create mode 100644 static/templates/vite-svelte/public/icons.svg create mode 100644 static/templates/vite-svelte/src/App.svelte create mode 100644 static/templates/vite-svelte/src/app.css create mode 100644 static/templates/vite-svelte/src/assets/hero.png create mode 100644 static/templates/vite-svelte/src/assets/svelte.svg create mode 100644 static/templates/vite-svelte/src/assets/vite.svg create mode 100644 static/templates/vite-svelte/src/lib/Counter.svelte create mode 100644 static/templates/vite-svelte/src/main.js create mode 100644 static/templates/vite-svelte/svelte.config.js create mode 100644 static/templates/vite-svelte/vite.config.js create mode 100644 static/templates/vite-vue/index.html create mode 100644 static/templates/vite-vue/manifest.txt create mode 100644 static/templates/vite-vue/package-lock.json create mode 100644 static/templates/vite-vue/package.json create mode 100644 static/templates/vite-vue/public/favicon.svg create mode 100644 static/templates/vite-vue/public/icons.svg create mode 100644 static/templates/vite-vue/src/App.vue create mode 100644 static/templates/vite-vue/src/assets/hero.png create mode 100644 static/templates/vite-vue/src/assets/vite.svg create mode 100644 static/templates/vite-vue/src/assets/vue.svg create mode 100644 static/templates/vite-vue/src/components/HelloWorld.vue create mode 100644 static/templates/vite-vue/src/main.js create mode 100644 static/templates/vite-vue/src/style.css create mode 100644 static/templates/vite-vue/start-vite.mjs create mode 100644 static/templates/vite-vue/vite.config.js create mode 100644 static/templates/vite/index.html create mode 100644 static/templates/vite/manifest.txt create mode 100644 static/templates/vite/package-lock.json create mode 100644 static/templates/vite/package.json create mode 100644 static/templates/vite/public/favicon.svg create mode 100644 static/templates/vite/public/icons.svg create mode 100644 static/templates/vite/src/assets/hero.png create mode 100644 static/templates/vite/src/assets/javascript.svg create mode 100644 static/templates/vite/src/assets/vite.svg create mode 100644 static/templates/vite/src/counter.js create mode 100644 static/templates/vite/src/main.js create mode 100644 static/templates/vite/src/style.css create mode 100644 static/templates/vite/start-vite.mjs create mode 100644 static/templates/vite/vite.config.js diff --git a/.prettierignore b/.prettierignore index 7d74fe2..2293a05 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,4 @@ bun.lockb # Miscellaneous /static/ +.claude/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e9d6bd6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,218 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What is BrowserCode? + +BrowserCode is a web-based IDE that runs entirely in the browser using WebAssembly. It has two experiences: a **playground IDE** at `/ide` (editor + file tree + terminals + live preview, with per-framework starter templates) and **AI coding CLIs** (Claude Code, Gemini CLI) at `/agents/[tool]`. Both are built on BrowserPod, which provides a sandboxed Node.js environment with persistent filesystem storage, POSIX CLI tools (bash, git, npm), and instant app previews via WebAssembly-based portal URLs. + +See `docs/codebase-guide.md` for a full structural tour. + +## Quick Start + +```bash +npm install # Install dependencies +npm run dev # Start development server at http://localhost:5173 +npm run check # Run type checking and Svelte validation +npm run lint # Check code style with ESLint and Prettier +npm run format # Auto-format code with Prettier +npm run build # Build for production +npm run preview # Preview production build locally +``` + +## Tech Stack + +- **Framework**: SvelteKit 2.50.2 (Svelte 5.51.0) +- **Build**: Vite 7.3.1 +- **Styling**: Tailwind CSS 4.1.18 +- **Runtime**: BrowserPod 2.8.0 (WebAssembly Node.js environment) +- **Type System**: TypeScript 5.9.3 (strict mode) +- **Icons**: Iconify + mingcute icon set +- **Code Quality**: ESLint 9.39.2, Prettier 3.8.1 + +## High-Level Architecture + +### User-Facing Flow + +``` +User visits browsercode.io + ↓ +/ redirects to /ide (playground); agents live at /agents/claude, /agents/gemini + ↓ ++layout.svelte renders: Sidebar + Main Content Area + ↓ +/ide: ide/+page.svelte → IdeSession.boot() hydrates a framework template into a pod +/agents/[tool]: agents/[tool]/+page.svelte → bootCLI() boots the tool's disk image + ↓ +Terminal renders stdio output; Portal iframes live previews +``` + +### Routing & Tool Selection + +The app is a client-rendered SPA (`adapter-static`, `ssr = false`, `200.html` fallback), so all redirects below run client-side. + +- **Root**: `/` redirects to `/ide` (`src/routes/+page.ts`) +- **Playground IDE**: `/ide`, framework selected via `?framework=` (registry in `lib/config/frameworks.ts`; templates in `static/templates/`) +- **Agent Pages**: `/agents/[tool]` (`routes/agents/[tool]/+page.svelte`) +- **Legacy URLs**: `/claude`, `/gemini`, … redirect to `/agents/` (`routes/[tool]/+page.ts`) +- **Supported Tools** (lib/config/tools.ts): + - Claude Code: enabled, uses claude_20260506 disk image + - Gemini CLI: enabled, uses gemini_20260430_2 disk image + - Codex, OpenCode: disabled (coming soon) + +Tool/IDE switching uses full page loads (`window.location.href`) on purpose — that is the teardown mechanism for the running pod. + +### Component Hierarchy + +**+layout.svelte** (root layout) + +- Renders Sidebar (left) + main content (right) +- Manages tool validation and active tool state +- Sets up OG meta tags for social sharing + +**agents/[tool]/+page.svelte** (agent application) + +- Terminal component (simple div#console container) +- Portal component (preview iframe + controls) +- Desktop: split layout with drag-to-resize (portalFraction state) +- Mobile: tab navigation between Terminal and Preview views +- Handles portal updates from BrowserPod callbacks + +**ide/+page.svelte** (playground application) + +- Composes the IDE: icon rail, resizable side panel (FileTreePanel / FrameworkPanel), EditorPane (CodeMirror), TerminalTabs (Output + Bash), Portal preview +- Pod lifecycle and file/portal state live in `lib/ide/session.svelte.ts` (IdeSession); pod file I/O helpers in `lib/ide/pod-fs.ts` +- `lib/components/ide/EditorPane.svelte` is the only file importing `@codemirror/*` — it is designed to be replaced by VS Code Web later; session/pod-fs/frameworks survive that migration + +**Portal.svelte** + +- iFrame displaying preview URLs +- Port selector (if multiple services running) +- Menu for copy URL, QR code, open in new tab +- QR code generation using qrcode library + +**Sidebar.svelte** + +- Fixed-width nav (desktop only, hidden mobile) +- Tool buttons, GitHub/Discord links, Help button +- Uses Iconify icons with hover tooltips + +**Stepper.svelte** + +- Onboarding tutorial modal +- State managed via stepperState store + +### Data Flow: BrowserPod & Portals + +``` +lib/utils/main.ts: bootCLI() + 1. Detects iOS (unsupported platform) + 2. Initializes BrowserPod with VITE_API_KEY env var + 3. Creates terminal linked to #console div + 4. Sets up portal listener (pod.onPortal callback) + 5. Creates /home/user/project directory + 6. Copies optional project file (CLAUDE.md or GEMINI.md) + 7. Runs CLI: node /path/to/cli.js in /home/user/project + +BrowserPod callbacks: + - Portal events: { port, url } → portalUpdate state → Portal component + - Open events: Optional custom handler (e.g., OAuth URL rewriting for Claude) +``` + +**Portal Updates**: When a service starts on a port, BrowserPod emits `{ port, url, active: true }`. This is collected in the `portals` array (state in [tool]/+page.svelte), and the selected portal renders in the iFrame. + +### Storage & Persistence + +- Each tool has a `storageKey` (e.g., `claude_20260506`) that maps to a BrowserPod disk image URL +- BrowserPod caches the WebAssembly filesystem locally (IndexedDB) +- Changes persist across sessions within the same storage key +- Disk images are versioned; updating the version syncs a new baseline + +### Configuration + +**lib/config/tools.ts** defines: + +- Tool metadata (id, label, icon, disabled flag) +- CLI configurations: disk image URL, storage key, command, args, optional project file, optional open callback + +To add a new tool: + +1. Add to `toolItems` array +2. Add config to `cliConfigs` object +3. Ensure disk image is available at the WSS URL + +## Development Workflow + +### Adding a Component + +1. Create a `.svelte` file in `src/lib/components/` +2. Use ` {#snippet navButton(item: { id: string; icon: string | null; label: string; disabled: boolean })} @@ -67,6 +74,10 @@