Skip to content

Commit 33201e2

Browse files
author
iganbold
committed
Implement webview functionality in the Superdesign VS Code extension. Added a new design panel with color and typography tabs, integrated React for UI components, and established communication between the webview and the extension. Updated build scripts to bundle the webview assets and added necessary CSS styles. Enhanced TypeScript configuration for better compatibility.
1 parent b5ab313 commit 33201e2

10 files changed

Lines changed: 599 additions & 23 deletions

File tree

esbuild.js

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,45 @@ async function main() {
4242
esbuildProblemMatcherPlugin,
4343
],
4444
});
45+
46+
// Webview build context
47+
const webviewCtx = await esbuild.context({
48+
entryPoints: ['src/webview/index.tsx'],
49+
bundle: true,
50+
format: 'esm',
51+
minify: production,
52+
sourcemap: !production,
53+
sourcesContent: false,
54+
platform: 'browser',
55+
outfile: 'dist/webview.js',
56+
logLevel: 'silent',
57+
plugins: [esbuildProblemMatcherPlugin],
58+
loader: {
59+
'.css': 'text',
60+
'.png': 'file',
61+
'.jpg': 'file',
62+
'.svg': 'file',
63+
},
64+
define: {
65+
'process.env.NODE_ENV': production ? '"production"' : '"development"',
66+
},
67+
jsx: 'automatic', // This enables JSX support
68+
});
69+
4570
if (watch) {
46-
await ctx.watch();
71+
await Promise.all([
72+
ctx.watch(),
73+
webviewCtx.watch()
74+
]);
75+
console.log('Watching for changes...');
4776
} else {
48-
await ctx.rebuild();
77+
await Promise.all([
78+
ctx.rebuild(),
79+
webviewCtx.rebuild()
80+
]);
4981
await ctx.dispose();
82+
await webviewCtx.dispose();
83+
console.log('Build complete!');
5084
}
5185
}
5286

package-lock.json

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@
2222
"commands": [
2323
{
2424
"command": "superdesign.helloWorld",
25-
"title": "Hello World"
25+
"title": "Hello World",
26+
"category": "Superdesign"
27+
},
28+
{
29+
"command": "superdesign.openDesignPanel",
30+
"title": "Open Design Panel",
31+
"category": "Superdesign"
2632
}
2733
]
2834
},
@@ -41,16 +47,22 @@
4147
"test": "vscode-test"
4248
},
4349
"devDependencies": {
44-
"@types/vscode": "^1.95.0",
4550
"@types/mocha": "^10.0.10",
4651
"@types/node": "20.x",
52+
"@types/vscode": "^1.95.0",
4753
"@typescript-eslint/eslint-plugin": "^8.31.1",
4854
"@typescript-eslint/parser": "^8.31.1",
49-
"eslint": "^9.25.1",
55+
"@vscode/test-cli": "^0.0.10",
56+
"@vscode/test-electron": "^2.5.2",
5057
"esbuild": "^0.25.3",
58+
"eslint": "^9.25.1",
5159
"npm-run-all": "^4.1.5",
52-
"typescript": "^5.8.3",
53-
"@vscode/test-cli": "^0.0.10",
54-
"@vscode/test-electron": "^2.5.2"
60+
"typescript": "^5.8.3"
61+
},
62+
"dependencies": {
63+
"@types/react": "^19.1.8",
64+
"@types/react-dom": "^19.1.6",
65+
"react": "^19.1.0",
66+
"react-dom": "^19.1.0"
5567
}
5668
}

src/extension.ts

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,123 @@ import * as vscode from 'vscode';
55
// This method is called when your extension is activated
66
// Your extension is activated the very first time the command is executed
77
export function activate(context: vscode.ExtensionContext) {
8+
console.log('Superdesign extension is now active!');
89

910
// The command has been defined in the package.json file
1011
// Now provide the implementation of the command with registerCommand
1112
// The commandId parameter must match the command field in package.json
12-
const disposable = vscode.commands.registerCommand('superdesign.helloWorld', () => {
13+
const helloWorldDisposable = vscode.commands.registerCommand('superdesign.helloWorld', () => {
1314
// The code you place here will be executed every time your command is executed
1415
// Display a message box to the user
1516
vscode.window.showInformationMessage('Hello World from superdesign!');
1617
});
1718

18-
context.subscriptions.push(disposable);
19+
// Register new design panel command
20+
const openPanelDisposable = vscode.commands.registerCommand('superdesign.openDesignPanel', () => {
21+
SuperdesignPanel.createOrShow(context.extensionUri);
22+
});
23+
24+
context.subscriptions.push(helloWorldDisposable, openPanelDisposable);
1925
}
2026

27+
class SuperdesignPanel {
28+
public static currentPanel: SuperdesignPanel | undefined;
29+
public static readonly viewType = 'superdesignPanel';
30+
31+
private readonly _panel: vscode.WebviewPanel;
32+
private readonly _extensionUri: vscode.Uri;
33+
private _disposables: vscode.Disposable[] = [];
34+
35+
public static createOrShow(extensionUri: vscode.Uri) {
36+
const column = vscode.window.activeTextEditor?.viewColumn;
37+
38+
if (SuperdesignPanel.currentPanel) {
39+
SuperdesignPanel.currentPanel._panel.reveal(column);
40+
return;
41+
}
42+
43+
const panel = vscode.window.createWebviewPanel(
44+
SuperdesignPanel.viewType,
45+
'Superdesign',
46+
column || vscode.ViewColumn.One,
47+
{
48+
enableScripts: true,
49+
localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'dist')]
50+
}
51+
);
52+
53+
SuperdesignPanel.currentPanel = new SuperdesignPanel(panel, extensionUri);
54+
}
55+
56+
private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
57+
this._panel = panel;
58+
this._extensionUri = extensionUri;
59+
60+
this._update();
61+
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
62+
63+
// Handle messages from the webview
64+
this._panel.webview.onDidReceiveMessage(
65+
message => {
66+
switch (message.command) {
67+
case 'exportDesign':
68+
vscode.window.showInformationMessage('Design system exported!');
69+
break;
70+
}
71+
},
72+
null,
73+
this._disposables
74+
);
75+
}
76+
77+
public dispose() {
78+
SuperdesignPanel.currentPanel = undefined;
79+
this._panel.dispose();
80+
while (this._disposables.length) {
81+
const x = this._disposables.pop();
82+
if (x) {
83+
x.dispose();
84+
}
85+
}
86+
}
87+
88+
private _update() {
89+
const webview = this._panel.webview;
90+
this._panel.webview.html = this._getHtmlForWebview(webview);
91+
}
92+
93+
private _getHtmlForWebview(webview: vscode.Webview) {
94+
const scriptUri = webview.asWebviewUri(
95+
vscode.Uri.joinPath(this._extensionUri, 'dist', 'webview.js')
96+
);
97+
98+
const nonce = getNonce();
99+
100+
return `<!DOCTYPE html>
101+
<html lang="en">
102+
<head>
103+
<meta charset="UTF-8">
104+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}';">
105+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
106+
<title>Superdesign</title>
107+
</head>
108+
<body>
109+
<div id="root"></div>
110+
<script nonce="${nonce}" src="${scriptUri}"></script>
111+
</body>
112+
</html>`;
113+
}
114+
}
115+
116+
function getNonce() {
117+
let text = '';
118+
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
119+
for (let i = 0; i < 32; i++) {
120+
text += possible.charAt(Math.floor(Math.random() * possible.length));
121+
}
122+
return text;
123+
}
124+
125+
21126
// This method is called when your extension is deactivated
22127
export function deactivate() {}

0 commit comments

Comments
 (0)