Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1e18d65
Remove OVERRIDE pragma and apply button, make config panel source of …
EugeneChoi4 Sep 9, 2025
88ccbb4
Remove config override gating
EugeneChoi4 Sep 9, 2025
09e8ef7
Initial palyground changes
EugeneChoi4 Sep 10, 2025
6faa9f1
Add styling to panels and chevron opener
EugeneChoi4 Sep 10, 2025
0788958
Add consistent styling
EugeneChoi4 Sep 11, 2025
c8fb918
Fix formatting
EugeneChoi4 Sep 11, 2025
46ffa19
Merge branch 'main' into compiler-playground-design
EugeneChoi4 Sep 11, 2025
5e5a84d
Update test
EugeneChoi4 Sep 11, 2025
a7bb236
update test
EugeneChoi4 Sep 11, 2025
16561a4
Initial changes
EugeneChoi4 Sep 12, 2025
10d938c
Merge branch 'main' into playground-applied-configs
EugeneChoi4 Sep 12, 2025
ef87362
update style
EugeneChoi4 Sep 12, 2025
0e0fcb0
Finalize config panel
EugeneChoi4 Sep 12, 2025
c0769ce
Update default config
EugeneChoi4 Sep 12, 2025
a128cac
Test changes
EugeneChoi4 Sep 15, 2025
84f2e54
Change versioning for experimental
EugeneChoi4 Sep 15, 2025
2004c12
Merge branch 'main' into playground-bug-fixes
EugeneChoi4 Sep 15, 2025
f527d55
add debouncing, improve show/hide
EugeneChoi4 Sep 16, 2025
d84a587
Remove loading indicator
EugeneChoi4 Sep 17, 2025
b201a1d
remove old index
EugeneChoi4 Sep 17, 2025
bdde8ec
Try new store loading
EugeneChoi4 Sep 17, 2025
87339bb
Remove unecessary text
EugeneChoi4 Sep 17, 2025
8263b5e
Merge branch 'main' into playground-bug-fixes
EugeneChoi4 Sep 17, 2025
201301e
update tests
EugeneChoi4 Sep 17, 2025
8a7d036
update yarn
EugeneChoi4 Sep 17, 2025
59f54a2
Revert experimental react for now
EugeneChoi4 Sep 18, 2025
d158da1
remove cleanup effect
EugeneChoi4 Sep 18, 2025
030099e
typo fix
EugeneChoi4 Sep 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 46 additions & 19 deletions compiler/apps/playground/components/Editor/ConfigEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
import {PluginOptions} from 'babel-plugin-react-compiler';
import type {editor} from 'monaco-editor';
import * as monaco from 'monaco-editor';
import React, {useState} from 'react';
import React, {useState, useRef, useEffect} from 'react';
import {Resizable} from 're-resizable';
import {useStore, useStoreDispatch} from '../StoreContext';
import {monacoOptions} from './monacoOptions';
Expand All @@ -28,10 +28,24 @@ export default function ConfigEditor({
}): React.ReactElement {
const [isExpanded, setIsExpanded] = useState(false);

return isExpanded ? (
<ExpandedEditor onToggle={setIsExpanded} appliedOptions={appliedOptions} />
) : (
<CollapsedEditor onToggle={setIsExpanded} />
return (
<>
<div
style={{
display: isExpanded ? 'block' : 'none',
}}>
<ExpandedEditor
onToggle={setIsExpanded}
appliedOptions={appliedOptions}
/>
</div>
<div
style={{
display: !isExpanded ? 'block' : 'none',
}}>
<CollapsedEditor onToggle={setIsExpanded} />
</div>
</>
);
}

Expand All @@ -44,16 +58,34 @@ function ExpandedEditor({
}): React.ReactElement {
const store = useStore();
const dispatchStore = useStoreDispatch();
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);

// Cleanup timeout on unmount
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need this effect for cleanup?

In the unlikely scenario that you finished typing and closed the config editor within 500ms, you'd still watch the last input change to dispatch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my understanding, when would it be appropriate to have the cleanup effect? If the ExpandedEditor component had some state that was being manipulated by the timer, would it then be necessary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If for example this used a setInterval or otherwise initialized a resource that could cause memory leaks or other side effects by staying around then you want to clean that up. In this case, the timer reference is going to expire at the end of the timeout anyway, so you'd just want to decide if its important if that function is canceled after an unmount or not.

useEffect(() => {
return () => {
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
}
};
}, []);

const handleChange: (value: string | undefined) => void = value => {
const handleChange: (value: string | undefined) => void = (
value: string | undefined,
) => {
if (value === undefined) return;

dispatchStore({
type: 'updateConfig',
payload: {
config: value,
},
});
if (debounceTimerRef.current) {
clearTimeout(debounceTimerRef.current);
}

debounceTimerRef.current = setTimeout(() => {
dispatchStore({
type: 'updateConfig',
payload: {
config: value,
},
});
}, 500); // 500ms debounce delay
Copy link
Member

@josephsavona josephsavona Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use useDeferredValue() instead of manually debouncing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useDeferredValue was already being used previously to update the output: https://github.com/facebook/react/blob/main/compiler/apps/playground/components/Editor/EditorImpl.tsx#L329.

It seemed like these updates were still happening too quickly while typing in the config editor so I tried adding a manual debouncing mechanism with a longer timeout window.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josephsavona we're adding debouncing to reduce thrash in the output panel from JS parse errors

};

const handleMount: (
Expand All @@ -77,12 +109,6 @@ function ExpandedEditor({
allowSyntheticDefaultImports: true,
jsx: monaco.languages.typescript.JsxEmit.React,
});

const uri = monaco.Uri.parse(`file:///config.ts`);
const model = monaco.editor.getModel(uri);
if (model) {
model.updateOptions({tabSize: 2});
}
};

const formattedAppliedOptions = appliedOptions
Expand Down Expand Up @@ -126,6 +152,7 @@ function ExpandedEditor({
value={store.config}
onMount={handleMount}
onChange={handleChange}
loading={''}
options={{
...monacoOptions,
lineNumbers: 'off',
Expand All @@ -139,7 +166,6 @@ function ExpandedEditor({
/>
</div>
</div>

<div className="flex-1 flex flex-col m-2">
<div className="pb-2">
<h2 className="inline-block text-blue-50 py-1.5 px-1.5 xs:px-3 sm:px-4 text-sm">
Expand All @@ -151,6 +177,7 @@ function ExpandedEditor({
path={'applied-config.js'}
language={'javascript'}
value={formattedAppliedOptions}
loading={''}
options={{
...monacoOptions,
lineNumbers: 'off',
Expand Down
40 changes: 10 additions & 30 deletions compiler/apps/playground/components/Editor/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
import invariant from 'invariant';
import type {editor} from 'monaco-editor';
import * as monaco from 'monaco-editor';
import {Resizable} from 're-resizable';
import {useEffect, useState} from 'react';
import {renderReactCompilerMarkers} from '../../lib/reactCompilerMonacoDiagnostics';
import {useStore, useStoreDispatch} from '../StoreContext';
Expand Down Expand Up @@ -46,11 +45,6 @@ export default function Input({errors, language}: Props): JSX.Element {
details: errors,
source: store.source,
});
/**
* N.B. that `tabSize` is a model property, not an editor property.
* So, the tab size has to be set per model.
*/
model.updateOptions({tabSize: 2});
}, [monaco, errors, store.source]);

useEffect(() => {
Expand Down Expand Up @@ -152,38 +146,24 @@ export default function Input({errors, language}: Props): JSX.Element {
onMount={handleMount}
onChange={handleChange}
options={monacoOptions}
loading={''}
/>
);

const tabs = new Map([['Input', editorContent]]);
const [activeTab, setActiveTab] = useState('Input');

const tabbedContent = (
<div className="flex flex-col h-full">
<TabbedWindow
tabs={tabs}
activeTab={activeTab}
onTabChange={setActiveTab}
/>
</div>
);

return (
<div className="relative flex flex-col flex-none border-r border-gray-200">
{store.showInternals ? (
<Resizable
minWidth={550}
enable={{right: true}}
/**
* Restrict MonacoEditor's height, since the config autoLayout:true
* will grow the editor to fit within parent element
*/
className="!h-[calc(100vh_-_3.5rem)]">
{tabbedContent}
</Resizable>
) : (
<div className="!h-[calc(100vh_-_3.5rem)]">{tabbedContent}</div>
)}
<div className="!h-[calc(100vh_-_3.5rem)]">
<div className="flex flex-col h-full">
<TabbedWindow
tabs={tabs}
activeTab={activeTab}
onTabChange={setActiveTab}
/>
</div>
</div>
</div>
);
}
2 changes: 2 additions & 0 deletions compiler/apps/playground/components/Editor/Output.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ function TextTabContent({
<DiffEditor
original={diff}
modified={output}
loading={''}
options={{
...monacoOptions,
readOnly: true,
Expand All @@ -338,6 +339,7 @@ function TextTabContent({
<MonacoEditor
language={language ?? 'javascript'}
value={output}
loading={''}
options={{
...monacoOptions,
readOnly: true,
Expand Down
2 changes: 2 additions & 0 deletions compiler/apps/playground/components/Editor/monacoOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ export const monacoOptions: Partial<EditorProps['options']> = {
automaticLayout: true,
wordWrap: 'on',
wrappingIndent: 'same',

tabSize: 2,
};
2 changes: 1 addition & 1 deletion compiler/apps/playground/lib/stores/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function initStoreFromUrlOrLocalStorage(): Store {
// Make sure all properties are populated
return {
source: raw.source,
config: 'config' in raw ? raw.config : defaultConfig,
config: 'config' in raw && raw['`config'] ? raw.config : defaultConfig,
showInternals: 'showInternals' in raw ? raw.showInternals : false,
};
}
2 changes: 1 addition & 1 deletion compiler/apps/playground/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
import "./.next/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
1 change: 1 addition & 0 deletions compiler/apps/playground/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const path = require('path');
const nextConfig = {
experimental: {
reactCompiler: true,
viewTransition: true,
},
reactStrictMode: true,
webpack: (config, options) => {
Expand Down
8 changes: 4 additions & 4 deletions compiler/apps/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@
"invariant": "^2.2.4",
"lz-string": "^1.5.0",
"monaco-editor": "^0.52.0",
"next": "15.5.2",
"next": "15.6.0-canary.7",
"notistack": "^3.0.0-alpha.7",
"prettier": "^3.3.3",
"pretty-format": "^29.3.1",
"re-resizable": "^6.9.16",
"react": "19.1.1",
"react-dom": "19.1.1"
"react": "^0.0.0-experimental-67a44bcd-20250915",
"react-dom": "^0.0.0-experimental-67a44bcd-20250915"
},
"devDependencies": {
"@types/node": "18.11.9",
"@types/react": "19.1.12",
"@types/react": "19.1.13",
"@types/react-dom": "19.1.9",
"autoprefixer": "^10.4.13",
"clsx": "^1.2.1",
Expand Down
5 changes: 4 additions & 1 deletion compiler/apps/playground/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"dom.iterable",
"esnext"
],
"types": [
"react/experimental"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
Expand All @@ -16,7 +19,7 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
Expand Down
Loading
Loading