feat(playground): Multi-language Coding Playground with AST instrumentation & live visualizations#31
Merged
mnaimfaizy merged 10 commits intomainfrom Apr 8, 2026
Merged
Conversation
- Add playground feature directory structure under src/features/playground/
- Create core TypeScript types (PlaygroundLanguage, VisualizationLens,
ExecutionState, StateSnapshot, SandboxMessage, ConsoleEntry, etc.)
- Add default code templates for JavaScript, TypeScript, and Python
- Restructure App.tsx routing so /playground renders without Header,
Sidebar, Footer, or Breadcrumbs (standalone full-screen layout)
- Add Playground link with Rocket icon to Header (desktop + mobile),
opens in new tab via target=_blank
- Create space-themed dark layout shell with:
- Design tokens (deep space palette, electric blue accents)
- Scoped CSS under [data-theme=playground] with scrollbar, focus,
and selection overrides
- Animated starfield canvas with parallax layers, respects
prefers-reduced-motion
- PlaygroundLayout with translucent glassmorphism panes
- BackToSiteLink with close-tab-if-opener pattern
- Add PlaygroundApp with three-pane placeholder layout and toolbar
- Add PlaygroundEntry with ErrorBoundary + SEO meta tags
- Update vite.config.ts manualChunks for vendor-monaco, vendor-xyflow
- Build verified: playground chunk at ~5.5 KB, CSS scoped separately
Step 1.1 — Monaco Editor Integration: - Install @monaco-editor/react@4.7.0 + monaco-editor@0.53.0 (0 audit vulns) - Create useMonaco hook with custom space dark theme, Ctrl+Enter/Ctrl+S - Create MonacoEditor wrapper with loading skeleton and aria-label - Create LanguagePicker (JS/TS/Python) with radio group accessibility - Create EditorTabs with filename display and reset button Step 1.2 — JavaScript/TypeScript Sandboxed Execution: - Create SandboxFrame with sandbox='allow-scripts', nonce-based CSP - Block network APIs (fetch/XHR/WebSocket/EventSource) inside iframe - Create useSandbox hook with execute/terminate, 50KB input validation - Create ConsoleOutput with color-coded entries, auto-scroll, role=log - Create ExecutionController with Run/Stop/Reset and state indicators - 10s execution timeout with proper error handling Step 1.3 — Python Execution via Pyodide: - Create pyodide-loader service (CDN singleton, retry-safe) - Create usePyodide hook with lazy load, stdout/stderr capture, timeout - Create PythonRunner loading/error state component Wiring: - Create usePlaygroundState for language/code/execution state management - Wire all components into PlaygroundApp three-pane layout - Language switching preserves per-language code edits - Global keyboard shortcuts (Escape=stop, Ctrl+L=clear console)
…pture Step 2.1 — JavaScript/TypeScript Instrumentation: - Create instrumentCode.ts: acorn AST parsing + astring code generation Injects __tracker__ calls at statements, function entry/exit, variable captures - Create JsInstrumenter.ts: higher-level wrapper, snapshot reporting via postMessage - Create StateSnapshot.ts: deep clone, diff utility, formatValue helpers - Update SandboxFrame.tsx: handle 'timeline' message type, return snapshots - Update useSandbox.ts: expose snapshots/clearSnapshots from execution results Step 2.2 — Python Instrumentation: - Create PythonInstrumenter.ts: sys.settrace() wrapper, base64-encoded user code Captures line numbers, local variables, call stack per frame - Update usePyodide.ts: add runPythonInstrumented() with trace function wrapping Step 2.3 — Timeline Player & Playback Controls: - Create useTimeline.ts: step navigation, auto-play with speed control (0.5-4x) Respects prefers-reduced-motion, interval-based auto-advance - Create TimelinePlayer.tsx: timeline slider, playback buttons (First/Prev/Play/Next/Last) Speed dropdown, variables panel with diff highlighting, call stack panel Keyboard shortcuts (Left/Right=step, Space=play/pause, Home/End=first/last) - Wire into PlaygroundApp.tsx: instrumented mode toggle, timeline in center pane Line highlighting in Monaco editor via decorations API - Add CSS for pg-highlight-line and pg-highlight-glyph decorations - Create instrumentation/index.ts barrel export
Add interactive visualization system with lens-based architecture:
New components:
- LensSelector: Toolbar radio group with auto-suggest based on snapshot data
- VisualizationCanvas: Lazy-loaded container rendering the active lens
- EventLoopLens: SVG-based Call Stack, Microtask Queue, Task Queue visualization
- HeapStackLens: React Flow graph with stack frames and heap object nodes
- DataStructureLens: Auto-detects arrays, linked lists, trees from variables
- StreamLens: SVG Node.js stream pipeline with backpressure indicators
- AnimatedNode: Shared React Flow custom node with animation states
Dependencies:
- Add @xyflow/react@12.10.2 for graph-based visualizations
- Import @xyflow/react CSS in index.css
Bug fixes:
- Fix stale closure in handleRun: execute/run now return results directly
- Fix race condition: sandbox iframe reads __tracker__ directly in result
message instead of relying on separate timeline postMessage
- Fix __tracker__ scoping: use window.__tracker__ so it's accessible from
sandbox iframe's setTimeout callback after new Function() executes
Modified:
- PlaygroundApp: Wire lens selector and visualization canvas into layout
- SandboxFrame: Include snapshots in result message, remove timeline handler
- useSandbox/usePyodide: Return {error, snapshots} from execute/run
- JsInstrumenter: Remove appended timeline postMessage code
- instrumentCode: Use window.__tracker__ for global accessibility
Step 4.1 — Three-Pane Resizable Layout: - PaneResizer: draggable divider with pointer events, keyboard arrow keys, double-click reset, ARIA separator role, grip dots on hover - usePaneLayout hook: manages two divider fractions with localStorage persistence, min 15% pane width enforcement - PlaygroundApp: flex layout with explicit width percentages replacing static grid-cols-3 Step 4.2 — Toolbar & Settings: - PlaygroundToolbar: extracted toolbar with BackToSiteLink, LanguagePicker, Trace toggle, LensSelector, Examples dropdown with language filtering, Settings gear icon, ExecutionController - exampleSnippets: 7 examples (Event Loop, BST, Promise Chain, Bubble Sort, Closures, Python Collections, Linked List) with language/lens metadata - SettingsPanel: font size slider (10-22px), word wrap toggle, execution timeout slider (1-30s), animations toggle, localStorage persistence - MonacoEditor: added fontSize and wordWrap prop overrides Step 4.3 — Space Background Refinement: - Shooting stars spawning every ~15s with gradient tails - 30fps cap via FRAME_INTERVAL_MS - Tab visibility pause (document.hidden) - DPI fix: ctx.setTransform() instead of cumulative ctx.scale() - will-change: transform for GPU acceleration Step 4.4 — Keyboard Shortcuts & Accessibility: - Ctrl+Enter: run code - Escape: close settings or stop execution - Ctrl+L: clear console - Arrow keys: timeline stepping (when not in editor) - Space: play/pause timeline (when not in editor)
…tion Security: - Add sanitize.ts with HTML escaping, entry limiting, and binary detection - Harden SandboxFrame: block network APIs (fetch, XMLHttpRequest, WebSocket, importScripts, sendBeacon, window.open) with Object.defineProperty - Override window.parent/top/opener to prevent sandbox escape - Add rate limiting (1 exec/sec) and input validation to useSandbox/usePyodide - Inject Python security preamble blocking urllib, requests, subprocess, etc. - Escape console output HTML in ConsoleOutput to prevent XSS Performance: - Disable heavy Monaco features (codeLens, colorDecorators, inlayHints, etc.) - Wrap all 4 visualization lenses and AnimatedNode in React.memo - Cap DataStructureLens at 200 nodes with edge filtering - Adaptive star count in SpaceBackground based on navigator.hardwareConcurrency Bug fixes: - Remove duplicate keydown handler from TimelinePlayer that blocked Space in Monaco editor (PlaygroundApp already handles Arrow/Space/Home/End shortcuts) - Add z-index to toolbar wrapper so Examples dropdown renders above panels
Unit tests: - JsInstrumenter (11): instrumentation, async, error handling, source maps - PythonInstrumenter (14): trace wrapper, frame filtering, snapshot parsing - sanitize (~25): escapeHtml, sanitizeOutput, circular refs, prototype pollution - codeTemplates (5): template completeness and validity Component tests: - MonacoEditor (9): rendering, language switching, onChange, loading - SandboxFrame (6): iframe CSP, ref methods, cleanup - ConsoleOutput (11): entry types, clear, ARIA live region, XSS escaping - TimelinePlayer (16): step nav, play/pause, speed, variables, a11y - SpaceBackground (7): canvas animation, reduced motion, cleanup - PlaygroundApp (6): smoke test with mocked subsystems Integration tests (19): - JS/Python instrumentation pipelines end-to-end - Sanitization + console output pipeline (XSS, entry limits) - StateSnapshot utilities (safeDeepClone, diffSnapshots) - Example snippets coverage (languages, uniqueness, valid lenses) Documentation: - Create docs/Playground-Developer-Guide.md (lenses, languages, theme, snippets, security, performance, testing) - Update README.md with Playground architecture and dependencies - Update copilot-instructions.md with Playground module guidelines - Mark Phase 6 complete in Playground-Implementation-Plan.md All 211 tests passing (59 new + 152 existing).
…App functionality
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🎮 Coding Playground — New Feature
A fully in-browser, multi-language coding playground with AST-based instrumentation, timeline debugging, and pluggable data-structure visualization lenses. No server required — JavaScript, TypeScript, and Python all execute client-side.
✨ Highlights
__snapshot()calls around every statement; Python usessys.settracewrapper.playground-dark)📸 Architecture
🔧 Implementation Phases
d8f118d58496ed2a5f41ad710d6a14fe0b532e4df69ce9d60🧪 Test Coverage
59 new tests (211 total passing) across three tiers:
Unit Tests (55)
JsInstrumenter(11) — instrumentation, async code, error handling, source mapsPythonInstrumenter(14) — trace wrapper, frame filtering, snapshot parsingsanitize(~25) — escapeHtml, sanitizeOutput, circular refs, prototype pollution, entry limitscodeTemplates(5) — template completeness and validityComponent Tests (55)
MonacoEditor(9) — rendering, language switching, onChange, loadingSandboxFrame(6) — iframe CSP, ref methods, cleanupConsoleOutput(11) — entry types, clear, ARIA live region, XSS escapingTimelinePlayer(16) — step nav, play/pause, speed, variables, a11ySpaceBackground(7) — canvas animation, reduced motion, cleanupPlaygroundApp(6) — smoke tests with mocked subsystemsIntegration Tests (19)
🔒 Security
sandbox="allow-scripts", CSPdefault-src 'none'; script-src 'unsafe-inline'fetch,XMLHttpRequest,WebSocket,EventSource,importScripts,eval,Function__proto__,constructor,prototype)safeDeepClonecaps at 10 levels,sanitizeOutputat 5📁 New Files
64 files changed —
+10,444 / -180Key additions under
src/features/playground/:PlaygroundApp.tsx— Root orchestratorcomponents/editor/— MonacoEditor, EditorTabs, LanguagePickercomponents/execution/— SandboxFrame, ConsoleOutput, ExecutionController, PythonRunnercomponents/instrumentation/— TimelinePlayercomponents/layout/— PlaygroundLayout, PlaygroundToolbar, PaneResizer, SettingsPanelcomponents/theme/— SpaceBackground, tokens, playground-theme.csscomponents/visualizations/— VisualizationCanvas, LensSelector, 4 lenses, AnimatedNodehooks/— useMonaco, usePaneLayout, usePlaygroundState, usePyodide, useSandbox, useTimelineinstrumentation/— JsInstrumenter, PythonInstrumenter, StateSnapshotservices/— instrumentCode, pyodide-loaderutils/— codeTemplates, exampleSnippets, sanitizeDocumentation:
docs/Playground-Developer-Guide.md— How to add lenses, languages, snippets, theme changesdocs/Playground-Implementation-Plan.md— Full 7-phase plan (all complete)📖 Developer Guide
See
docs/Playground-Developer-Guide.mdfor:Dependencies Added
acornastring@xyflow/reactAll 211 tests passing. Build succeeds. No lint errors.