Vue.js or Angular app run in minibrowser

Hi all,

I’m trying since some days to run a vue or angular web app inside a minibrowser window instanced via theia.
To do this, I was trying to embed the content of dist folder of the web app project - got after building process - inside the html attribute of the webview. Unfortunely , the minibrowser window results always empty.

So my question is: what I’m trying to do is feasible in general?

Thanks and regards,
Davide

Could you share your project setup?

Yes sure. I’m writing a plugin that should serve this web app in a webview by invoking a particular command.
Web app content files are stored under resources/app/ folder of the plugin:

  • index.html
  • favicon.ico
  • css/
    • chunk-vendors…css
  • js/
    • app…js
    • app…ja.map
    • chunk-vendors…js
    • chunk-vendors…js.map

Below you can find the main code of plugin. (Note: it works fine by serving a static index.html file. I think the relevant code is at private _getHtmlForWebview(){...} )

import * as theia from '@theia/plugin';
import * as path from 'path';
const fs = require('fs');
const SerialPort = require('serialport');


export class WepAppPanel {
    /**
     * Track the currently panel. Only allow a single panel to exist at a time.
     */
    public static currentPanel: WepAppPanel | undefined;
    private _backgroundOutputChannel: theia.OutputChannel;
    public static readonly viewType = 'webApp';

    private readonly _panel: theia.WebviewPanel;
    private readonly _extensionPath: string;
    private _disposables: theia.Disposable[] = [];

    public static createOrShow(extensionPath: string) {
        const column = theia.window.activeTextEditor ? theia.window.activeTextEditor.viewColumn : undefined;
        if (WepAppPanel.currentPanel) {
            WepAppPanel.currentPanel._panel.reveal(column);
            return;
        }
        const panel = theia.window.createWebviewPanel(WepAppPanel.viewType, 'WebApp', column || theia.ViewColumn.One, {
            enableScripts: true,
            localResourceRoots: [
                theia.Uri.file(path.join(extensionPath, 'resources','app'))
            ]
        });
        const darkIconUri = theia.Uri.file(path.join(extensionPath, 'resources', 'search.png'));
        panel.iconPath = darkIconUri;
        WepAppPanel.currentPanel = new WepAppPanel(panel, extensionPath);
    }

    public static revive(panel: theia.WebviewPanel, extensionPath: string) {
        WepAppPanel.currentPanel = new WepAppPanel(panel, extensionPath);
    }

    private constructor(panel: theia.WebviewPanel, extensionPath: string) {
        this._panel = panel;
        this._extensionPath = extensionPath;
        this._backgroundOutputChannel = theia.window.createOutputChannel('WebApp');
        this._backgroundOutputChannel.show();
        this._update();
        this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
        this._panel.onDidChangeViewState(e => {
            if (this._panel.visible) {
                this._update()
            }
        }, null, this._disposables);
        this._panel.webview.onDidReceiveMessage(message => {

            switch (message.command) {
                case 'connect':
                    this.connectAndReceive();
                    return;
                case 'disconnect':
                    theia.window.showErrorMessage(message.text);
                    return;
                case 'shellcommand':
                    this.getAcquisition(message.text);
                    return;
            }
        }, null, this._disposables);
    }

    public async connectAndReceive() {
        let portsList: any[] = (await SerialPort.list() as any[]);
        const port = new SerialPort(portsList.find((p:any)=>p.manufacturer=="example").path, {
            baudRate: 112500
        })
        // Read data that is available but keep the stream in "paused mode"
        port.on('readable', () => {
            this._backgroundOutputChannel.show();
            this._backgroundOutputChannel.append(port.read());
        })
    }

    public async updateWebView(){
        this._panel.webview.html = this._getHtmlForWebview();
    }

    public async getAcquisition(command: string) {
        const terminal = this.createTerminalWithOptions();
        terminal.show();
        await this.sleep(1000);
        terminal.sendText(command);
    }

    public async sleep(milliseconds: number = 0): Promise<any> {
        return new Promise(resolve => setTimeout(resolve, milliseconds));;
    }

    public createTerminalWithOptions(): theia.Terminal {
        const termOptions: theia.TerminalOptions = {
            name: 'Test terminal',
            shellPath: '/bin/bash'
        }
        return theia.window.createTerminal(termOptions);
    }

    public setOptions(options: theia.WebviewOptions) {
        if (WepAppPanel.currentPanel) {
            WepAppPanel.currentPanel._panel.webview.options = options;
            return;
        }
    }

    public doRefactor() {
        this._panel.webview.postMessage({ command: 'refactor' });
    }

    public dispose() {
        WepAppPanel.currentPanel = undefined;
        this._panel.dispose();
        while (this._disposables.length) {
            const x = this._disposables.pop();
            if (x) {
                x.dispose();
            }
        }
    }

    private _update() {
        this._panel.title = 'Web app';
        this._panel.webview.html = this._getHtmlForWebview();
    }

    private _getHtmlForWebview() {
        const scriptPathOnDisk = theia.Uri.file(path.join(this._extensionPath, 'resources', 'app','index.html'));
        const scriptUri = scriptPathOnDisk.with({ scheme: 'theia-resource' });
        return fs.readFileSync(scriptUri.fsPath);
    }
}

I am not sure how webview and mini-browser are related. Could you elaborate please?

btw you should not use internal schemes like theia-resource but Webview.asWebviewUri function instead: https://github.com/microsoft/vscode/blob/307cb32f306a527a30a5b1756e8bf548c141192c/src/vs/vscode.d.ts#L6703