user9491184
user9491184

Reputation:

VSCode Extension webview external html and css

I'm trying to create a vscode extension and the goal is to display a webview with a extern html and css.
Loading and setting the html works but unfortunately the css won't get load.

Creating webview,loading html and setting :

var resultPanel = vscode.window.createWebviewPanel("test", "TestWebView", vscode.ViewColumn.One, {});
    fs.readFile(path.join(context.extensionPath,'src','languageMenue.html'),(err,data) => {
          if(err) {console.error(err)}
          resultPanel.webview.html = data;
      });

This works, inside the html the css get loaded like that :

<meta charset="UTF-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="theme.css">

The css is in the same folder as the html (inside the src folder of my the extension project)

What am I missing? Why the css won't get load?

Upvotes: 11

Views: 14438

Answers (5)

MegaMech
MegaMech

Reputation: 33

I was struggling to figure things out so here's a more verbose solution using registerWebviewViewProvider which is setup slightly differently.

I want to point out that localResourceRoots should contain directories not specific files (Took me awhile to figure that out).

html:

<!DOCTYPE html>
<html lang="en">
    <head>
        <link rel="stylesheet" href="{{pathCSS}}">
    </head>
    <body>

typescript:

export function activate(context: vscode.ExtensionContext) {
    const provider = new customViewProvider(context.extensionPath);
}
class customViewProvider implements vscode.WebviewViewProvider {
    public static readonly viewType = "decomp_webview";
    private _view?: vscode.WebviewView;
    private extensionUri: string;
    public webview: vscode.Webview | undefined;
    constructor(
        private readonly _extensionUri: string,
    ) {
        this.extensionUri = _extensionUri;
     }

    public resolveWebviewView(
        webviewView: vscode.WebviewView,
        context: vscode.WebviewViewResolveContext,
        _token: vscode.CancellationToken,
    ) {
        this._view = webviewView;
        console.log("PATH: "+this._extensionUri);
        webviewView.webview.options = {
            enableScripts: true,
            localResourceRoots: [
                vscode.Uri.file(path.join(this.extensionUri, "/src")),
            ]
        };

        let html = fs.readFileSync(vscode.Uri.file(path.join(this.extensionUri, "src/ui.html")).with({scheme: 'vscode-resource'}).fsPath, "utf-8");

        const pathToCSS = vscode.Uri.file(path.join(this.extensionUri, 'src/ui.css'));

        html = html.replace("{{pathCSS}}", String(webviewView.webview.asWebviewUri(pathCSS)));

        console.log(html);

        const updateWebview = () => {
            webviewView.webview.html = html
        };

        setInterval(updateWebview, 1000);

Upvotes: 0

rgos
rgos

Reputation: 99

You have to replace the css filename in your html file with styleUri.toString(), just binding with {{styleSrc}} does not work.

const htmlPathOnDisk = vscode.Uri.file(path.join(this._extensionPath, '/res/html/index.html'));
var html = fs.readFileSync(htmlPathOnDisk.path).toString();

const stylePathOnDisk = vscode.Uri.file(path.join(this._extensionPath, '/res/css/styles.css'));
const styleUri = webview.asWebviewUri(stylePathOnDisk);
html = html.replace('filename_of_css_file_in_your_html_file.css', styleUri.toString());

// continue for any other resources, e.g. scripts

webview.html = html

Upvotes: 0

Jimi
Jimi

Reputation: 1228

To make this work you have to specify the absolute path where the css file is stored.

i.e.

<link rel="stylesheet" type="text/css" href="/path/to/dir/of/theme.css">

Since vscode suggests to use secure content policy, the way to achieve dynamic loading of the path is like this:

<link rel="stylesheet" type="text/css" href="{{styleSrc}}">

where {{styleSrc}} is binded as:

const styleSrc = vscode.Uri.file(path.join(vscode.context.extensionPath, '/path/to/dir/of/theme.css')).with({ scheme: 'vscode-resource' })

or with the asWebviewUri API as:

const onDiskPath = vscode.Uri.file(path.join(vscode.context.extensionPath, '/path/to/dir/of/theme.css'))
const styleSrc = panel.webview.asWebviewUri(onDiskPath)

where panel is the one created with vscode.window.createWebviewPanel(...)

For more info see: https://code.visualstudio.com/api/extension-guides/webview#loading-local-content

Upvotes: 5

aviit
aviit

Reputation: 2109

Please read this Controlling access to local resources

And note about localResourceRoots as below code:

const panel = vscode.window.createWebviewPanel(
        'catCoding',
        'Cat Coding',
        vscode.ViewColumn.One,
        {
          // Only allow the webview to access resources in our extension's media directory
          localResourceRoots: [vscode.Uri.file(path.join(context.extensionPath, 'media'))]
        }
      );

You can edit the line to:
localResourceRoots: [vscode.Uri.file(path.join(context.extensionPath, 'your/relative/location/here/in/your/extension/path'))]

Upvotes: 10

Almenon
Almenon

Reputation: 1466

Read through the vscode webview guide: https://code.visualstudio.com/api/extension-guides/webview

Specifically pay close attention to the "Loading local content" section: https://code.visualstudio.com/api/extension-guides/webview#loading-local-content

There are special rules for how to load external content for security reasons.

Upvotes: 1

Related Questions