Skip to content

Commit

Permalink
chore: moving static webview logic to dedicated util file (#1167)
Browse files Browse the repository at this point in the history
* chore: moving static webview logic to dedicated util file

Signed-off-by: axel7083 <[email protected]>

* fix: linter

Signed-off-by: axel7083 <[email protected]>

---------

Signed-off-by: axel7083 <[email protected]>
  • Loading branch information
axel7083 authored Jun 6, 2024
1 parent 4b2e042 commit bf5ff69
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 57 deletions.
61 changes: 4 additions & 57 deletions packages/backend/src/studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand All @@ -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';

Expand Down Expand Up @@ -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 <script type="module" crossorigin src="./index-RKnfBG18.js"></script> replace src with webview.asWebviewUri
const scriptLink = indexHtml.match(/<script.*?src="(.*?)".*?>/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(/<link.*?href="(.*?)".*?>/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();
Expand Down Expand Up @@ -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')],
};
}
}
78 changes: 78 additions & 0 deletions packages/backend/src/webviewUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -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('<html></html>');
});

const panel = await initWebview({} as unknown as Uri);
expect(panel.webview.html).toBe('<html></html>');
});

test('script src should be replaced with asWebviewUri result', async () => {
vi.mocked(promises.readFile).mockImplementation(() => {
return Promise.resolve('<script type="module" crossorigin src="./index-RKnfBG18.js"></script>');
});

const panel = await initWebview({} as unknown as Uri);
expect(panel.webview.html).toBe('<script type="module" crossorigin src="dummy-src"></script>');
});

test('links src should be replaced with asWebviewUri result', async () => {
vi.mocked(promises.readFile).mockImplementation(() => {
return Promise.resolve('<link rel="stylesheet" href="./styles.css">');
});

const panel = await initWebview({} as unknown as Uri);
expect(panel.webview.html).toBe('<link rel="stylesheet" href="dummy-src">');
});
73 changes: 73 additions & 0 deletions packages/backend/src/webviewUtils.ts
Original file line number Diff line number Diff line change
@@ -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<WebviewPanel> {
// 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 <script type="module" crossorigin src="./index-RKnfBG18.js"></script> replace src with webview.asWebviewUri
const scriptLink = indexHtml.match(/<script.*?src="(.*?)".*?>/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(/<link.*?href="(.*?)".*?>/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;
}

0 comments on commit bf5ff69

Please sign in to comment.