Fix Monaco editor initialization regression#57
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting
| useEffect(() => { | ||
| if (editorRef.current && typeof ((window as any).require) !== 'undefined') { | ||
| // Configure Monaco Environment to load workers from CDN. This is crucial for syntax highlighting. | ||
| if (!(window as any).MonacoEnvironment) { | ||
| (window as any).MonacoEnvironment = { | ||
| getWorkerUrl: function (_moduleId: any, label: string) { | ||
| const CDN_PATH = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs'; | ||
| if (label === 'json') return `${CDN_PATH}/language/json/json.worker.js`; | ||
| if (label === 'css' || label === 'scss' || label === 'less') return `${CDN_PATH}/language/css/css.worker.js`; | ||
| if (label === 'html' || label === 'handlebars' || label === 'razor') return `${CDN_PATH}/language/html/html.worker.js`; | ||
| if (label === 'typescript' || label === 'javascript') return `${CDN_PATH}/language/typescript/ts.worker.js`; | ||
| return `${CDN_PATH}/editor/editor.worker.js`; | ||
| }, | ||
| }; | ||
| let isCancelled = false; | ||
|
|
||
| const initializeEditor = async () => { | ||
| if (!editorRef.current) { | ||
| return; | ||
| } | ||
|
|
||
| (window as any).require.config({ paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.44.0/min/vs' }}); | ||
| (window as any).require(['vs/editor/editor.main'], () => { | ||
| if (editorRef.current) { | ||
| // Ensure any previous instance is disposed | ||
| if (monacoInstanceRef.current) { | ||
| disposeEditorShortcuts(); | ||
| monacoInstanceRef.current.dispose(); | ||
| } | ||
| try { | ||
| const monacoApi = await ensureMonaco(); | ||
| if (!monacoApi || isCancelled || !editorRef.current) { | ||
| return; | ||
| } | ||
|
|
||
| const editorInstance = monaco.editor.create(editorRef.current, { | ||
| value: content, | ||
| language: language || 'plaintext', | ||
| theme: theme === 'dark' ? 'vs-dark' : 'vs', | ||
| automaticLayout: true, | ||
| fontSize: computedFontSize, | ||
| fontFamily: computedFontFamily, | ||
| minimap: { | ||
| enabled: true, | ||
| }, | ||
| wordWrap: 'on', | ||
| folding: true, | ||
| showFoldingControls: 'always', | ||
| bracketPairColorization: { | ||
| enabled: true, | ||
| }, | ||
| }); | ||
| monacoApiRef.current = monacoApi; | ||
|
|
||
| editorInstance.onDidChangeModelContent(() => { | ||
| const currentValue = editorInstance.getValue(); | ||
| if (currentValue !== contentRef.current) { | ||
| onChange(currentValue); | ||
| } | ||
| }); | ||
| if (monacoInstanceRef.current) { | ||
| disposeEditorShortcuts(); | ||
| monacoInstanceRef.current.dispose(); | ||
| } | ||
|
|
||
| editorInstance.onDidScrollChange((e: any) => { | ||
| if (e.scrollTopChanged) { | ||
| onScroll?.({ | ||
| scrollTop: e.scrollTop, | ||
| scrollHeight: e.scrollHeight, | ||
| clientHeight: editorInstance.getLayoutInfo().height | ||
| }); | ||
| } | ||
| }); | ||
| const editorInstance = monacoApi.editor.create(editorRef.current, { | ||
| value: content, | ||
| language: language || 'plaintext', | ||
| theme: theme === 'dark' ? 'vs-dark' : 'vs', | ||
| automaticLayout: true, |
There was a problem hiding this comment.
Initialize editor with latest props when loader resolves
The async loader now waits for ensureMonaco() before creating the editor, but the initialization closure captures content, language, and theme from the first render and these props are not part of the effect’s dependency list. If the component mounts with placeholder values (e.g., empty string) and the real content/language/theme arrive before the Monaco loader finishes downloading, the editor will be created with the stale initial props and the subsequent effects ([content], [language], [theme]) never rerun because they already executed earlier when monacoInstanceRef.current was still null. This leaves the editor showing outdated content or incorrect language/theme until those props change again. Use the ref values (contentRef.current) or include the props in the effect dependencies so the editor initializes with the most recent data.
Useful? React with 👍 / 👎.
Summary
Testing
https://chatgpt.com/codex/tasks/task_e_68e125d25e448332805b34b0b4f8756f