You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
React execution and captured console output in an iframe
Vim mode and light and dark themes
Module-system selection
JSX preservation and let?
Detailed warning flags
Resizable panes
Diagnostics, hovers, and type information where supported
It does not provide the development playground’s current-checkout, master, and pull-request compiler workflows or its intermediate compiler representations.
The resizable panes are implemented directly in Playground.res, without a split-pane dependency. The implementation tracks dragging, installs global mouse and touch listeners, updates percentage widths or heights, and changes orientation at a responsive breakpoint.
Feature comparison
Capability
Development playground
Production /try
Live compilation
Yes
Yes
Format/reset/share
Yes
Yes
Released compiler versions
No
Yes
Current checkout/master/PR builds
Yes
No
Intermediate compiler output
Yes
No
CodeMirror 6
No
Yes
Diagnostics/hovers/type information
No
Yes
Execute JavaScript
No
Yes
React rendering
No
Yes
Captured console output
No
Yes
Vim mode and themes
No
Yes
Resizable panes
No
Yes
Browser/E2E coverage
No
Yes
Findings
The compiler API varies by version and source
A unified playground needs to load released, development, and pull-request compiler bundles through one abstraction. Capabilities vary across compiler versions, so UI logic should not infer support from version strings or scatter compatibility checks throughout components.
A normalized compiler adapter should expose explicit capabilities:
Capabilities with multiple possible values should expose those values directly instead of adding more booleans. For example, the adapter can return the available intermediate-output kinds.
Compiler source identity should use normal variants with payloads where required:
This covers published releases, master, pull requests, and the locally built compiler without representing invalid states such as a pull-request source without a PR number. Stable IDs can be derived from the source, while labels and asset locations remain configurable by the host.
Legacy Reason syntax is out of scope. The unified playground only needs to support ReScript syntax.
Keep the existing /try URL format
A new URL format is not necessary. The existing production /try format should remain canonical for newly generated links.
The unified implementation should:
Continue decoding existing ReScript /try links.
Preserve the current production format for ordinary links.
Add parameters only for state the format cannot currently represent, such as a pull-request compiler source or selected intermediate representation.
Add fixtures ensuring existing ReScript /try links continue to decode.
Test that newly generated links use the same canonical format.
Development-playground URLs do not require backward compatibility or migration. Legacy Reason links are also out of scope.
Xote needs an explicit disposal API for React hosting
The production route is currently React-based. A reusable Xote playground could be mounted as an island from the React route during migration, but Xote 7.1’s public View.mount and View.mountById APIs return unit and expose no public unmount or disposal handle.
Removing the host element directly would bypass Xote’s internal disposal and can leak subscriptions and event handlers.
This lifecycle functionality belongs in Xote itself rather than in a playground-specific workaround.
Program execution should remain isolated
Xote should render the playground UI, but user programs should continue to run in an iframe. The evaluator should be retained and hardened around:
Sandbox permissions
Origin validation
Message validation
Runtime/compiler version selection
React runtime loading
Cleanup between runs
The UI framework and the runtime used by compiled user programs are separate concerns. An Xote playground can still execute and render React examples inside the output iframe.
Proposed architecture
1. Framework-neutral PlaygroundEngine
Extract the non-visual behavior into a reusable core:
Compiler catalog and loader
Compiler-version compatibility adapters
Normalized compilation results
Playground state and settings
/try URL decoding and encoding
Program evaluator protocol
Capability detection
This layer should not depend on React or Xote.
2. Xote PlaygroundView
Build one Xote UI on top of the engine, using CodeMirror 6 rather than the development playground’s custom textarea editor.
CodeMirror 6 remains the current major CodeMirror architecture. Its packages are versioned independently, so the implementation should use current compatible releases rather than treating “6” as a single package version.
The view should merge both playground feature sets:
Editing, diagnostics, hover and type information
Live compilation, formatting, and sharing
JavaScript, output, problems, and settings
Program execution, console output, and React rendering
Released, local, master, and pull-request compilers
Intermediate compiler representations
Themes and Vim mode
Resizable panes
Intermediate compiler representations should be exposed through an output selector:
JavaScript
Parse tree
Typed tree
Lambda
Lam
JavaScript remains the default. Unsupported representations should be disabled or omitted based on the selected compiler’s declared capabilities.
3. Reusable Xote SplitPane
Replace the current React-specific resizing logic with a reusable Xote SplitPane component.
It should support:
Horizontal and vertical layouts
Responsive orientation changes
Pointer events for mouse, touch, and pen input
Minimum pane sizes
Keyboard resizing
An accessible separator
Correct listener and subscription cleanup when unmounted
The current behavior is small enough that an additional split-pane dependency does not appear necessary.
4. Thin host integrations
Provide two small entry points around the same view:
A standalone host for the compiler repository’s development playground
A React wrapper for /try that mounts the Xote view and disposes it from useEffect cleanup
This allows the implementation to be introduced incrementally without requiring the documentation site to stop using React.
The available compiler sources should be configurable by the host:
Production /try shows released versions by default.
The development host shows the local build, master, pull-request builds, and optionally releases.
Non-release sources may remain accessible on /try through explicit URLs without appearing in its normal compiler selector.
5. Canonical package ownership and naming
The shared implementation should live in this compiler repository and be published as @rescript/playground for consumption by the website.
Reasons:
Development bundles, pull-request previews, and intermediate representations are closely coupled to compiler internals.
Compiler API compatibility can evolve alongside the artifact producer.
The website can consume a stable package instead of maintaining a second compiler integration.
The existing private artifact producer under packages/playground should remain, but be renamed to playground-compiler to distinguish it from the published UI package.
The public package name should not include “Xote,” since Xote is an implementation detail rather than part of its external contract.
Proposed migration
Define contracts and parity tests
Inventory both feature sets as testable behavior.
Define normalized compiler, capability, state, URL, and evaluator interfaces.
Summary
ReScript currently has two separate browser playground implementations:
packages/dev-playground.This RFC proposes consolidating them into one reusable Xote-based implementation while preserving the combined feature set.
Note
packages/playgroundin this repository is not a third playground UI. It produces the browser compiler and.cmijartifacts consumed by playgrounds.Current implementations
Xote development playground
The development playground is closely coupled to compiler development and supports:
master, and pull requestslet?optionsIt currently does not provide:
Production
/tryplaygroundThe production playground is a large React implementation centered around:
Playground.resCompilerManagerHook.resCodeMirror.resandRescriptCompilerApi.resbindingsTryRoute.reshost routeIt supports:
let?It does not provide the development playground’s current-checkout,
master, and pull-request compiler workflows or its intermediate compiler representations.The resizable panes are implemented directly in
Playground.res, without a split-pane dependency. The implementation tracks dragging, installs global mouse and touch listeners, updates percentage widths or heights, and changes orientation at a responsive breakpoint.Feature comparison
/trymaster/PR buildsFindings
The compiler API varies by version and source
A unified playground needs to load released, development, and pull-request compiler bundles through one abstraction. Capabilities vary across compiler versions, so UI logic should not infer support from version strings or scatter compatibility checks throughout components.
A normalized compiler adapter should expose explicit capabilities:
Capabilities with multiple possible values should expose those values directly instead of adding more booleans. For example, the adapter can return the available intermediate-output kinds.
Compiler source identity should use normal variants with payloads where required:
This covers published releases,
master, pull requests, and the locally built compiler without representing invalid states such as a pull-request source without a PR number. Stable IDs can be derived from the source, while labels and asset locations remain configurable by the host.Legacy Reason syntax is out of scope. The unified playground only needs to support ReScript syntax.
Keep the existing
/tryURL formatA new URL format is not necessary. The existing production
/tryformat should remain canonical for newly generated links.The unified implementation should:
/trylinks./trylinks continue to decode.Development-playground URLs do not require backward compatibility or migration. Legacy Reason links are also out of scope.
Xote needs an explicit disposal API for React hosting
The production route is currently React-based. A reusable Xote playground could be mounted as an island from the React route during migration, but Xote 7.1’s public
View.mountandView.mountByIdAPIs returnunitand expose no public unmount or disposal handle.Removing the host element directly would bypass Xote’s internal disposal and can leak subscriptions and event handlers.
Xote should expose something equivalent to:
or a root handle with an explicit
unmountmethod.This lifecycle functionality belongs in Xote itself rather than in a playground-specific workaround.
Program execution should remain isolated
Xote should render the playground UI, but user programs should continue to run in an iframe. The evaluator should be retained and hardened around:
The UI framework and the runtime used by compiled user programs are separate concerns. An Xote playground can still execute and render React examples inside the output iframe.
Proposed architecture
1. Framework-neutral
PlaygroundEngineExtract the non-visual behavior into a reusable core:
/tryURL decoding and encodingThis layer should not depend on React or Xote.
2. Xote
PlaygroundViewBuild one Xote UI on top of the engine, using CodeMirror 6 rather than the development playground’s custom textarea editor.
CodeMirror 6 remains the current major CodeMirror architecture. Its packages are versioned independently, so the implementation should use current compatible releases rather than treating “6” as a single package version.
The view should merge both playground feature sets:
master, and pull-request compilersIntermediate compiler representations should be exposed through an output selector:
JavaScript remains the default. Unsupported representations should be disabled or omitted based on the selected compiler’s declared capabilities.
3. Reusable Xote
SplitPaneReplace the current React-specific resizing logic with a reusable Xote
SplitPanecomponent.It should support:
The current behavior is small enough that an additional split-pane dependency does not appear necessary.
4. Thin host integrations
Provide two small entry points around the same view:
/trythat mounts the Xote view and disposes it fromuseEffectcleanupThis allows the implementation to be introduced incrementally without requiring the documentation site to stop using React.
The available compiler sources should be configurable by the host:
/tryshows released versions by default.master, pull-request builds, and optionally releases./trythrough explicit URLs without appearing in its normal compiler selector.5. Canonical package ownership and naming
The shared implementation should live in this compiler repository and be published as
@rescript/playgroundfor consumption by the website.Reasons:
The existing private artifact producer under
packages/playgroundshould remain, but be renamed toplayground-compilerto distinguish it from the published UI package.The public package name should not include “Xote,” since Xote is an implementation detail rather than part of its external contract.
Proposed migration
Define contracts and parity tests
/tryURLs as fixtures.Extract the shared engine
Introduce CodeMirror 6 in the Xote view
Reach production feature parity
Add Xote unmount and disposal support
Replace hosts incrementally
/tryafter parity and compatibility tests pass.Remove the duplicate implementation
Testing requirements
The consolidated implementation should include:
/trylinksSplitPaneinteraction and accessibility testsmaster, and pull-request compiler sources