一键导入
vscode-webview-and-ui
Guidance for building VS Code webviews and native extension UI while preserving accessibility, security, and theme compatibility.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Guidance for building VS Code webviews and native extension UI while preserving accessibility, security, and theme compatibility.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Guidance for building, packaging, and releasing VS Code extensions with esbuild, vsce, and GitHub Actions workflows.
Guidance for implementing and modifying VS Code commands, menus, when clauses, and activation behavior.
Guidance for declaring, reading, updating, and reacting to VS Code extension configuration and settings changes safely.
Guidance for implementing and modifying VS Code extension architecture, activation flow, disposables, and extension-host-safe patterns.
Guidance for keeping VS Code extension localization, package.nls files, README content, and changelog updates in sync.
Guidance for implementing VS Code language features such as definitions, hovers, completions, symbols, diagnostics, formatting, and related providers.
基于 SOC 职业分类
| name | vscode-webview-and-ui |
| description | Guidance for building VS Code webviews and native extension UI while preserving accessibility, security, and theme compatibility. |
VS Code provides both native UI components (QuickPick, InputBox, TreeView, StatusBar, etc.) and the Webview API for fully custom HTML/CSS/JS panels. Always prefer native UI when it fits — it stays consistent with VS Code themes and accessibility standards. Use Webviews for complex interactive UIs that cannot be expressed with native components.
// Information / Warning / Error messages
await vscode.window.showInformationMessage('Operation complete.');
const action = await vscode.window.showWarningMessage('Overwrite file?', 'Yes', 'Cancel');
vscode.window.showErrorMessage(`Failed: ${error.message}`);
// Simple string pick
const choice = await vscode.window.showQuickPick(['Option A', 'Option B'], {
placeHolder: 'Select an option',
canPickMany: false,
});
// Structured items
const items: vscode.QuickPickItem[] = [
{ label: '$(symbol-class) MyClass', description: 'src/MyClass.ts', detail: 'A class' },
];
const selected = await vscode.window.showQuickPick(items, { matchOnDescription: true });
const value = await vscode.window.showInputBox({
prompt: 'Enter Scene Builder path',
placeHolder: '/Applications/SceneBuilder.app',
validateInput: (v) => v.trim() ? undefined : 'Path cannot be empty',
});
await vscode.window.withProgress(
{ location: vscode.ProgressLocation.Notification, title: 'Indexing…', cancellable: true },
async (progress, token) => {
for (let i = 0; i < steps.length; i++) {
if (token.isCancellationRequested) { break; }
progress.report({ message: steps[i], increment: (100 / steps.length) });
await processStep(steps[i]);
}
}
);
const item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
item.text = '$(symbol-class) FXML';
item.tooltip = 'JavaFX Support active';
item.command = 'ext.myCommand';
item.show();
context.subscriptions.push(item);
export class MyTreeProvider implements vscode.TreeDataProvider<MyNode> {
private _onDidChangeTreeData = new vscode.EventEmitter<MyNode | undefined | void>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
refresh(): void { this._onDidChangeTreeData.fire(); }
getTreeItem(element: MyNode): vscode.TreeItem {
const item = new vscode.TreeItem(element.label, vscode.TreeItemCollapsibleState.Collapsed);
item.iconPath = new vscode.ThemeIcon('symbol-class');
item.command = { command: 'ext.openNode', title: 'Open', arguments: [element] };
return item;
}
getChildren(element?: MyNode): vscode.ProviderResult<MyNode[]> {
return element ? element.children : this.roots;
}
}
const provider = new MyTreeProvider();
context.subscriptions.push(
vscode.window.createTreeView('myView', { treeDataProvider: provider, showCollapseAll: true })
);
Register the view in package.json:
"contributes": {
"views": {
"explorer": [
{ "id": "myView", "name": "My View", "when": "ext.myFeatureEnabled" }
]
}
}
Use Webviews for rich custom panels (e.g., visual editors, dashboards, preview panes).
const panel = vscode.window.createWebviewPanel(
'myPanel', // viewType (unique identifier)
'My Panel', // title
vscode.ViewColumn.Beside, // editor column
{
enableScripts: true, // allow JavaScript in the webview
localResourceRoots: [ // restrict file access to these URIs
vscode.Uri.joinPath(context.extensionUri, 'media'),
],
retainContextWhenHidden: false, // true = keep alive when tab is hidden (memory cost)
}
);
context.subscriptions.push(panel);
panel.webview.html = getWebviewContent(panel.webview, context.extensionUri);
function getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string {
const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'main.js'));
const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'main.css'));
const nonce = getNonce(); // random string for CSP
return /* html */ `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; style-src ${webview.cspSource}; script-src 'nonce-${nonce}';">
<link rel="stylesheet" href="${styleUri}">
</head>
<body>
<div id="app"></div>
<script nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>`;
}
function getNonce(): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return Array.from({ length: 32 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
}
// Extension → Webview
panel.webview.postMessage({ command: 'update', data: payload });
// Webview → Extension
panel.webview.onDidReceiveMessage((message) => {
switch (message.command) {
case 'save': saveData(message.data); break;
case 'alert': vscode.window.showErrorMessage(message.text); break;
}
});
// In webview JavaScript (media/main.js):
const vscode = acquireVsCodeApi();
vscode.postMessage({ command: 'save', data: formData });
window.addEventListener('message', (event) => {
const msg = event.data;
if (msg.command === 'update') { renderUpdate(msg.data); }
});
For file-format-specific visual editors (e.g., a visual FXML layout editor):
export class FxmlEditorProvider implements vscode.CustomTextEditorProvider {
static register(context: vscode.ExtensionContext): vscode.Disposable {
return vscode.window.registerCustomEditorProvider('ext.fxmlEditor', new FxmlEditorProvider(context));
}
async resolveCustomTextEditor(
document: vscode.TextDocument,
webviewPanel: vscode.WebviewPanel,
token: vscode.CancellationToken
): Promise<void> {
webviewPanel.webview.options = { enableScripts: true };
webviewPanel.webview.html = this.getHtml(webviewPanel.webview);
// Sync document changes → webview
const changeSubscription = vscode.workspace.onDidChangeTextDocument((e) => {
if (e.document.uri.toString() === document.uri.toString()) {
webviewPanel.webview.postMessage({ type: 'update', text: document.getText() });
}
});
webviewPanel.onDidDispose(() => changeSubscription.dispose());
}
}
Register in package.json:
"customEditors": [
{
"viewType": "ext.fxmlEditor",
"displayName": "FXML Visual Editor",
"selector": [{ "filenamePattern": "*.fxml" }],
"priority": "option"
}
]
Content-Security-Policy with a nonce.textContent in JS, not innerHTML).localResourceRoots as narrow as possible.enableScripts unless the webview actually needs scripts.