Skip to content
Merged
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
42 changes: 34 additions & 8 deletions src/backend/configPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export class BackendConfigPanel implements vscode.Disposable {

constructor(private readonly extensionUri: vscode.Uri, private readonly callbacks: BackendConfigPanelCallbacks) {}

public isDisposed(): boolean {
return this.disposed;
}

public async show(): Promise<void> {
const state = await this.callbacks.getState();
if (!this.panel) {
Expand Down Expand Up @@ -454,10 +458,17 @@ export class BackendConfigPanel implements vscode.Disposable {
</main>
</div>
<script type="module" nonce="${nonce}">
const { provideVSCodeDesignSystem, vsCodeButton, vsCodeBadge } = await import('${toolkitUri}');
provideVSCodeDesignSystem().register(vsCodeButton(), vsCodeBadge());
// Register toolkit components before main script runs
try {
const { provideVSCodeDesignSystem, vsCodeButton, vsCodeBadge } = await import('${toolkitUri}');
provideVSCodeDesignSystem().register(vsCodeButton(), vsCodeBadge());
} catch (error) {
console.warn('Failed to load VS Code Webview UI Toolkit:', error);
}
// Signal that toolkit registration is complete (or has failed)
window.__toolkitReady = true;
</script>
<script nonce="${nonce}">
<script nonce="${nonce}">
const vscodeApi = acquireVsCodeApi();
const initial = ${initialState};
let currentState = initial;
Expand Down Expand Up @@ -795,11 +806,26 @@ export class BackendConfigPanel implements vscode.Disposable {
updateConnectionAvailability();
window.addEventListener('offline', updateConnectionAvailability);
window.addEventListener('online', updateConnectionAvailability);
setFieldValues(initial);
setErrors(initial.errors || {});
bindNav();
bindActions();
updateValidity();

function init() {
setFieldValues(initial);
setErrors(initial.errors || {});
bindNav();
bindActions();
updateValidity();
}

// Wait for toolkit registration to complete before initializing
function waitForToolkit() {
if (window.__toolkitReady) {
init();
} else {
// Poll for toolkit readiness (handles async module loading)
setTimeout(waitForToolkit, 10);
}
}

waitForToolkit();
</script>
</body>
</html>`;
Expand Down
3 changes: 2 additions & 1 deletion src/backend/facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,8 @@ export class BackendFacade {
vscode.window.showErrorMessage('Extension context is unavailable; cannot open backend configuration.');
return;
}
if (!this.configPanel) {
// Create a new panel if we don't have one or if it was disposed
if (!this.configPanel || this.configPanel.isDisposed()) {
this.configPanel = new BackendConfigPanel(this.deps.context.extensionUri, {
getState: () => this.getConfigPanelState(),
onSave: async (draft) => this.saveDraft(draft),
Expand Down
8 changes: 7 additions & 1 deletion src/test-node/backend-configPanel-webview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ suite('Backend Config Panel Webview Integration Tests', () => {
return renderBackendConfigHtml(webview as any, initialState);
}

setup(() => {
setup(async () => {
const html = createPanelHtml();
dom = new JSDOM(html, {
runScripts: 'dangerously',
Expand All @@ -60,10 +60,16 @@ suite('Backend Config Panel Webview Integration Tests', () => {
getState: () => ({}),
setState: (state: any) => {}
});

// Set toolkit ready flag immediately (JSDOM doesn't run module scripts properly)
(window as any).__toolkitReady = true;
}
});
window = dom.window as unknown as Window;
document = window.document;

// Wait briefly for init() to run
await new Promise(resolve => setTimeout(resolve, 50));
});

teardown(() => {
Expand Down