graph-notebook icon indicating copy to clipboard operation
graph-notebook copied to clipboard

Embed the Monaco editor in the cell

Open AndreaNassisi opened this issue 4 months ago • 1 comments

Community Note

  • Please use a 👍 reaction to provide a +1/vote. This helps the community and maintainers prioritize this request.
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment.

Graph Notebook Version (and Graph Database and Version used if applicable)

Is your feature request related to a problem? Please describe. Magics and graph languages keywords are challenging to remember.

Describe the solution you'd like Embed the Monaco editor in a cell. It provides Magic keywords autocomplete, language keyword coloring, language keyword auto-complete, node labels autocomplete, edge types autocomplete, and properties auto-complete.

Additional context Add any other context or screenshots about the feature request here. Describe any alternatives you've considered.

AndreaNassisi avatar Sep 29 '25 17:09 AndreaNassisi

There are several ways to embed the Monaco editor in a Jupyter Lab cell. Here are the most effective approaches:

Method 1: Direct HTML/JavaScript Implementation

from IPython.display import HTML, display
import json

def create_monaco_editor(initial_code="", language="python", height="400px"):
    editor_id = "monaco-editor-" + str(id(initial_code))
    
    html_content = f'''
    <div id="{editor_id}" style="width: 100%; height: {height}; border: 1px solid #ccc;"></div>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs/loader.min.js"></script>
    <script>
        require.config({{ paths: {{ vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs' }} }});
        
        require(['vs/editor/editor.main'], function() {{
            const editor = monaco.editor.create(document.getElementById('{editor_id}'), {{
                value: {json.dumps(initial_code)},
                language: '{language}',
                theme: 'vs-dark',
                automaticLayout: true,
                minimap: {{ enabled: false }},
                scrollBeyondLastLine: false
            }});
            
            // Store editor reference globally for access
            window.{editor_id.replace('-', '_')} = editor;
            
            // Optional: Add button to get editor content
            const button = document.createElement('button');
            button.innerText = 'Get Code';
            button.onclick = function() {{
                const code = editor.getValue();
                console.log(code);
                // You can also store it in a Python variable or execute it
            }};
            document.getElementById('{editor_id}').parentNode.insertBefore(button, document.getElementById('{editor_id}').nextSibling);
        }});
    </script>
    '''
    
    return HTML(html_content)

# Usage
editor = create_monaco_editor(
    initial_code="def hello_world():\n    print('Hello from Monaco!')\n    return 'success'",
    language="python",
    height="300px"
)
display(editor)

Method 2: Using ipywidgets (More Interactive)

First install the required packages:

pip install ipywidgets
jupyter nbextension enable --py widgetsnbextension
import ipywidgets as widgets
from IPython.display import display, HTML, Javascript
from traitlets import Unicode, Int

class MonacoWidget(widgets.DOMWidget):
    _view_name = Unicode('MonacoView').tag(sync=True)
    _view_module = Unicode('monaco_widget').tag(sync=True)
    _view_module_version = Unicode('0.1.0').tag(sync=True)
    
    value = Unicode('').tag(sync=True)
    language = Unicode('python').tag(sync=True)

# JavaScript for the widget
js_code = """
require.undef('monaco_widget');

define('monaco_widget', ["@jupyter-widgets/base"], function(widgets) {
    
    var MonacoView = widgets.DOMWidgetView.extend({
        
        render: function() {
            this.el.style.width = '100%';
            this.el.style.height = '400px';
            this.el.style.border = '1px solid #ccc';
            
            // Load Monaco Editor
            var script = document.createElement('script');
            script.src = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs/loader.min.js';
            script.onload = () => {
                require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs' }});
                require(['vs/editor/editor.main'], () => {
                    this.editor = monaco.editor.create(this.el, {
                        value: this.model.get('value'),
                        language: this.model.get('language'),
                        theme: 'vs-dark',
                        automaticLayout: true
                    });
                    
                    // Sync changes back to Python
                    this.editor.onDidChangeModelContent(() => {
                        this.model.set('value', this.editor.getValue());
                        this.touch();
                    });
                });
            };
            document.head.appendChild(script);
        },
        
        value_changed: function() {
            if (this.editor) {
                this.editor.setValue(this.model.get('value'));
            }
        }
    });
    
    return {
        MonacoView: MonacoView
    };
});
"""

# Display the JavaScript
display(Javascript(js_code))

# Create and display the widget
monaco_widget = MonacoWidget()
monaco_widget.value = "def example():\n    return 'Hello Monaco!'"
display(monaco_widget)

Method 3: Using Existing Packages

You can also try existing packages like jupyter-monaco:

pip install jupyter-monaco
from jupyter_monaco import Monaco

editor = Monaco(
    value="print('Hello World')",
    language='python',
    theme='vs-dark'
)
display(editor)

Method 4: Simple Inline Solution

For a quick and dirty solution:

from IPython.display import HTML

HTML('''
<div id="editor" style="width: 100%; height: 400px;"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs/loader.min.js"></script>
<script>
require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs' }});
require(['vs/editor/editor.main'], function() {
    window.monacoEditor = monaco.editor.create(document.getElementById('editor'), {
        value: 'print("Hello from Monaco Editor!")',
        language: 'python',
        theme: 'vs-dark'
    });
});
</script>
''')

Getting Code Back to Python

To execute code from Monaco in your Jupyter environment:

from IPython.display import Javascript

# JavaScript to get code and execute it in Python
Javascript('''
const code = window.monacoEditor.getValue();
const command = `
executed_code = """${code}"""
exec(executed_code)
`;
IPython.notebook.kernel.execute(command);
''')

The first method (Direct HTML/JavaScript) is usually the most reliable and easiest to implement. Choose the method that best fits your specific needs and technical requirements.

AndreaNassisi avatar Sep 29 '25 17:09 AndreaNassisi