Create new webviews with IPC protocol, Lit app, and registration
Scaffold a new webview with all required boilerplate.
/add-webview [name]
myFeature)view (sidebar) or panel (editor panel)src/webviews/{name}/protocol.tsimport type { IpcScope } from '../ipc/models/ipc.js';
import { IpcCommand, IpcNotification, IpcRequest } from '../ipc/models/ipc.js';
import type { WebviewState } from '../protocol.js';
export const scope: IpcScope = '{name}';
export interface State extends WebviewState<'gitlens.views.{name}'> {
loading: boolean;
}
// Commands (fire-and-forget)
export interface DoSomethingParams {
id: string;
}
export const DoSomethingCommand = new IpcCommand<DoSomethingParams>(scope, 'doSomething');
// Requests (with response)
export interface GetDataParams {
filter?: string;
}
export interface GetDataResponse {
items: unknown[];
}
export const GetDataRequest = new IpcRequest<GetDataParams, GetDataResponse>(scope, 'getData');
// Notifications (host → webview)
export interface DidChangeDataParams {
items: unknown[];
}
export const DidChangeDataNotification = new IpcNotification<DidChangeDataParams>(scope, 'data/didChange');
src/webviews/{name}/{name}Webview.tsimport type { Disposable } from 'vscode';
import type { Container } from '../../container.js';
import type { WebviewHost, WebviewProvider } from '../webviewProvider.js';
import { ipcCommand, ipcRequest } from '../ipc/handlerRegistry.js';
import type { IpcParams, IpcResponse } from '../ipc/models/ipc.js';
import type { State } from './protocol.js';
import { DoSomethingCommand, GetDataRequest, DidChangeDataNotification } from './protocol.js';
export class {Name}WebviewProvider implements WebviewProvider<State, State>, Disposable {
constructor(
private readonly container: Container,
private readonly host: WebviewHost<'gitlens.views.{name}'>,
) {}
dispose(): void {}
async includeBootstrap(): Promise<State> {
return {
webviewId: this.host.id,
webviewInstanceId: this.host.instanceId,
loading: false,
};
}
@ipcCommand(DoSomethingCommand)
private async onDoSomething(params: IpcParams<typeof DoSomethingCommand>): Promise<void> {
// Handle command
}
@ipcRequest(GetDataRequest)
private async onGetData(params: IpcParams<typeof GetDataRequest>): Promise<IpcResponse<typeof GetDataRequest>> {
return { items: [] };
}
}
src/webviews/{name}/registration.tsimport type { WebviewsController } from '../webviewsController.js';
import type { WebviewViewProxy } from '../webviewProxy.js';
import type { State } from './protocol.js';
export type {Name}WebviewShowingArgs = [];
export function register{Name}WebviewView(
controller: WebviewsController,
): WebviewViewProxy<'gitlens.views.{name}', {Name}WebviewShowingArgs, State> {
return controller.registerWebviewView<'gitlens.views.{name}', State, State, {Name}WebviewShowingArgs>(
{
id: 'gitlens.views.{name}',
fileName: '{name}.html',
title: '{Title}',
contextKeyPrefix: 'gitlens:webviewView:{name}',
trackingFeature: '{name}View',
type: '{name}',
plusFeature: false,
webviewHostOptions: { retainContextWhenHidden: true },
},
async (container, host) => {
const { {Name}WebviewProvider } = await import(
/* webpackChunkName: "webview-{name}" */ './{name}Webview.js'
);
return new {Name}WebviewProvider(container, host);
},
);
}
src/webviews/apps/{name}/{name}.tsimport { html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { GlAppHost } from '../shared/appHost.js';
import type { HostIpc } from '../shared/ipc.js';
import type { LoggerContext } from '../shared/contexts/logger.js';
import type { State } from '../../{name}/protocol.js';
import { {Name}StateProvider } from './stateProvider.js';
import { styles } from './{name}.css.js';
@customElement('gl-{name}-app')
export class Gl{Name}App extends GlAppHost<State, {Name}StateProvider> {
static override styles = styles;
protected override createStateProvider(
bootstrap: string, ipc: HostIpc, logger: LoggerContext,
): {Name}StateProvider {
return new {Name}StateProvider(this, bootstrap, ipc, logger);
}
override render() {
return html`<div class="{name}"><h1>{Title}</h1></div>`;
}
}
src/webviews/apps/{name}/stateProvider.tsimport type { Disposable } from 'vscode';
import type { StateProvider } from '../shared/stateProviderBase.js';
import type { HostIpc } from '../shared/ipc.js';
import type { LoggerContext } from '../shared/contexts/logger.js';
import type { State } from '../../{name}/protocol.js';
export class {Name}StateProvider implements StateProvider<State>, Disposable {
readonly state: State;
constructor(
private readonly host: Gl{Name}App,
bootstrap: string,
private readonly ipc: HostIpc,
private readonly logger: LoggerContext,
) {
this.state = JSON.parse(bootstrap);
}
dispose(): void {}
}
src/webviews/apps/{name}/{name}.css.tsimport { css } from 'lit';
export const styles = css`
:host { display: block; height: 100%; }
.{name} { padding: 1rem; }
`;
For accessibility requirements when creating or modifying webview components, see docs/accessibility.md.
getWebviewsConfigs() in webpack.config.mjssrc/webviews/webviewsController.tssrc/constants.views.tspnpm run build:webviews| Component | Community | Pro |
|---|---|---|
| Protocol/Provider | src/webviews/{name}/ | src/webviews/plus/{name}/ |
| App | src/webviews/apps/{name}/ | src/webviews/apps/plus/{name}/ |