From 615b754c74df8172d1fcf877e706281af335bf31 Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:53:48 +0100 Subject: [PATCH 1/9] fix: imports and typing Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- packages/backend/src/{ => managers}/playground.ts | 2 +- packages/backend/src/studio-api-impl.spec.ts | 2 +- packages/backend/src/studio-api-impl.ts | 2 +- packages/backend/src/studio.ts | 4 ++-- packages/frontend/src/pages/ModelPlayground.svelte | 6 +++--- packages/frontend/src/utils/client.ts | 2 +- packages/shared/src/{ => messages}/MessageProxy.spec.ts | 1 - packages/shared/src/{ => messages}/MessageProxy.ts | 8 ++++---- 8 files changed, 13 insertions(+), 14 deletions(-) rename packages/backend/src/{ => managers}/playground.ts (99%) rename packages/shared/src/{ => messages}/MessageProxy.spec.ts (96%) rename packages/shared/src/{ => messages}/MessageProxy.ts (95%) diff --git a/packages/backend/src/playground.ts b/packages/backend/src/managers/playground.ts similarity index 99% rename from packages/backend/src/playground.ts rename to packages/backend/src/managers/playground.ts index 214c9349d..c1ccfbe47 100644 --- a/packages/backend/src/playground.ts +++ b/packages/backend/src/managers/playground.ts @@ -10,7 +10,7 @@ import type { ModelResponse } from '@shared/src/models/IModelResponse'; import path from 'node:path'; import * as http from 'node:http'; -import { getFreePort } from './utils/ports'; +import { getFreePort } from '../utils/ports'; import type { QueryState } from '@shared/src/models/IPlaygroundQueryState'; import { MSG_NEW_PLAYGROUND_QUERIES_STATE } from '@shared/Messages'; diff --git a/packages/backend/src/studio-api-impl.spec.ts b/packages/backend/src/studio-api-impl.spec.ts index 408ad551b..b50c77b7c 100644 --- a/packages/backend/src/studio-api-impl.spec.ts +++ b/packages/backend/src/studio-api-impl.spec.ts @@ -24,7 +24,7 @@ import userContent from './ai-user-test.json'; import type { ApplicationManager } from './managers/applicationManager'; import type { RecipeStatusRegistry } from './registries/RecipeStatusRegistry'; import { StudioApiImpl } from './studio-api-impl'; -import type { PlayGroundManager } from './playground'; +import type { PlayGroundManager } from './managers/playground'; import type { TaskRegistry } from './registries/TaskRegistry'; import type { Webview } from '@podman-desktop/api'; diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index 58e29f35e..13d4fbc6d 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -7,7 +7,7 @@ import type { RecipeStatus } from '@shared/src/models/IRecipeStatus'; import type { ModelInfo } from '@shared/src/models/IModelInfo'; import type { TaskRegistry } from './registries/TaskRegistry'; import type { Task } from '@shared/src/models/ITask'; -import type { PlayGroundManager } from './playground'; +import type { PlayGroundManager } from './managers/playground'; import * as podmanDesktopApi from '@podman-desktop/api'; import type { QueryState } from '@shared/src/models/IPlaygroundQueryState'; import type { Catalog } from '@shared/src/models/ICatalog'; diff --git a/packages/backend/src/studio.ts b/packages/backend/src/studio.ts index 4136d8cb0..d502ff405 100644 --- a/packages/backend/src/studio.ts +++ b/packages/backend/src/studio.ts @@ -18,7 +18,7 @@ import type { ExtensionContext, WebviewOptions, WebviewPanel } from '@podman-desktop/api'; import { Uri, window } from '@podman-desktop/api'; -import { RpcExtension } from '@shared/src/MessageProxy'; +import { RpcExtension } from '@shared/src/messages/MessageProxy'; import { StudioApiImpl } from './studio-api-impl'; import { ApplicationManager } from './managers/applicationManager'; import { GitManager } from './managers/gitManager'; @@ -26,7 +26,7 @@ import { RecipeStatusRegistry } from './registries/RecipeStatusRegistry'; import * as fs from 'node:fs'; import { TaskRegistry } from './registries/TaskRegistry'; -import { PlayGroundManager } from './playground'; +import { PlayGroundManager } from './managers/playground'; export class Studio { readonly #extensionContext: ExtensionContext; diff --git a/packages/frontend/src/pages/ModelPlayground.svelte b/packages/frontend/src/pages/ModelPlayground.svelte index ab4811b62..62907d27d 100644 --- a/packages/frontend/src/pages/ModelPlayground.svelte +++ b/packages/frontend/src/pages/ModelPlayground.svelte @@ -60,7 +60,7 @@ result = undefined; // do not display anything before we get a response from askPlayground // (we can receive a new queryState before the new QueryId) - queryId = -1; + queryId = -1; queryId = await studioClient.askPlayground(model.id, prompt); } @@ -73,8 +73,8 @@ rows="4" class="w-full p-2 outline-none text-sm bg-charcoal-800 rounded-sm text-gray-700 placeholder-gray-700" placeholder="Type your prompt here"> - -
+ +
diff --git a/packages/frontend/src/utils/client.ts b/packages/frontend/src/utils/client.ts index 71c97a9c9..93efd1cea 100644 --- a/packages/frontend/src/utils/client.ts +++ b/packages/frontend/src/utils/client.ts @@ -1,5 +1,5 @@ import type { StudioAPI } from '@shared/src/StudioAPI'; -import { RpcBrowser } from '@shared/src/MessageProxy'; +import { RpcBrowser } from '@shared/src/messages/MessageProxy'; export const RECENT_CATEGORY_ID = 'recent-category'; const podmanDesktopApi = acquirePodmanDesktopApi(); diff --git a/packages/shared/src/MessageProxy.spec.ts b/packages/shared/src/messages/MessageProxy.spec.ts similarity index 96% rename from packages/shared/src/MessageProxy.spec.ts rename to packages/shared/src/messages/MessageProxy.spec.ts index 595ce0fa6..656b9b9e6 100644 --- a/packages/shared/src/MessageProxy.spec.ts +++ b/packages/shared/src/messages/MessageProxy.spec.ts @@ -1,6 +1,5 @@ import { test, expect, beforeAll } from 'vitest'; import { RpcBrowser, RpcExtension } from './MessageProxy'; -import type { PodmanDesktopApi } from '../../../types/podman-desktop-api'; import type { Webview } from '@podman-desktop/api'; let webview: Webview; diff --git a/packages/shared/src/MessageProxy.ts b/packages/shared/src/messages/MessageProxy.ts similarity index 95% rename from packages/shared/src/MessageProxy.ts rename to packages/shared/src/messages/MessageProxy.ts index 6e5e777b6..e98e83c56 100644 --- a/packages/shared/src/MessageProxy.ts +++ b/packages/shared/src/messages/MessageProxy.ts @@ -17,7 +17,6 @@ ***********************************************************************/ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { PodmanDesktopApi } from '../../../types/podman-desktop-api'; import type { Webview } from '@podman-desktop/api'; export interface IMessage { @@ -40,6 +39,8 @@ export interface ISubscribedMessage { body: any; } +type UnaryRPC = (...args: unknown[]) => Promise; + export function isMessageRequest(content: unknown): content is IMessageRequest { return ( content !== undefined && content !== null && typeof content === 'object' && 'id' in content && 'channel' in content @@ -90,7 +91,7 @@ export class RpcExtension { }); } - registerInstance(classType: { new (...args: any[]): T }, instance: T) { + registerInstance>(classType: { new (...args: any[]): T }, instance: T) { const methodNames = Object.getOwnPropertyNames(classType.prototype).filter( name => name !== 'constructor' && typeof instance[name as keyof T] === 'function', ); @@ -156,8 +157,7 @@ export class RpcBrowser { getProxy(): T { // eslint-disable-next-line @typescript-eslint/no-this-alias const thisRef = this; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const proxyHandler: ProxyHandler = { + const proxyHandler: ProxyHandler = { get(target, prop, receiver) { if (typeof prop === 'string') { return (...args: unknown[]) => { From 23e4a4977f9d6d35bf2d04430d01355ea830160f Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Fri, 19 Jan 2024 11:48:03 +0100 Subject: [PATCH 2/9] fix: moving utility methods out of the Studio API implementation Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- .../backend/src/managers/catalogManager.ts | 54 ++++++++++++++++++ packages/backend/src/studio-api-impl.ts | 57 ++++++++++++------- packages/backend/src/studio.ts | 10 +++- 3 files changed, 100 insertions(+), 21 deletions(-) create mode 100644 packages/backend/src/managers/catalogManager.ts diff --git a/packages/backend/src/managers/catalogManager.ts b/packages/backend/src/managers/catalogManager.ts new file mode 100644 index 000000000..ef8bf68ab --- /dev/null +++ b/packages/backend/src/managers/catalogManager.ts @@ -0,0 +1,54 @@ +import type { Catalog } from '@shared/src/models/ICatalog'; +import path from 'node:path'; +import fs from 'node:fs'; +import defaultCatalog from '../ai.json'; +import { Category } from '@shared/src/models/ICategory'; +import { Recipe } from '@shared/src/models/IRecipe'; +import { ModelInfo } from '@shared/src/models/IModelInfo'; + +export class CatalogManager { + private catalog: Catalog; + + constructor(private appUserDirectory: string) { + // We start with an empty catalog, for the methods to work before the catalog is loaded + this.catalog = { + categories: [], + models: [], + recipes: [], + }; + } + + public getCategories(): Category[] { + return this.catalog.categories; + } + + public getModels(): ModelInfo[] { + return this.catalog.models; + } + public getRecipes(): Recipe[] { + return this.catalog.recipes; + } + + + async loadCatalog() { + const catalogPath = path.resolve(this.appUserDirectory, 'catalog.json'); + try { + if (!fs.existsSync(catalogPath)) { + this.setCatalog(defaultCatalog); + return; + } + // TODO(feloy): watch catalog file and update catalog with new content + const data = await fs.promises.readFile(catalogPath, 'utf-8'); + const cat = JSON.parse(data) as Catalog; + this.setCatalog(cat); + } catch (err: unknown) { + console.error('unable to read catalog file, reverting to default catalog', err); + this.setCatalog(defaultCatalog); + } + } + + setCatalog(newCatalog: Catalog) { + // TODO(feloy): send message to frontend with new catalog + this.catalog = newCatalog; + } +} diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index 13d4fbc6d..4dc50f6f4 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -1,4 +1,5 @@ import type { StudioAPI } from '@shared/src/StudioAPI'; +import type { Category } from '@shared/src/models/ICategory'; import type { Recipe } from '@shared/src/models/IRecipe'; import defaultCatalog from './ai.json'; import type { ApplicationManager } from './managers/applicationManager'; @@ -15,26 +16,20 @@ import { MSG_NEW_CATALOG_STATE } from '@shared/Messages'; import * as path from 'node:path'; import * as fs from 'node:fs'; +import { CatalogManager } from './managers/catalogManager'; export const RECENT_CATEGORY_ID = 'recent-category'; export class StudioApiImpl implements StudioAPI { - private catalog: Catalog; constructor( private applicationManager: ApplicationManager, private recipeStatusRegistry: RecipeStatusRegistry, private taskRegistry: TaskRegistry, private playgroundManager: PlayGroundManager, + private catalogManager: CatalogManager, private webview: podmanDesktopApi.Webview, - ) { - // We start with an empty catalog, for the methods to work before the catalog is loaded - this.catalog = { - categories: [], - models: [], - recipes: [], - }; - } + ) {} async loadCatalog() { const catalogPath = path.resolve(this.applicationManager.appUserDirectory, 'catalog.json'); @@ -108,32 +103,53 @@ export class StudioApiImpl implements StudioAPI { return 'pong'; } - async getCatalog(): Promise { - return this.catalog; + async getRecentRecipes(): Promise { + return []; // no recent implementation for now + } + + async getCategories(): Promise { + return this.catalogManager.getCategories(); + } + + async getRecipesByCategory(categoryId: string): Promise { + if (categoryId === RECENT_CATEGORY_ID) return this.getRecentRecipes(); + + // TODO: move logic to catalog manager + return this.catalogManager.getRecipes().filter(recipe => recipe.categories.includes(categoryId)); } - getRecipeById(recipeId: string): Recipe { - const recipe = (this.catalog.recipes as Recipe[]).find(recipe => recipe.id === recipeId); + async getRecipeById(recipeId: string): Promise { + // TODO: move logic to catalog manager + const recipe = this.catalogManager.getRecipes().find(recipe => recipe.id === recipeId); if (recipe) return recipe; throw new Error('Not found'); } - getModelById(modelId: string): ModelInfo { - const model = this.catalog.models.find(m => modelId === m.id); + async getModelById(modelId: string): Promise { + // TODO: move logic to catalog manager + const model = this.catalogManager.getModels().find(m => modelId === m.id); if (!model) { throw new Error(`No model found having id ${modelId}`); } return model; } + async getModelsByIds(ids: string[]): Promise { + // TODO: move logic to catalog manager + return this.catalogManager.getModels().filter(m => ids.includes(m.id)) ?? []; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async searchRecipes(_query: string): Promise { + return []; // todo: not implemented + } + async pullApplication(recipeId: string): Promise { - console.log('StudioApiImpl pullApplication', recipeId); - const recipe: Recipe = this.getRecipeById(recipeId); - console.log('StudioApiImpl recipe', recipe); + const recipe: Recipe = await this.getRecipeById(recipeId); // the user should have selected one model, we use the first one for the moment const modelId = recipe.models[0]; - const model = this.getModelById(modelId); + const model = await this.getModelById(modelId); // Do not wait for the pull application, run it separately this.applicationManager.pullApplication(recipe, model).catch((error: unknown) => console.warn(error)); @@ -142,9 +158,10 @@ export class StudioApiImpl implements StudioAPI { } async getLocalModels(): Promise { + // TODO: move logic to catalog manager const local = this.applicationManager.getLocalModels(); const localIds = local.map(l => l.id); - return this.catalog.models.filter(m => localIds.includes(m.id)); + return this.catalogManager.getModels().filter(m => localIds.includes(m.id)); } async getTasksByLabel(label: string): Promise { diff --git a/packages/backend/src/studio.ts b/packages/backend/src/studio.ts index d502ff405..f80118b4f 100644 --- a/packages/backend/src/studio.ts +++ b/packages/backend/src/studio.ts @@ -27,6 +27,7 @@ import { RecipeStatusRegistry } from './registries/RecipeStatusRegistry'; import * as fs from 'node:fs'; import { TaskRegistry } from './registries/TaskRegistry'; import { PlayGroundManager } from './managers/playground'; +import { CatalogManager } from './managers/catalogManager'; export class Studio { readonly #extensionContext: ExtensionContext; @@ -36,6 +37,7 @@ export class Studio { rpcExtension: RpcExtension; studioApi: StudioApiImpl; playgroundManager: PlayGroundManager; + catalogManager: CatalogManager; constructor(readonly extensionContext: ExtensionContext) { this.#extensionContext = extensionContext; @@ -93,14 +95,20 @@ export class Studio { const recipeStatusRegistry = new RecipeStatusRegistry(taskRegistry); const applicationManager = new ApplicationManager(gitManager, recipeStatusRegistry, this.#extensionContext); this.playgroundManager = new PlayGroundManager(this.#panel.webview); + this.catalogManager = new CatalogManager(applicationManager.appUserDirectory); + + // Creating StudioApiImpl this.studioApi = new StudioApiImpl( applicationManager, recipeStatusRegistry, taskRegistry, this.playgroundManager, + this.catalogManager, this.#panel.webview, ); - await this.studioApi.loadCatalog(); + + await this.catalogManager.loadCatalog(); + // Register the instance this.rpcExtension.registerInstance(StudioApiImpl, this.studioApi); } From 9117ff8c47098e0eb1d2f07e59c6bfe04420ea34 Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Fri, 19 Jan 2024 11:48:59 +0100 Subject: [PATCH 3/9] fix: prettier Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- packages/backend/src/managers/catalogManager.ts | 1 - packages/backend/src/studio-api-impl.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/backend/src/managers/catalogManager.ts b/packages/backend/src/managers/catalogManager.ts index ef8bf68ab..c8db5a1e0 100644 --- a/packages/backend/src/managers/catalogManager.ts +++ b/packages/backend/src/managers/catalogManager.ts @@ -29,7 +29,6 @@ export class CatalogManager { return this.catalog.recipes; } - async loadCatalog() { const catalogPath = path.resolve(this.appUserDirectory, 'catalog.json'); try { diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index 4dc50f6f4..1f8dd368e 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -21,7 +21,6 @@ import { CatalogManager } from './managers/catalogManager'; export const RECENT_CATEGORY_ID = 'recent-category'; export class StudioApiImpl implements StudioAPI { - constructor( private applicationManager: ApplicationManager, private recipeStatusRegistry: RecipeStatusRegistry, From 462bc5c8aac51a6de59d74c8f52428ee5f426a36 Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Fri, 19 Jan 2024 11:51:24 +0100 Subject: [PATCH 4/9] fix: linter Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- packages/backend/src/managers/catalogManager.ts | 6 +++--- packages/backend/src/studio-api-impl.ts | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/managers/catalogManager.ts b/packages/backend/src/managers/catalogManager.ts index c8db5a1e0..20e29cc04 100644 --- a/packages/backend/src/managers/catalogManager.ts +++ b/packages/backend/src/managers/catalogManager.ts @@ -2,9 +2,9 @@ import type { Catalog } from '@shared/src/models/ICatalog'; import path from 'node:path'; import fs from 'node:fs'; import defaultCatalog from '../ai.json'; -import { Category } from '@shared/src/models/ICategory'; -import { Recipe } from '@shared/src/models/IRecipe'; -import { ModelInfo } from '@shared/src/models/IModelInfo'; +import type { Category } from '@shared/src/models/ICategory'; +import type { Recipe } from '@shared/src/models/IRecipe'; +import type { ModelInfo } from '@shared/src/models/IModelInfo'; export class CatalogManager { private catalog: Catalog; diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index 1f8dd368e..57b1e7b3a 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -1,7 +1,6 @@ import type { StudioAPI } from '@shared/src/StudioAPI'; import type { Category } from '@shared/src/models/ICategory'; import type { Recipe } from '@shared/src/models/IRecipe'; -import defaultCatalog from './ai.json'; import type { ApplicationManager } from './managers/applicationManager'; import type { RecipeStatusRegistry } from './registries/RecipeStatusRegistry'; import type { RecipeStatus } from '@shared/src/models/IRecipeStatus'; @@ -15,8 +14,7 @@ import type { Catalog } from '@shared/src/models/ICatalog'; import { MSG_NEW_CATALOG_STATE } from '@shared/Messages'; import * as path from 'node:path'; -import * as fs from 'node:fs'; -import { CatalogManager } from './managers/catalogManager'; +import type { CatalogManager } from './managers/catalogManager'; export const RECENT_CATEGORY_ID = 'recent-category'; From 558aabae8f7bd20156f1ee5263752baed3eeff38 Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:57:14 +0100 Subject: [PATCH 5/9] fix: tests Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- .../backend/src/managers/catalogManager.ts | 6 ++-- packages/backend/src/studio-api-impl.spec.ts | 31 ++++++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/managers/catalogManager.ts b/packages/backend/src/managers/catalogManager.ts index 20e29cc04..b92aa85a8 100644 --- a/packages/backend/src/managers/catalogManager.ts +++ b/packages/backend/src/managers/catalogManager.ts @@ -1,6 +1,6 @@ import type { Catalog } from '@shared/src/models/ICatalog'; import path from 'node:path'; -import fs from 'node:fs'; +import { existsSync, promises } from 'node:fs'; import defaultCatalog from '../ai.json'; import type { Category } from '@shared/src/models/ICategory'; import type { Recipe } from '@shared/src/models/IRecipe'; @@ -32,12 +32,12 @@ export class CatalogManager { async loadCatalog() { const catalogPath = path.resolve(this.appUserDirectory, 'catalog.json'); try { - if (!fs.existsSync(catalogPath)) { + if (!existsSync(catalogPath)) { this.setCatalog(defaultCatalog); return; } // TODO(feloy): watch catalog file and update catalog with new content - const data = await fs.promises.readFile(catalogPath, 'utf-8'); + const data = await promises.readFile(catalogPath, 'utf-8'); const cat = JSON.parse(data) as Catalog; this.setCatalog(cat); } catch (err: unknown) { diff --git a/packages/backend/src/studio-api-impl.spec.ts b/packages/backend/src/studio-api-impl.spec.ts index b50c77b7c..fefb9a2d4 100644 --- a/packages/backend/src/studio-api-impl.spec.ts +++ b/packages/backend/src/studio-api-impl.spec.ts @@ -36,16 +36,30 @@ vi.mock('./ai.json', () => { }; }); +vi.mock('node:fs', () => { + return { + existsSync: vi.fn(), + promises: { + readFile: vi.fn(), + } + }; +}); + + let studioApiImpl: StudioApiImpl; +let catalogManager; beforeEach(async () => { + const appUserDirectory = '.'; + catalogManager = new CatalogManager(appUserDirectory); studioApiImpl = new StudioApiImpl( { - appUserDirectory: '.', + appUserDirectory, } as unknown as ApplicationManager, {} as unknown as RecipeStatusRegistry, {} as unknown as TaskRegistry, {} as unknown as PlayGroundManager, + catalogManager, { postMessage: vi.fn(), } as unknown as Webview, @@ -57,11 +71,11 @@ beforeEach(async () => { describe('invalid user catalog', () => { beforeEach(async () => { vi.spyOn(fs.promises, 'readFile').mockResolvedValue('invalid json'); - await studioApiImpl.loadCatalog(); + await catalogManager.loadCatalog(); }); - test('expect correct model is returned with valid id', () => { - const model = studioApiImpl.getModelById('llama-2-7b-chat.Q5_K_S'); + test('expect correct model is returned with valid id', async () => { + const model = await studioApiImpl.getModelById('llama-2-7b-chat.Q5_K_S'); expect(model).toBeDefined(); expect(model.name).toEqual('Llama-2-7B-Chat-GGUF'); expect(model.registry).toEqual('Hugging Face'); @@ -77,8 +91,8 @@ describe('invalid user catalog', () => { test('expect correct model is returned from default catalog with valid id when no user catalog exists', async () => { vi.spyOn(fs, 'existsSync').mockReturnValue(false); - await studioApiImpl.loadCatalog(); - const model = studioApiImpl.getModelById('llama-2-7b-chat.Q5_K_S'); + await catalogManager.loadCatalog(); + const model = await studioApiImpl.getModelById('llama-2-7b-chat.Q5_K_S'); expect(model).toBeDefined(); expect(model.name).toEqual('Llama-2-7B-Chat-GGUF'); expect(model.registry).toEqual('Hugging Face'); @@ -90,8 +104,9 @@ test('expect correct model is returned from default catalog with valid id when n test('expect correct model is returned with valid id when the user catalog is valid', async () => { vi.spyOn(fs, 'existsSync').mockReturnValue(true); vi.spyOn(fs.promises, 'readFile').mockResolvedValue(JSON.stringify(userContent)); - await studioApiImpl.loadCatalog(); - const model = studioApiImpl.getModelById('model1'); + + await catalogManager.loadCatalog(); + const model = await studioApiImpl.getModelById('model1'); expect(model).toBeDefined(); expect(model.name).toEqual('Model 1'); expect(model.registry).toEqual('Hugging Face'); From 1d715be762860861bf10f4f01db33cd36526342b Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:31:38 +0100 Subject: [PATCH 6/9] fix: rebase + prettier + lint Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- .../backend/src/managers/catalogManager.ts | 72 +++++++++++++++---- packages/backend/src/studio-api-impl.spec.ts | 29 ++++++-- packages/backend/src/studio-api-impl.ts | 72 ++----------------- packages/backend/src/studio.ts | 7 +- 4 files changed, 94 insertions(+), 86 deletions(-) diff --git a/packages/backend/src/managers/catalogManager.ts b/packages/backend/src/managers/catalogManager.ts index b92aa85a8..83037692f 100644 --- a/packages/backend/src/managers/catalogManager.ts +++ b/packages/backend/src/managers/catalogManager.ts @@ -5,11 +5,17 @@ import defaultCatalog from '../ai.json'; import type { Category } from '@shared/src/models/ICategory'; import type { Recipe } from '@shared/src/models/IRecipe'; import type { ModelInfo } from '@shared/src/models/IModelInfo'; +import { MSG_NEW_CATALOG_STATE } from '@shared/Messages'; +import { fs } from '@podman-desktop/api'; +import type { Webview } from '@podman-desktop/api'; export class CatalogManager { private catalog: Catalog; - constructor(private appUserDirectory: string) { + constructor( + private appUserDirectory: string, + private webview: Webview, + ) { // We start with an empty catalog, for the methods to work before the catalog is loaded this.catalog = { categories: [], @@ -18,6 +24,10 @@ export class CatalogManager { }; } + public getCatalog(): Catalog { + return this.catalog; + } + public getCategories(): Category[] { return this.catalog.categories; } @@ -31,23 +41,61 @@ export class CatalogManager { async loadCatalog() { const catalogPath = path.resolve(this.appUserDirectory, 'catalog.json'); + if (!existsSync(catalogPath)) { + return this.setCatalog(defaultCatalog); + } + try { - if (!existsSync(catalogPath)) { - this.setCatalog(defaultCatalog); - return; - } - // TODO(feloy): watch catalog file and update catalog with new content - const data = await promises.readFile(catalogPath, 'utf-8'); - const cat = JSON.parse(data) as Catalog; - this.setCatalog(cat); + this.watchCatalogFile(catalogPath); // do not await, we want to do this async + } catch (err: unknown) { + console.error('unable to watch catalog file, changes to the catalog file won\'t be reflected to the UI', err); + } + + try { + const cat = await this.readAndAnalyzeCatalog(catalogPath); + return this.setCatalog(cat); } catch (err: unknown) { console.error('unable to read catalog file, reverting to default catalog', err); - this.setCatalog(defaultCatalog); } + // If something went wrong we load the default catalog + return this.setCatalog(defaultCatalog); + } + + watchCatalogFile(path: string) { + const watcher = fs.createFileSystemWatcher(path); + watcher.onDidCreate(async () => { + try { + const cat = await this.readAndAnalyzeCatalog(path); + await this.setCatalog(cat); + } catch (err: unknown) { + console.error('unable to read created catalog file, continue using default catalog', err); + } + }); + watcher.onDidDelete(async () => { + console.log('user catalog file deleted, reverting to default catalog'); + await this.setCatalog(defaultCatalog); + }); + watcher.onDidChange(async () => { + try { + const cat = await this.readAndAnalyzeCatalog(path); + await this.setCatalog(cat); + } catch (err: unknown) { + console.error('unable to read modified catalog file, reverting to default catalog', err); + } + }); + } + + async readAndAnalyzeCatalog(path: string): Promise { + const data = await promises.readFile(path, 'utf-8'); + return JSON.parse(data) as Catalog; + // TODO(feloy): check version, ... } - setCatalog(newCatalog: Catalog) { - // TODO(feloy): send message to frontend with new catalog + async setCatalog(newCatalog: Catalog) { this.catalog = newCatalog; + await this.webview.postMessage({ + id: MSG_NEW_CATALOG_STATE, + body: this.catalog, + }); } } diff --git a/packages/backend/src/studio-api-impl.spec.ts b/packages/backend/src/studio-api-impl.spec.ts index fefb9a2d4..71c31bbd6 100644 --- a/packages/backend/src/studio-api-impl.spec.ts +++ b/packages/backend/src/studio-api-impl.spec.ts @@ -29,6 +29,7 @@ import type { TaskRegistry } from './registries/TaskRegistry'; import type { Webview } from '@podman-desktop/api'; import * as fs from 'node:fs'; +import { CatalogManager } from './managers/catalogManager'; vi.mock('./ai.json', () => { return { @@ -41,17 +42,34 @@ vi.mock('node:fs', () => { existsSync: vi.fn(), promises: { readFile: vi.fn(), - } + }, }; }); +vi.mock('@podman-desktop/api', () => { + return { + fs: { + createFileSystemWatcher: () => ({ + onDidCreate: vi.fn(), + onDidDelete: vi.fn(), + onDidChange: vi.fn(), + }), + }, + }; +}); let studioApiImpl: StudioApiImpl; let catalogManager; beforeEach(async () => { const appUserDirectory = '.'; - catalogManager = new CatalogManager(appUserDirectory); + + // Creating CatalogManager + catalogManager = new CatalogManager(appUserDirectory, { + postMessage: vi.fn(), + } as unknown as Webview); + + // Creating StudioApiImpl studioApiImpl = new StudioApiImpl( { appUserDirectory, @@ -60,9 +78,6 @@ beforeEach(async () => { {} as unknown as TaskRegistry, {} as unknown as PlayGroundManager, catalogManager, - { - postMessage: vi.fn(), - } as unknown as Webview, ); vi.resetAllMocks(); vi.mock('node:fs'); @@ -84,8 +99,8 @@ describe('invalid user catalog', () => { ); }); - test('expect error if id does not correspond to any model', () => { - expect(() => studioApiImpl.getModelById('unknown')).toThrowError('No model found having id unknown'); + test('expect error if id does not correspond to any model', async () => { + await expect(() => studioApiImpl.getModelById('unknown')).rejects.toThrowError('No model found having id unknown'); }); }); diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index 57b1e7b3a..e8b1b0be1 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -10,11 +10,10 @@ import type { Task } from '@shared/src/models/ITask'; import type { PlayGroundManager } from './managers/playground'; import * as podmanDesktopApi from '@podman-desktop/api'; import type { QueryState } from '@shared/src/models/IPlaygroundQueryState'; -import type { Catalog } from '@shared/src/models/ICatalog'; -import { MSG_NEW_CATALOG_STATE } from '@shared/Messages'; import * as path from 'node:path'; import type { CatalogManager } from './managers/catalogManager'; +import type { Catalog } from '@shared/src/models/ICatalog'; export const RECENT_CATEGORY_ID = 'recent-category'; @@ -25,67 +24,10 @@ export class StudioApiImpl implements StudioAPI { private taskRegistry: TaskRegistry, private playgroundManager: PlayGroundManager, private catalogManager: CatalogManager, - private webview: podmanDesktopApi.Webview, ) {} - async loadCatalog() { - const catalogPath = path.resolve(this.applicationManager.appUserDirectory, 'catalog.json'); - - try { - this.watchCatalogFile(catalogPath); // do not await, we want to do this async - } catch (err: unknown) { - console.error("unable to watch catalog file, changes to the catalog file won't be reflected to the UI", err); - } - - try { - if (!fs.existsSync(catalogPath)) { - await this.setCatalog(defaultCatalog); - return; - } - const cat = await this.readAndAnalyzeCatalog(catalogPath); - await this.setCatalog(cat); - } catch (err: unknown) { - console.error('unable to read catalog file, reverting to default catalog', err); - await this.setCatalog(defaultCatalog); - } - } - - watchCatalogFile(path: string) { - const watcher = podmanDesktopApi.fs.createFileSystemWatcher(path); - watcher.onDidCreate(async () => { - try { - const cat = await this.readAndAnalyzeCatalog(path); - await this.setCatalog(cat); - } catch (err: unknown) { - console.error('unable to read created catalog file, continue using default catalog', err); - } - }); - watcher.onDidDelete(async () => { - console.log('user catalog file deleted, reverting to default catalog'); - await this.setCatalog(defaultCatalog); - }); - watcher.onDidChange(async () => { - try { - const cat = await this.readAndAnalyzeCatalog(path); - await this.setCatalog(cat); - } catch (err: unknown) { - console.error('unable to read modified catalog file, reverting to default catalog', err); - } - }); - } - - async readAndAnalyzeCatalog(path: string): Promise { - const data = await fs.promises.readFile(path, 'utf-8'); - return JSON.parse(data) as Catalog; - // TODO(feloy): check version, ... - } - - async setCatalog(newCatalog: Catalog) { - this.catalog = newCatalog; - await this.webview.postMessage({ - id: MSG_NEW_CATALOG_STATE, - body: this.catalog, - }); + async ping(): Promise { + return 'pong'; } async openURL(url: string): Promise { @@ -96,10 +38,6 @@ export class StudioApiImpl implements StudioAPI { return this.recipeStatusRegistry.getStatus(recipeId); } - async ping(): Promise { - return 'pong'; - } - async getRecentRecipes(): Promise { return []; // no recent implementation for now } @@ -187,4 +125,8 @@ export class StudioApiImpl implements StudioAPI { async getPlaygroundStates(): Promise { return this.playgroundManager.getState(); } + + async getCatalog(): Promise { + return this.catalogManager.getCatalog(); + } } diff --git a/packages/backend/src/studio.ts b/packages/backend/src/studio.ts index f80118b4f..a81f3fa22 100644 --- a/packages/backend/src/studio.ts +++ b/packages/backend/src/studio.ts @@ -95,7 +95,11 @@ export class Studio { const recipeStatusRegistry = new RecipeStatusRegistry(taskRegistry); const applicationManager = new ApplicationManager(gitManager, recipeStatusRegistry, this.#extensionContext); this.playgroundManager = new PlayGroundManager(this.#panel.webview); - this.catalogManager = new CatalogManager(applicationManager.appUserDirectory); + // Create catalog manager, responsible for loading the catalog files and watching for changes + this.catalogManager = new CatalogManager( + applicationManager.appUserDirectory, + this.#panel.webview, + ); // Creating StudioApiImpl this.studioApi = new StudioApiImpl( @@ -104,7 +108,6 @@ export class Studio { taskRegistry, this.playgroundManager, this.catalogManager, - this.#panel.webview, ); await this.catalogManager.loadCatalog(); From bfd3e0a90071981544ffb8f6f84bc91d5e01e928 Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:34:24 +0100 Subject: [PATCH 7/9] fix: copyrights missing Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- .../src/managers/applicationManager.ts | 18 ++++++++++++++++++ .../backend/src/managers/catalogManager.ts | 18 ++++++++++++++++++ packages/backend/src/managers/gitManager.ts | 18 ++++++++++++++++++ packages/backend/src/managers/playground.ts | 19 +++++++++++++++++++ packages/backend/src/models/AIConfig.ts | 18 ++++++++++++++++++ .../src/registries/RecipeStatusRegistry.ts | 18 ++++++++++++++++++ .../backend/src/registries/TaskRegistry.ts | 18 ++++++++++++++++++ packages/backend/src/studio-api-impl.ts | 18 ++++++++++++++++++ packages/backend/src/utils/pathUtils.ts | 18 ++++++++++++++++++ .../backend/src/utils/recipeStatusUtils.ts | 18 ++++++++++++++++++ 10 files changed, 181 insertions(+) diff --git a/packages/backend/src/managers/applicationManager.ts b/packages/backend/src/managers/applicationManager.ts index 971bfe1fd..7158cfe1a 100644 --- a/packages/backend/src/managers/applicationManager.ts +++ b/packages/backend/src/managers/applicationManager.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 type { Recipe } from '@shared/src/models/IRecipe'; import { arch } from 'node:os'; import type { GitManager } from './gitManager'; diff --git a/packages/backend/src/managers/catalogManager.ts b/packages/backend/src/managers/catalogManager.ts index 83037692f..605a9ac97 100644 --- a/packages/backend/src/managers/catalogManager.ts +++ b/packages/backend/src/managers/catalogManager.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 type { Catalog } from '@shared/src/models/ICatalog'; import path from 'node:path'; import { existsSync, promises } from 'node:fs'; diff --git a/packages/backend/src/managers/gitManager.ts b/packages/backend/src/managers/gitManager.ts index a5004acb3..919e232ac 100644 --- a/packages/backend/src/managers/gitManager.ts +++ b/packages/backend/src/managers/gitManager.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 simpleGit, { type SimpleGit } from 'simple-git'; export class GitManager { diff --git a/packages/backend/src/managers/playground.ts b/packages/backend/src/managers/playground.ts index c1ccfbe47..7bf9e8c38 100644 --- a/packages/backend/src/managers/playground.ts +++ b/packages/backend/src/managers/playground.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 { provider, containerEngine, @@ -14,6 +32,7 @@ import { getFreePort } from '../utils/ports'; import type { QueryState } from '@shared/src/models/IPlaygroundQueryState'; import { MSG_NEW_PLAYGROUND_QUERIES_STATE } from '@shared/Messages'; +// TODO: this should not be hardcoded const LOCALAI_IMAGE = 'quay.io/go-skynet/local-ai:v2.5.1'; function findFirstProvider(): ProviderContainerConnection | undefined { diff --git a/packages/backend/src/models/AIConfig.ts b/packages/backend/src/models/AIConfig.ts index 097f5230d..849a549ce 100644 --- a/packages/backend/src/models/AIConfig.ts +++ b/packages/backend/src/models/AIConfig.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 * as jsYaml from 'js-yaml'; export interface ContainerConfig { diff --git a/packages/backend/src/registries/RecipeStatusRegistry.ts b/packages/backend/src/registries/RecipeStatusRegistry.ts index 63820c2f9..0a8779817 100644 --- a/packages/backend/src/registries/RecipeStatusRegistry.ts +++ b/packages/backend/src/registries/RecipeStatusRegistry.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 type { RecipeStatus } from '@shared/src/models/IRecipeStatus'; import type { TaskRegistry } from './TaskRegistry'; diff --git a/packages/backend/src/registries/TaskRegistry.ts b/packages/backend/src/registries/TaskRegistry.ts index 1508a831c..7c7f1cf6e 100644 --- a/packages/backend/src/registries/TaskRegistry.ts +++ b/packages/backend/src/registries/TaskRegistry.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 type { Task } from '@shared/src/models/ITask'; export class TaskRegistry { diff --git a/packages/backend/src/studio-api-impl.ts b/packages/backend/src/studio-api-impl.ts index e8b1b0be1..73a73b16b 100644 --- a/packages/backend/src/studio-api-impl.ts +++ b/packages/backend/src/studio-api-impl.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 type { StudioAPI } from '@shared/src/StudioAPI'; import type { Category } from '@shared/src/models/ICategory'; import type { Recipe } from '@shared/src/models/IRecipe'; diff --git a/packages/backend/src/utils/pathUtils.ts b/packages/backend/src/utils/pathUtils.ts index 0d60707d3..f3fb60d6b 100644 --- a/packages/backend/src/utils/pathUtils.ts +++ b/packages/backend/src/utils/pathUtils.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 path from 'path'; export function getParentDirectory(filePath: string): string { diff --git a/packages/backend/src/utils/recipeStatusUtils.ts b/packages/backend/src/utils/recipeStatusUtils.ts index e04c0cbca..4e937c0bc 100644 --- a/packages/backend/src/utils/recipeStatusUtils.ts +++ b/packages/backend/src/utils/recipeStatusUtils.ts @@ -1,3 +1,21 @@ +/********************************************************************** + * 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 type { RecipeStatus, RecipeStatusState } from '@shared/src/models/IRecipeStatus'; import type { Task, TaskState } from '@shared/src/models/ITask'; import type { RecipeStatusRegistry } from '../registries/RecipeStatusRegistry'; From b150cab18f4bc59826b5ef1fbe7dd4d9c5fb5156 Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:37:26 +0100 Subject: [PATCH 8/9] fix: prettier Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- packages/backend/src/studio.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/backend/src/studio.ts b/packages/backend/src/studio.ts index a81f3fa22..15db733c0 100644 --- a/packages/backend/src/studio.ts +++ b/packages/backend/src/studio.ts @@ -96,10 +96,7 @@ export class Studio { const applicationManager = new ApplicationManager(gitManager, recipeStatusRegistry, this.#extensionContext); this.playgroundManager = new PlayGroundManager(this.#panel.webview); // Create catalog manager, responsible for loading the catalog files and watching for changes - this.catalogManager = new CatalogManager( - applicationManager.appUserDirectory, - this.#panel.webview, - ); + this.catalogManager = new CatalogManager(applicationManager.appUserDirectory, this.#panel.webview); // Creating StudioApiImpl this.studioApi = new StudioApiImpl( From 91601e2f8aa7ede1caaf0d65f910620d3ef2a3b6 Mon Sep 17 00:00:00 2001 From: axel7083 <42176370+axel7083@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:41:52 +0100 Subject: [PATCH 9/9] fix: prettier Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com> --- packages/backend/src/managers/catalogManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/managers/catalogManager.ts b/packages/backend/src/managers/catalogManager.ts index 605a9ac97..776bf27da 100644 --- a/packages/backend/src/managers/catalogManager.ts +++ b/packages/backend/src/managers/catalogManager.ts @@ -66,7 +66,7 @@ export class CatalogManager { try { this.watchCatalogFile(catalogPath); // do not await, we want to do this async } catch (err: unknown) { - console.error('unable to watch catalog file, changes to the catalog file won\'t be reflected to the UI', err); + console.error("unable to watch catalog file, changes to the catalog file won't be reflected to the UI", err); } try {