Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webview codicons: styling and interactions with codicons #352

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 67 additions & 2 deletions webview-codicons-sample/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Cat Codicons
# Webview with Codicons

Demonstrates loading [codicons](https://github.com/microsoft/vscode-codicons) in a [webview](https://code.visualstudio.com/api/extension-guides/webview).

Expand All @@ -15,4 +15,69 @@ Demonstrates loading [codicons](https://github.com/microsoft/vscode-codicons) in
- `npm run watch` or `npm run compile`
- `F5` to start debugging

Run the `Cat Codicons: Show Cat Codicons` command to create the webview.
Run the `Webview Codicons: Show Webview Codicons` command to create the webview.

While similar user experience can be achieved by including image files into your extension and using the `<img>` tag in the Webview HTML, using Codicons provides far simpler way to support themes. While on the _Webview Codicons_ panel, try switching between the light and dark theme.

![demo](demo.gif)

## How to use Codicons

First, include the `vscode-codicons` package into the `dependencies` in `package.json`:

```json
"dependencies": {
"vscode-codicons": "0.0.12"
}
```

Second, refer to the Codicons font and styles from your Webview HTML:

Get resource paths:

```typescript
const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'styles.css'));
const codiconsUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'node_modules', 'vscode-codicons', 'dist', 'codicon.css'));
const codiconsFontUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'node_modules', 'vscode-codicons', 'dist', 'codicon.ttf'));
```

Declare the Codicons resources in the Content Security Policy and link the stylesheets into your HTML:

```html
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; font-src ${codiconsFontUri}; style-src ${webview.cspSource} ${codiconsUri};">

<link href="${styleUri}" rel="stylesheet" />
<link href="${codiconsUri}" rel="stylesheet" />
```

Last, add any Codicon into your HTML:

```html
<i class="codicon codicon-check"></i>
```

## Styling and interacting with codicons

Icons can be styled as any other HTML content:

```html
<div class="styledIcon"><i class="codicon codicon-check"></i> check</div>
```

```css
div.styledIcon .codicon {
font-size: 50px;
color: green;
padding: 3px;
}
```

However, it is recommended to use CSS variable to reference a VS Code color token,
rather than hard-coding any concrete color.

```css
color: var(--vscode-debugIcon-startForeground);
```

![demo styling and interacting with codicons](demo_styling.gif)
Binary file modified webview-codicons-sample/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webview-codicons-sample/demo_styling.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 36 additions & 1 deletion webview-codicons-sample/media/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,40 @@ h1 {

#icons .icon .codicon {
font-size: 32px;
padding-bottom: 16px;
}

/* Codicon styling and interaction demo */

div.iconBar .codicon {
font-size: 60px;
cursor: pointer;
}

div.iconBar .disabled {
opacity: 0.3;
cursor: not-allowed;
}

#play {
/* Use CSS variable to reference a VS Code color token, rather than any concrete color. */
color: var(--vscode-debugIcon-startForeground);
}

#stop {
color: var(--vscode-debugIcon-stopForeground);
}

/* Rotating the 'loading' Codicon to indicate _progress_.
Same implementation as what VS Code is using in the user interface.
https://github.com/microsoft/vscode/blob/master/src/vs/base/browser/ui/codicons/codicon/codicon-animations.css
*/
@keyframes codicon-spin {
100% {
transform:rotate(360deg);
}
}

.codicon-animation-spin {
/* Use steps to throttle FPS to reduce CPU usage */
animation: codicon-spin 1.5s steps(30) infinite;
}
21 changes: 11 additions & 10 deletions webview-codicons-sample/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "cat-codicons",
"description": "Cat Codicons - Using codicons in webviews",
"version": "0.0.1",
"name": "webview-codicons",
"description": "Webview Codicons - Using codicons in webviews",
"version": "0.0.2",
"publisher": "vscode-samples",
"engines": {
"vscode": "^1.47.0"
Expand All @@ -10,7 +10,7 @@
"Other"
],
"activationEvents": [
"onCommand:catCodicons.show"
"onCommand:webviewCodicons.show"
],
"repository": {
"type": "git",
Expand All @@ -20,9 +20,9 @@
"contributes": {
"commands": [
{
"command": "catCodicons.show",
"title": "Show Cat Codicons",
"category": "Cat Codicons"
"command": "webviewCodicons.show",
"title": "Show Webview Codicons",
"category": "Webview Codicons"
}
]
},
Expand All @@ -32,14 +32,15 @@
"lint": "eslint . --ext .ts,.tsx",
"watch": "tsc -w -p ./"
},
"dependencies": {},
"dependencies": {
"vscode-codicons": "0.0.14"
},
"devDependencies": {
"@types/node": "^12.12.0",
"@types/vscode": "^1.47.0",
"@typescript-eslint/eslint-plugin": "^4.16.0",
"@typescript-eslint/parser": "^4.16.0",
"eslint": "^7.21.0",
"typescript": "^4.2.2",
"vscode-codicons": "0.0.7"
"typescript": "^4.2.2"
}
}
65 changes: 57 additions & 8 deletions webview-codicons-sample/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,37 @@ import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('catCodicons.show', () => {
CatCodiconsPanel.show(context.extensionUri);
vscode.commands.registerCommand('webviewCodicons.show', () => {
WebviewCodiconsPanel.show(context.extensionUri);
})
);
}


class CatCodiconsPanel {
class WebviewCodiconsPanel {

public static readonly viewType = 'catCodicons';
public static readonly viewType = 'webviewCodicons';

public static show(extensionUri: vscode.Uri) {
const column = vscode.window.activeTextEditor
? vscode.window.activeTextEditor.viewColumn
: undefined;

const panel = vscode.window.createWebviewPanel(
CatCodiconsPanel.viewType,
"Cat Codicons",
WebviewCodiconsPanel.viewType,
"Webview Codicons",
column || vscode.ViewColumn.One
);

panel.webview.options = {
// Allow scripts in the webview
enableScripts: true,

localResourceRoots: [
extensionUri
]
}

panel.webview.html = this._getHtmlForWebview(panel.webview, extensionUri);
}

Expand All @@ -34,6 +43,9 @@ class CatCodiconsPanel {
const codiconsUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'node_modules', 'vscode-codicons', 'dist', 'codicon.css'));
const codiconsFontUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'node_modules', 'vscode-codicons', 'dist', 'codicon.ttf'));

// Use a nonce to only allow a specific script to be run.
const nonce = getNonce();

return `<!DOCTYPE html>
<html lang="en">
<head>
Expand All @@ -43,15 +55,16 @@ class CatCodiconsPanel {
Use a content security policy to only allow loading images from https or from our extension directory,
and only allow scripts that have a specific nonce.
-->
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${codiconsFontUri}; style-src ${webview.cspSource} ${codiconsUri};">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${codiconsFontUri}; style-src ${webview.cspSource} ${codiconsUri}; script-src 'nonce-${nonce}'">

<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cat Coding</title>
<title>Webview Coding</title>

<link href="${styleUri}" rel="stylesheet" />
<link href="${codiconsUri}" rel="stylesheet" />
</head>
<body>

<h1>codicons</h1>
<div id="icons">
<div class="icon"><i class="codicon codicon-account"></i> account</div>
Expand Down Expand Up @@ -380,8 +393,44 @@ class CatCodiconsPanel {
<div class="icon"><i class="codicon codicon-zoom-in"></i> zoom-in</div>
<div class="icon"><i class="codicon codicon-zoom-out"></i> zoom-out</div>
</div>

<h1>styling and interacting with codicons</h1>
<div class="iconBar">
<i id="play" class="codicon disabled codicon-play-circle"></i>
<i id="stop" class="codicon codicon-stop-circle"></i>
<i id="loading" class="codicon codicon-loading codicon-animation-spin" title="This icon is styled with continuous rotation"></i>
</div>

<script nonce="${nonce}">
function play() {
if (!document.getElementById('play').classList.contains('disabled')) {
document.getElementById('play').classList.add('disabled');
document.getElementById('stop').classList.remove('disabled');
document.getElementById('loading').classList.add('codicon-animation-spin');
}
}

function stop() {
if (!document.getElementById('stop').classList.contains('disabled')) {
document.getElementById('play').classList.remove('disabled');
document.getElementById('stop').classList.add('disabled');
document.getElementById('loading').classList.remove('codicon-animation-spin');
}
}

document.getElementById('play').onclick = play;
document.getElementById('stop').onclick = stop;
</script>
</body>
</html>`;
}
}

function getNonce() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}