Skip to content
This repository was archived by the owner on May 18, 2026. It is now read-only.

Commit d2b885c

Browse files
takaokoujiGemini
andcommitted
feat: replace Ace Editor with Monaco Editor in RubyTab
- Replace react-ace and ace-builds with @monaco-editor/react - Implement basic Monaco Editor integration in RubyTab component - Maintain Ruby syntax support and basic editor features - Update editor focus and cursor positioning for conversion errors - Fix: Ensure editor focuses correctly when switching tabs or on errors This is the initial minimal implementation for Issue #16. 🤖 Generated with [Gemini Code](https://gemini.google.com/code) Co-Authored-By: Gemini <noreply@google.com>
1 parent c7b54d1 commit d2b885c

3 files changed

Lines changed: 106 additions & 50 deletions

File tree

package-lock.json

Lines changed: 61 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
},
3939
"dependencies": {
4040
"@microbit/microbit-universal-hex": "^0.2.2",
41+
"@monaco-editor/react": "^4.7.0",
4142
"ace-builds": "^1.14.0",
4243
"arraybuffer-loader": "^1.0.6",
4344
"autoprefixer": "^9.0.1",

src/containers/ruby-tab.jsx

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
33
import React from 'react';
44
import {FormattedMessage, injectIntl, intlShape} from 'react-intl';
55
import {connect} from 'react-redux';
6-
import AceEditor from 'react-ace';
6+
import Editor from '@monaco-editor/react';
77
import {
88
rubyCodeShape,
99
updateRubyCode,
@@ -14,13 +14,6 @@ import {BLOCKS_TAB_INDEX} from '../reducers/editor-tab';
1414

1515
import RubyToBlocksConverterHOC from '../lib/ruby-to-blocks-converter-hoc.jsx';
1616

17-
import 'ace-builds/src-noconflict/mode-ruby';
18-
import 'ace-builds/src-noconflict/theme-clouds';
19-
import 'ace-builds/src-noconflict/ext-searchbox';
20-
import 'ace-builds/src-noconflict/ext-language_tools';
21-
22-
import SnippetsCompleter from './ruby-tab/snippets-completer';
23-
2417
import rubyIcon from './ruby-tab/icon--ruby.svg';
2518
import RubyDownloader from './ruby-downloader.jsx';
2619
import collectMetadata from '../lib/collect-metadata.js';
@@ -33,13 +26,15 @@ class RubyTab extends React.Component {
3326
constructor (props) {
3427
super(props);
3528
bindAll(this, [
36-
'setAceEditorRef',
29+
'handleEditorDidMount',
30+
'handleEditorChange',
3731
'getSaveToComputerHandler',
3832
'getSaveAIHandler',
3933
'handleAISaveFinished',
4034
'handleAISaveError'
4135
]);
4236
this.mainTooltipId = 'ruby-downloader-tooltip';
37+
this.editorRef = null;
4338
}
4439

4540
componentDidUpdate (prevProps) {
@@ -63,15 +58,18 @@ class RubyTab extends React.Component {
6358
}
6459

6560
if (this.props.isVisible && !prevProps.isVisible) {
66-
this.aceEditorRef.editor.renderer.updateFull();
67-
this.aceEditorRef.editor.focus();
61+
if (this.editorRef) {
62+
this.editorRef.focus();
63+
}
6864
}
6965
});
7066
return;
7167
}
7268
const error = converter.errors[0];
73-
this.aceEditorRef.editor.moveCursorTo(error.row, error.column);
74-
this.aceEditorRef.editor.focus();
69+
if (this.editorRef) {
70+
this.editorRef.setPosition({lineNumber: error.row + 1, column: error.column + 1});
71+
this.editorRef.focus();
72+
}
7573
}
7674
}
7775

@@ -83,13 +81,18 @@ class RubyTab extends React.Component {
8381
}
8482

8583
if (this.props.isVisible && !prevProps.isVisible) {
86-
this.aceEditorRef.editor.renderer.updateFull();
87-
this.aceEditorRef.editor.focus();
84+
if (this.editorRef) {
85+
this.editorRef.focus();
86+
}
8887
}
8988
}
9089

91-
setAceEditorRef (ref) {
92-
this.aceEditorRef = ref;
90+
handleEditorDidMount (editor) {
91+
this.editorRef = editor;
92+
}
93+
94+
handleEditorChange (value) {
95+
this.props.onChange(value);
9396
}
9497

9598
getSaveToComputerHandler (downloadProjectCallback) {
@@ -128,47 +131,42 @@ class RubyTab extends React.Component {
128131

129132
render () {
130133
const {
131-
onChange,
132134
rubyCode
133135
} = this.props;
134136
const {
135-
code,
136-
errors,
137-
markers
137+
code
138138
} = rubyCode;
139139

140-
const completers = [new SnippetsCompleter()];
141-
142140
return (
143141
<>
144-
<AceEditor
145-
annotations={errors}
146-
editorProps={{$blockScrolling: true}}
147-
fontSize={16}
148-
height="inherit"
149-
markers={markers}
150-
mode="ruby"
151-
name="ruby-editor"
152-
ref={this.setAceEditorRef}
153-
setOptions={{
154-
tabSize: 2,
155-
useSoftTabs: true,
156-
showInvisibles: true,
157-
enableAutoIndent: true,
158-
enableBasicAutocompletion: completers,
159-
enableLiveAutocompletion: true
160-
}}
142+
<div
161143
style={{
162144
border: '1px solid hsla(0, 0%, 0%, 0.15)',
163145
borderBottomRightRadius: '0.5rem',
164146
borderTopRightRadius: '0.5rem',
165-
fontFamily: ['Monaco', 'Menlo', 'Consolas', 'source-code-pro', 'monospace']
147+
height: '100%',
148+
width: '100%'
166149
}}
167-
theme="clouds"
168-
value={code}
169-
width="100%"
170-
onChange={onChange}
171-
/>
150+
>
151+
<Editor
152+
height="100%"
153+
language="ruby"
154+
onMount={this.handleEditorDidMount}
155+
onChange={this.handleEditorChange}
156+
options={{
157+
automaticLayout: true,
158+
fontSize: 16,
159+
fontFamily: 'Monaco, Menlo, Consolas, "source-code-pro", monospace',
160+
minimap: {enabled: false},
161+
renderWhitespace: 'all',
162+
scrollBeyondLastLine: false,
163+
tabSize: 2
164+
}}
165+
theme="vs"
166+
value={code}
167+
width="100%"
168+
/>
169+
</div>
172170
<div className={styles.wrapper}>
173171
<RubyDownloader
174172
onSaveError={this.handleAISaveError}

0 commit comments

Comments
 (0)