ngx-monaco-editor icon indicating copy to clipboard operation
ngx-monaco-editor copied to clipboard

Electron Support ?

Open DevidCIC opened this issue 6 years ago • 12 comments

The Editor is working fine in Web. But running it in Electron the Editor is not loading and I get the error:

window.require.config is not a function

It is from here:

 var onGotAmdLoader = function () {
                    // Load monaco
                    window.require.config({ paths: { 'vs': baseUrl + "/monaco/vs" } });
                    window.require(['vs/editor/editor.main'], function () {
                        if (typeof _this.config.onMonacoLoad === 'function') {
                            _this.config.onMonacoLoad();
                        }
                        _this.initMonaco(_this.options);
                        resolve();
                    });
                };

DevidCIC avatar May 18 '18 09:05 DevidCIC

Currently this doesn't support Electron. You can work on that and raise a PR to dev branch.

atu1kr avatar May 18 '18 10:05 atu1kr

..how can something not support Electron?

LoganDark avatar Jun 07 '18 02:06 LoganDark

Same Issue here

danielehrhardt avatar Jul 12 '18 15:07 danielehrhardt

Here's a sample, please fix https://github.com/Microsoft/monaco-editor-samples/blob/master/electron-amd/electron-index.html

vasiltabakov avatar Nov 25 '18 12:11 vasiltabakov

I managed to make Monaco-Editor run in Electron. What I did is overwrite the ngAfterViewInit method from base-editor class generated in JavaScript with my additional implementation. I do this in my component class. Here is the code, hope it helps others (MAYBE SOMEONE CAN IMPLEMENT THIS AND CREATE A PULL REQUEST):

import { remote } from 'electron';
import { BaseEditor } from 'ngx-monaco-editor/base-editor';
import { BehaviorSubject } from 'rxjs';
import { KeyCode } from '../controls/ngx-datepicker/enums/key-code.enum';
import { ClarionEditorService } from './clarion-editor.service';

let loadedMonaco = false; // need this for electron
let loadPromise: Promise<void>; // need this for electron

// this function overwrites the internal function of ngx-monaco-editor
// and it is used just so that monaco editor can run in electron
BaseEditor.prototype.ngAfterViewInit = function() {
    // tslint:disable-next-line:no-invalid-this
    const _this = this;
    if (loadedMonaco) {
        // Wait until monaco editor is available
        loadPromise.then(() => {
            _this.initMonaco(_this.options);
        });
    } else {
        loadedMonaco = true;
        loadPromise = new Promise<void>((resolve: any) => {
            const baseUrl: string = _this.config.baseUrl || '/assets';
            if (typeof ((<any>window).monaco) === 'object') {
                resolve();
                return;
            }

            const onGotAmdLoader: any = () => {
                // Load monaco
                (<any>window).require.config({ paths: { vs: `${baseUrl}/monaco/vs` } });
                (<any>window).require(['vs/editor/editor.main'], () => {
                    if (typeof _this.config.onMonacoLoad === 'function') {
                        _this.config.onMonacoLoad();
                    }
                    _this.initMonaco(_this.options);
                    resolve();
                });
            };

            // Load AMD loader if necessary
            if (!(<any>window).require) {
                const loaderScript: HTMLScriptElement = document.createElement('script');
                loaderScript.type = 'text/javascript';
                loaderScript.src = `${baseUrl}/monaco/vs/loader.js`;
                loaderScript.addEventListener('load', onGotAmdLoader);
                document.body.appendChild(loaderScript);
            } else if (!(<any>window).require.config) {
                const amdLoader = (<any>window).require(`${baseUrl}/monaco/vs/loader.js`);
                const amdRequire = amdLoader.require;

                const monacoDir: string = remote.getGlobal('monacoDir');

                // Load monaco
                const mncDir = monacoDir.replace(/\\/g, '/');
                amdRequire.config({ paths: { vs: mncDir } });
                console.log('​BaseEditor.prototype.ngAfterViewInit -> monacoDir', mncDir);

                amdRequire(['vs/editor/editor.main'], () => {
                    if (typeof _this.config.onMonacoLoad === 'function') {
                        _this.config.onMonacoLoad();
                    }
                    _this.initMonaco(_this.options);
                    resolve();
                });

            } else {
                onGotAmdLoader();
            }
        });
    }
};


@Component({
    selector: 'clarion-editor',
    templateUrl: 'clarion-editor.component.html',
    styleUrls: ['clarion-editor.component.scss'],
})

export class ClarionEditorComponent implements OnChanges, OnDestroy {
    @Input() public text = new BehaviorSubject<string>('');
    @Input() public variables: ContextVariable[] = [];
    // @Input() public functions: any[];
    @Output() public onLoaded = new EventEmitter();
    // @Output() cursorPosition = new EventEmitter<monaco.editor.ICursorSelectionChangedEvent>();
    // public clarionEditor: monaco.editor.ICodeEditor;

    public editorOptions = <monaco.editor.IEditorOptions>{
        wordWrap: 'on',
        lineNumbersMinChars: 1,
        scrollBeyondLastLine: false,
        renderLineHighlight: 'none',
        minimap: { enabled: false },
        scrollbar: true,
        language: 'clarionLanguage',
        theme: 'clarionTheme',
        mouseWheelZoom: true,
        fontSize: 16,
        automaticLayout: true,
        folding: false,
        // renderControlCharacters: true,
        // renderWhitespace: 'all',
        // formatOnPaste: true,
        // formatOnType: true,
    };

    public monacoEditor: monaco.editor.IStandaloneCodeEditor;

    constructor(private readonly editorService: ClarionEditorService) { }

    public onEditorInit(editor: monaco.editor.IStandaloneCodeEditor) {
        this.monacoEditor = editor;

//... ... ... ... irrelevant code

In my main.electron.js I defined the monacoDir variable:

  global.monacoDir = `file:///${__dirname}/dist/assets/monaco/vs`;

DevidCIC avatar Nov 28 '18 16:11 DevidCIC

A variation of the above solution worked for me.

One thing that's worth noting is that you can actual get around having to define a global monacoDir variable by using app.getAppPath() from remote.

const monacoDir = `${require('remote').app.getAppPath()}/dist/${baseUrl}`;

That being said, in development you will get an error complaining that you're not allowed to load a local resource editor.main.css if you do it this way. You can get around this by adding webSecurity: false when creating the browser window in development

win = new BrowserWindow({
    x: 0,
    y: 0,
    width: size.width,
    height: size.height,
    webPreferences: {
      webSecurity: (development) ? false : true,
    },
  });

Hkattelu avatar Dec 02 '19 07:12 Hkattelu

I found this: https://github.com/materiahq/ngx-monaco-editor and it works out of the box with https://github.com/maximegris/angular-electron.git.

jakehockey10 avatar Dec 06 '19 05:12 jakehockey10

@jakehockey10 But it seems to use and old version of angular and monaco, right?

zerocewl avatar Dec 13 '19 10:12 zerocewl

@zerocewl The editor version may not be latest, but I it seems I'm using latest Angular

jakehockey10 avatar Dec 14 '19 16:12 jakehockey10

I confer with jakehockey10; angular8, bootsrap 4 and electron 8 - the https://github.com/materiahq/ngx-monaco-editor fork works perfectly in electron

neil-benn avatar Mar 23 '20 18:03 neil-benn

The easy way is set nodeIntegration as false. 6bcf19ba257a209f238a0b6469558e9

chenwei979 avatar Aug 11 '22 09:08 chenwei979

The second way is rename require(from Electron) to nodeRequire. image

Then use nodeRequire to get Electron-related modules c46f96ae8038cc3f9cdb1a0dce5f9c5

chenwei979 avatar Aug 11 '22 09:08 chenwei979