diff --git a/packages/backend/src/studio.ts b/packages/backend/src/studio.ts
index f7950141b..80f941bf3 100644
--- a/packages/backend/src/studio.ts
+++ b/packages/backend/src/studio.ts
@@ -16,12 +16,11 @@
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
-import { Uri, window, env, version } from '@podman-desktop/api';
+import { env, version } from '@podman-desktop/api';
import { satisfies, minVersion, coerce } from 'semver';
import type {
ExtensionContext,
TelemetryLogger,
- WebviewOptions,
WebviewPanel,
WebviewPanelOnDidChangeViewStateEvent,
} from '@podman-desktop/api';
@@ -32,7 +31,6 @@ import { GitManager } from './managers/gitManager';
import { TaskRegistry } from './registries/TaskRegistry';
import { CatalogManager } from './managers/catalogManager';
import { ModelsManager } from './managers/modelsManager';
-import fs from 'node:fs';
import { ContainerRegistry } from './registries/ContainerRegistry';
import { PodmanConnection } from './managers/podmanConnection';
import { LocalRepositoryRegistry } from './registries/LocalRepositoryRegistry';
@@ -43,6 +41,7 @@ import { CancellationTokenRegistry } from './registries/CancellationTokenRegistr
import { engines } from '../package.json';
import { BuilderManager } from './managers/recipes/BuilderManager';
import { PodManager } from './managers/recipes/PodManager';
+import { initWebview } from './webviewUtils';
export const AI_LAB_COLLECT_GPU_COMMAND = 'ai-lab.gpu.collect';
@@ -95,50 +94,8 @@ export class Studio {
this.telemetry.logUsage('start');
- const extensionUri = this.#extensionContext.extensionUri;
-
- // register webview
- this.#panel = window.createWebviewPanel('studio', 'AI Lab', this.getWebviewOptions(extensionUri));
- this.#extensionContext.subscriptions.push(this.#panel);
-
- // update html
-
- const indexHtmlUri = Uri.joinPath(extensionUri, 'media', 'index.html');
- const indexHtmlPath = indexHtmlUri.fsPath;
-
- let indexHtml = await fs.promises.readFile(indexHtmlPath, 'utf8');
-
- // replace links with webView Uri links
- // in the content replace src with webview.asWebviewUri
- const scriptLink = indexHtml.match(//g);
- if (scriptLink) {
- scriptLink.forEach(link => {
- const src = link.match(/src="(.*?)"/);
- if (src) {
- const webviewSrc = this.#panel?.webview.asWebviewUri(Uri.joinPath(extensionUri, 'media', src[1]));
- if (!webviewSrc) throw new Error('undefined webviewSrc');
- indexHtml = indexHtml.replace(src[1], webviewSrc.toString());
- }
- });
- }
-
- // and now replace for css file as well
- const cssLink = indexHtml.match(//g);
- if (cssLink) {
- cssLink.forEach(link => {
- const href = link.match(/href="(.*?)"/);
- if (href) {
- const webviewHref = this.#panel?.webview.asWebviewUri(Uri.joinPath(extensionUri, 'media', href[1]));
- if (!webviewHref)
- throw new Error('Something went wrong while replacing links with webView Uri links: undefined webviewHref');
- indexHtml = indexHtml.replace(href[1], webviewHref.toString());
- }
- });
- }
-
- console.log('updated indexHtml to', indexHtml);
-
- this.#panel.webview.html = indexHtml;
+ // init webview
+ this.#panel = await initWebview(this.#extensionContext.extensionUri);
// Creating cancellation token registry
const cancellationTokenRegistry = new CancellationTokenRegistry();
@@ -251,14 +208,4 @@ export class Studio {
console.log('stopping AI Lab extension');
this.telemetry?.logUsage('stop');
}
-
- getWebviewOptions(extensionUri: Uri): WebviewOptions {
- return {
- // Enable javascript in the webview
- // enableScripts: true,
-
- // And restrict the webview to only loading content from our extension's `media` directory.
- localResourceRoots: [Uri.joinPath(extensionUri, 'media')],
- };
- }
}
diff --git a/packages/backend/src/webviewUtils.spec.ts b/packages/backend/src/webviewUtils.spec.ts
new file mode 100644
index 000000000..b1e19d0bc
--- /dev/null
+++ b/packages/backend/src/webviewUtils.spec.ts
@@ -0,0 +1,78 @@
+/**********************************************************************
+ * Copyright (C) 2024 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ***********************************************************************/
+
+import { beforeEach, expect, test, vi } from 'vitest';
+import { initWebview } from './webviewUtils';
+import type { Uri } from '@podman-desktop/api';
+import { promises } from 'node:fs';
+
+vi.mock('@podman-desktop/api', async () => {
+ return {
+ Uri: class {
+ static joinPath = () => ({ fsPath: '.' });
+ },
+ window: {
+ createWebviewPanel: () => ({
+ webview: {
+ html: '',
+ onDidReceiveMessage: vi.fn(),
+ postMessage: vi.fn(),
+ asWebviewUri: () => 'dummy-src',
+ },
+ onDidChangeViewState: vi.fn(),
+ }),
+ },
+ };
+});
+
+vi.mock('node:fs', () => ({
+ promises: {
+ readFile: vi.fn(),
+ },
+}));
+
+beforeEach(() => {
+ vi.resetAllMocks();
+});
+
+test('panel should have file content as html', async () => {
+ vi.mocked(promises.readFile).mockImplementation(() => {
+ return Promise.resolve('');
+ });
+
+ const panel = await initWebview({} as unknown as Uri);
+ expect(panel.webview.html).toBe('');
+});
+
+test('script src should be replaced with asWebviewUri result', async () => {
+ vi.mocked(promises.readFile).mockImplementation(() => {
+ return Promise.resolve('');
+ });
+
+ const panel = await initWebview({} as unknown as Uri);
+ expect(panel.webview.html).toBe('');
+});
+
+test('links src should be replaced with asWebviewUri result', async () => {
+ vi.mocked(promises.readFile).mockImplementation(() => {
+ return Promise.resolve('');
+ });
+
+ const panel = await initWebview({} as unknown as Uri);
+ expect(panel.webview.html).toBe('');
+});
diff --git a/packages/backend/src/webviewUtils.ts b/packages/backend/src/webviewUtils.ts
new file mode 100644
index 000000000..2aea9b848
--- /dev/null
+++ b/packages/backend/src/webviewUtils.ts
@@ -0,0 +1,73 @@
+/**********************************************************************
+ * Copyright (C) 2024 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ***********************************************************************/
+
+import { Uri, type WebviewOptions, type WebviewPanel, window } from '@podman-desktop/api';
+import { promises } from 'node:fs';
+
+function getWebviewOptions(extensionUri: Uri): WebviewOptions {
+ return {
+ // Enable javascript in the webview
+ // enableScripts: true,
+
+ // And restrict the webview to only loading content from our extension's `media` directory.
+ localResourceRoots: [Uri.joinPath(extensionUri, 'media')],
+ };
+}
+
+export async function initWebview(extensionUri: Uri): Promise {
+ // register webview
+ const panel = window.createWebviewPanel('studio', 'AI Lab', getWebviewOptions(extensionUri));
+
+ // update html
+ const indexHtmlUri = Uri.joinPath(extensionUri, 'media', 'index.html');
+ const indexHtmlPath = indexHtmlUri.fsPath;
+
+ let indexHtml = await promises.readFile(indexHtmlPath, 'utf8');
+
+ // replace links with webView Uri links
+ // in the content replace src with webview.asWebviewUri
+ const scriptLink = indexHtml.match(//g);
+ if (scriptLink) {
+ scriptLink.forEach(link => {
+ const src = link.match(/src="(.*?)"/);
+ if (src) {
+ const webviewSrc = panel.webview.asWebviewUri(Uri.joinPath(extensionUri, 'media', src[1]));
+ if (!webviewSrc) throw new Error('undefined webviewSrc');
+ indexHtml = indexHtml.replace(src[1], webviewSrc.toString());
+ }
+ });
+ }
+
+ // and now replace for css file as well
+ const cssLink = indexHtml.match(//g);
+ if (cssLink) {
+ cssLink.forEach(link => {
+ const href = link.match(/href="(.*?)"/);
+ if (href) {
+ const webviewHref = panel.webview.asWebviewUri(Uri.joinPath(extensionUri, 'media', href[1]));
+ if (!webviewHref)
+ throw new Error('Something went wrong while replacing links with webView Uri links: undefined webviewHref');
+ indexHtml = indexHtml.replace(href[1], webviewHref.toString());
+ }
+ });
+ }
+
+ panel.webview.html = indexHtml;
+
+ return panel;
+}