diff --git a/src/app.css b/src/app.css index 3c723ff..99d7ca4 100644 --- a/src/app.css +++ b/src/app.css @@ -42,7 +42,7 @@ --background: 0.115 0 89.876; --foreground: 0.9461 0 0; - --border: 0.264 0 89.876; + --border: 0.2256 0 0; --input: 0.244 0 89.876; --ring: 0.4493 0.1953 294.69; diff --git a/src/lib/client/action/repositories/package.repository.ts b/src/lib/client/action/repositories/package.repository.ts index ac5de77..7b46896 100644 --- a/src/lib/client/action/repositories/package.repository.ts +++ b/src/lib/client/action/repositories/package.repository.ts @@ -2,30 +2,24 @@ import type { ComponentManifest, SystemManifest } from '$lib/server/project/pack import { BaseRepository } from '../base.repository'; import type { - AddComponentsActionInput, - AddSystemsActionInput, - ComponentPackageResult, + ComponentPkg, CreateComponentActionInput, CreateSystemActionInput, GetComponentsManifestsActionInput, GetSystemsManifestsActionInput, - SystemPackageResult, + InstallPackagesActionInput, + Package, + SearchInput, + SearchPackages, + SystemPkg, } from '../types'; export class ProjectPackageRepository extends BaseRepository { - addComponents(input: AddComponentsActionInput): Promise { - return this.run(`/actions/project/package?/add-components`, input); - } - - addSystems(input: AddSystemsActionInput): Promise { - return this.run(`/actions/project/package?/add-systems`, input); - } - - createComponent(input: CreateComponentActionInput): Promise { + createComponent(input: CreateComponentActionInput): Promise { return this.run(`/actions/project/package?/create-component`, input); } - createSystem(input: CreateSystemActionInput): Promise { + createSystem(input: CreateSystemActionInput): Promise { return this.run(`/actions/project/package?/create-system`, input); } @@ -37,11 +31,19 @@ export class ProjectPackageRepository extends BaseRepository { return this.run(`/actions/project/package?/get-systems-manifests`, input); } - getComponents(): Promise { + getComponents(): Promise { return this.run(`/actions/project/package?/get-components`); } - getSystems(): Promise { + getSystems(): Promise { return this.run(`/actions/project/package?/get-systems`); } + + installPackages(input: InstallPackagesActionInput): Promise { + return this.run(`/actions/project/package?/install-packages`, input); + } + + searchPackages(input: SearchInput): Promise { + return this.run(`/actions/project/package?/search-packages`, input); + } } diff --git a/src/lib/client/action/types/package.type.ts b/src/lib/client/action/types/package.type.ts index 645db8c..43c0928 100644 --- a/src/lib/client/action/types/package.type.ts +++ b/src/lib/client/action/types/package.type.ts @@ -1,18 +1,19 @@ -import type { AddComponentBody } from '$lib/server/actions/project/package/add-components.action'; -import type { AddSystemBody } from '$lib/server/actions/project/package/add-systems.action'; import type { CreateComponentBody } from '$lib/server/actions/project/package/create-component.action'; import type { CreateSystemBody } from '$lib/server/actions/project/package/create-system.action'; import type { GetComponentManifestBody } from '$lib/server/actions/project/package/get-components-manifests.action'; import type { GetSystemManifestBody } from '$lib/server/actions/project/package/get-systems-manifests.action'; +import type { InstallPackagesBody } from '$lib/server/actions/project/package/install-packages.action'; +import type { SearchPackagesBody } from '$lib/server/actions/project/package/search-packages.action'; +import type { PaginateResult, Package as ServerPackage } from '$lib/server/api'; import type { ComponentPackage, SystemPackage } from '$lib/server/project/package/package.type'; -export type ComponentPackageResult = ComponentPackage; +export type ComponentPkg = ComponentPackage; -export type SystemPackageResult = SystemPackage; +export type SystemPkg = SystemPackage; -export type AddComponentsActionInput = AddComponentBody; +export type Package = ComponentPackage | SystemPackage; -export type AddSystemsActionInput = AddSystemBody; +export type InstallPackagesActionInput = InstallPackagesBody; export type GetComponentsManifestsActionInput = GetComponentManifestBody; @@ -21,3 +22,9 @@ export type GetSystemsManifestsActionInput = GetSystemManifestBody; export type CreateComponentActionInput = CreateComponentBody; export type CreateSystemActionInput = CreateSystemBody; + +export type SearchInput = SearchPackagesBody; + +export type ApiPackage = ServerPackage; + +export type SearchPackages = PaginateResult; diff --git a/src/lib/client/ecs/component/component-manager.ts b/src/lib/client/ecs/component/component-manager.ts index f993853..aa7db66 100644 --- a/src/lib/client/ecs/component/component-manager.ts +++ b/src/lib/client/ecs/component/component-manager.ts @@ -1,5 +1,6 @@ import { type Unsubscriber, get, writable } from 'svelte/store'; +import type { ComponentPkg } from '$lib/client/action'; import { useProject } from '$lib/client/project'; import { componentTransformer, componentsTransformer } from '../transformers'; @@ -37,13 +38,8 @@ export class ComponentManager { await dir.readdir(true); } - async import(names: [string, ...string[]]) { - const { actions, ecs, fs } = useProject(); - await actions.package.addComponents({ componentNames: names }); - await this.sync(); - await ecs.components.sync(); - const dir = await fs.getDirectory(); - await dir.readdir(true); + add(component: ComponentPkg) { + this._add(componentTransformer(component)); } async sync() { diff --git a/src/lib/client/ecs/system/system-manager.ts b/src/lib/client/ecs/system/system-manager.ts index c5819c3..4a81cb4 100644 --- a/src/lib/client/ecs/system/system-manager.ts +++ b/src/lib/client/ecs/system/system-manager.ts @@ -1,5 +1,6 @@ import { type Unsubscriber, get, writable } from 'svelte/store'; +import type { SystemPkg } from '$lib/client/action'; import { useProject } from '$lib/client/project'; import { systemTransformer, systemsTransformer } from '../transformers'; @@ -36,13 +37,8 @@ export class SystemManager { await dir.readdir(true); } - async import(names: [string, ...string[]]) { - const { actions, ecs, fs } = useProject(); - await actions.package.addSystems({ systemNames: names }); - await this.sync(); - await ecs.components.sync(); - const dir = await fs.getDirectory(); - await dir.readdir(true); + add(system: SystemPkg) { + this._add(systemTransformer(system)); } async sync() { diff --git a/src/lib/client/ecs/transformers.ts b/src/lib/client/ecs/transformers.ts index 6505e5e..a234f8d 100644 --- a/src/lib/client/ecs/transformers.ts +++ b/src/lib/client/ecs/transformers.ts @@ -1,25 +1,25 @@ +import type { ComponentPkg, SystemPkg } from '$lib/client/action'; import type { Component, Library, Scene, System } from '$lib/client/ecs'; -import type { ComponentPackage, SystemPackage } from '$lib/server/project/package'; import type { Save, SaveLibrary } from '@utils/types'; -export const componentTransformer = (component: ComponentPackage): Component => ({ +export const componentTransformer = (component: ComponentPkg): Component => ({ id: component.manifest.id, name: component.manifest.name, path: component.save.path, params: component.manifest.params, }); -export const componentsTransformer = (components: ComponentPackage[]): Component[] => +export const componentsTransformer = (components: ComponentPkg[]): Component[] => components.map(componentTransformer); -export const systemTransformer = (system: SystemPackage): System => ({ +export const systemTransformer = (system: SystemPkg): System => ({ id: system.manifest.id, name: system.manifest.name, path: system.save.path, }); -export const systemsTransformer = (systems: SystemPackage[]): System[] => +export const systemsTransformer = (systems: SystemPkg[]): System[] => systems.map(systemTransformer); export const libraryTransformer = (lib: SaveLibrary): Library => ({ diff --git a/src/lib/client/project/package-handler.ts b/src/lib/client/project/package-handler.ts index 52ac77f..cf38dd4 100644 --- a/src/lib/client/project/package-handler.ts +++ b/src/lib/client/project/package-handler.ts @@ -1,73 +1,20 @@ import type { Project } from '$lib/client/project'; -import type { ComponentManifest, SystemManifest } from '$lib/server/project/package'; export class PackageHandler { private readonly _project: Project; - private _componentsManifests: Map = new Map(); - private _systemsManifests: Map = new Map(); - constructor(project: Project) { this._project = project; } - async init() { - // Not working but not used currently - // if (this._project.save.save.components.length > 0) { - // this._componentsManifests = new Map( - // ( - // await this._project.actions.package.getComponentsManifests({ - // componentPaths: this._project.save.save.components.map((c) => c.path) as [ - // string, - // ...string[], - // ], - // }) - // ).map((e, index) => [this._project.save.save.components[index].name, e]), - // ); - // } - // if (this._project.save.save.systems.length > 0) { - // this._systemsManifests = new Map( - // ( - // await this._project.actions.package.getSystemsManifests({ - // systemPaths: this._project.save.save.systems.map((s) => s.path) as [ - // string, - // ...string[], - // ], - // }) - // ).map((e, index) => [this._project.save.save.systems[index].name, e]), - // ); - // } - } - - getComponentManifest(componentName: string): ComponentManifest | undefined { - return this._componentsManifests.get(componentName); - } - - getSystemManifest(systemName: string): SystemManifest | undefined { - return this._systemsManifests.get(systemName); - } - - async installComponent(name: string): Promise { - const newComponent = ( - await this._project.actions.package.addComponents({ componentNames: [name] }) - )[0]; + async installPackages(names: [string, ...string[]]): Promise { + await this._project.actions.package.installPackages({ names }); - this._project.save.save.components.push(newComponent.save); - this._componentsManifests.set(newComponent.save.name, newComponent.manifest); - } - - async installSystem(name: string): Promise { - const newSystem = (await this._project.actions.package.addSystems({ systemNames: [name] }))[0]; - - this._project.save.save.systems.push(newSystem.save); - this._systemsManifests.set(newSystem.save.name, newSystem.manifest); - } - - addComponentManifest(componentName: string, component: ComponentManifest) { - this._componentsManifests.set(componentName, component); - } + // installPackages does not return dependencies, so it's required to sync to fetch all new components/systems + await this._project.ecs.components.sync(); + await this._project.ecs.systems.sync(); - addSystemManifest(systemName: string, system: SystemManifest) { - this._systemsManifests.set(systemName, system); + const dir = await this._project.fs.getDirectory(); + await dir.readdir(true); } } diff --git a/src/lib/client/project/project.ts b/src/lib/client/project/project.ts index 537a6bb..177f0cc 100644 --- a/src/lib/client/project/project.ts +++ b/src/lib/client/project/project.ts @@ -32,7 +32,6 @@ export class Project { await this.fs.init(); await this.ecs.init(); await this.save.init(); - await this.packages.init(); this._inited = true; return this; } diff --git a/src/lib/components/Widget/ecs-tree/packages/package-tab.svelte b/src/lib/components/Widget/ecs-tree/packages/package-tab.svelte index db032d9..ba38d58 100644 --- a/src/lib/components/Widget/ecs-tree/packages/package-tab.svelte +++ b/src/lib/components/Widget/ecs-tree/packages/package-tab.svelte @@ -7,8 +7,8 @@ } from '$lib/components/ui/dropdown-menu'; import PackageRow from './package-row.svelte'; import DialogCreatePackage from './dialog-create-package.svelte'; - import DialogImportPackage from './dialog-import-package.svelte'; import { Button } from '$lib/components/ui/button'; + import { getMarketplaceContext } from '$lib/components/marketplace'; import { InputGroup, InputGroupAddon, @@ -45,6 +45,7 @@ const namePlural = $derived(type === 'library' ? 'libraries' : type + 's'); const ecsQuery = getContext<{ packages: string }>('ecsQuery'); + const marketplace = getMarketplaceContext(); let query = $state(''); @@ -65,7 +66,6 @@ ); let createOpen = $state(false); - let importOpen = $state(false); const handleCreate = async (name: string) => { if (!('create' in manager)) @@ -73,12 +73,6 @@ await manager.create(name); }; - const handleImport = async (names: string) => { - if (!('import' in manager)) throw new Error(`Cannot import in library`); - // if (names.length === 0) throw new Error('No elements selected'); - await manager.import([names] as [string, ...string[]]); - }; - const validate = (raw: string, suffix: string = nameCapitalized) => { if (!raw) return 'Name is required'; const name = `${formatFrom.all(raw)[type === 'system' ? 'toCamel' : 'toPascal']()}${suffix}`; @@ -94,7 +88,6 @@ onConfirm={handleCreate} {validate} /> -
@@ -135,7 +128,7 @@ Create {/if} - (importOpen = true)}> + marketplace.open()}> Import diff --git a/src/lib/components/marketplace/index.ts b/src/lib/components/marketplace/index.ts new file mode 100644 index 0000000..2ec52b8 --- /dev/null +++ b/src/lib/components/marketplace/index.ts @@ -0,0 +1,3 @@ +export { default as MarketplaceDialog } from './marketplace-dialog.svelte'; +export { setMarketplaceContext, getMarketplaceContext } from './marketplace.context'; +export type { MarketplaceContext } from './marketplace.context'; diff --git a/src/lib/components/marketplace/marketplace-dialog.svelte b/src/lib/components/marketplace/marketplace-dialog.svelte new file mode 100644 index 0000000..f3f5d58 --- /dev/null +++ b/src/lib/components/marketplace/marketplace-dialog.svelte @@ -0,0 +1,149 @@ + + + + + +
+ Marketplace + +
+
+ +
+ (selectedPackage = pkg)} + {onQueue} + {onDequeue} + /> +
+ +
+
+ + +
+
diff --git a/src/lib/components/marketplace/marketplace-footer.svelte b/src/lib/components/marketplace/marketplace-footer.svelte new file mode 100644 index 0000000..05c9e11 --- /dev/null +++ b/src/lib/components/marketplace/marketplace-footer.svelte @@ -0,0 +1,72 @@ + + +{#if activeTab === 'marketplace'} +
+
+ {#each input as name (name)} + + {name} + + + {/each} + {#if input.length === 0} + Select packages to install + {/if} +
+
+ + +
+
+{:else} +
+ +
+{/if} diff --git a/src/lib/components/marketplace/marketplace-package-details.svelte b/src/lib/components/marketplace/marketplace-package-details.svelte new file mode 100644 index 0000000..df1b9a7 --- /dev/null +++ b/src/lib/components/marketplace/marketplace-package-details.svelte @@ -0,0 +1,89 @@ + + +{#if pkg} + +
+
+
+ {#if isInstalled(pkg)} + + {:else} + + {/if} +
+
+

{pkg.name}

+

{typeLabel(pkg.type)}

+
+ {#if activeTab === 'marketplace' && !isInstalled(pkg)} + {#if isQueued(pkg)} + + {:else} + + {/if} + {/if} +
+ + {#if pkg.description} +
+

+ Description +

+

{pkg.description}

+
+ {/if} + + {#if pkg.tags.length > 0} +
+

Tags

+
+ {#each pkg.tags as tag (tag)} + {tag} + {/each} +
+
+ {/if} + +
+

Type

+ {typeLabel(pkg.type)} +
+
+
+{:else} +
+ +

Select a package to view details

+
+{/if} diff --git a/src/lib/components/marketplace/marketplace-package-item.svelte b/src/lib/components/marketplace/marketplace-package-item.svelte new file mode 100644 index 0000000..4f379c6 --- /dev/null +++ b/src/lib/components/marketplace/marketplace-package-item.svelte @@ -0,0 +1,90 @@ + + +
e.key === 'Enter' && onSelect()} +> +
+ {#if installed} + + {:else} + + {/if} +
+
+

{pkg.name}

+

{typeLabel(pkg.type)}

+
+ + {#if installed} + + Installed + + {:else if onQueue || onDequeue} + {#if queued} + + {:else} + + {/if} + {/if} +
diff --git a/src/lib/components/marketplace/marketplace-package-list.svelte b/src/lib/components/marketplace/marketplace-package-list.svelte new file mode 100644 index 0000000..b5a2d40 --- /dev/null +++ b/src/lib/components/marketplace/marketplace-package-list.svelte @@ -0,0 +1,58 @@ + + + + {#if isLoading} +
+ +
+ {:else if packages.length === 0} +

{emptyMessage}

+ {:else} +
+ {#each packages as pkg (pkg.id)} + onSelect(pkg)} + onQueue={onQueue ? () => onQueue(pkg) : undefined} + onDequeue={onDequeue ? () => onDequeue(pkg.name) : undefined} + /> + {/each} +
+ {/if} +
diff --git a/src/lib/components/marketplace/marketplace-package-tabs.svelte b/src/lib/components/marketplace/marketplace-package-tabs.svelte new file mode 100644 index 0000000..9a43bef --- /dev/null +++ b/src/lib/components/marketplace/marketplace-package-tabs.svelte @@ -0,0 +1,94 @@ + + +
+ +
+ + Marketplace + Installed + +
+ +
+ { + refetch(); + onSelect(null); + }} + onClear={() => { + search = ''; + refetch(); + }} + /> +
+ + + + + + + + +
+
diff --git a/src/lib/components/marketplace/marketplace-search.svelte b/src/lib/components/marketplace/marketplace-search.svelte new file mode 100644 index 0000000..4b6df71 --- /dev/null +++ b/src/lib/components/marketplace/marketplace-search.svelte @@ -0,0 +1,40 @@ + + +
+ + + + {#if isFetching} + + {:else} + + {/if} + + {#if search} + + + + {/if} + +
diff --git a/src/lib/components/marketplace/marketplace.context.ts b/src/lib/components/marketplace/marketplace.context.ts new file mode 100644 index 0000000..77174f7 --- /dev/null +++ b/src/lib/components/marketplace/marketplace.context.ts @@ -0,0 +1,15 @@ +import { getContext, setContext } from 'svelte'; + +const MARKETPLACE_CONTEXT_KEY = 'marketplace'; + +export interface MarketplaceContext { + open: () => void; +} + +export function setMarketplaceContext(ctx: MarketplaceContext): void { + setContext(MARKETPLACE_CONTEXT_KEY, ctx); +} + +export function getMarketplaceContext(): MarketplaceContext { + return getContext(MARKETPLACE_CONTEXT_KEY); +} diff --git a/src/lib/server/actions/project/package/add-components.action.ts b/src/lib/server/actions/project/package/add-components.action.ts deleted file mode 100644 index 5ed7f75..0000000 --- a/src/lib/server/actions/project/package/add-components.action.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Expose } from 'class-transformer'; -import { IsNotEmpty, IsString } from 'class-validator'; - -import type { ComponentPackage } from '$lib/server/project/package/package.type'; - -import { useActionHandler } from '@utils-server/request-handler'; - -export class AddComponentBody { - @Expose() - @IsString({ each: true }) - @IsNotEmpty() - componentNames!: [string, ...string[]]; -} - -export const addComponentsProjectAction = useActionHandler( - async ({ body, project }): Promise => { - return await Promise.all( - body.componentNames.map((componentName) => - project.client.package.installComponent(componentName), - ), - ); - }, - { - body: AddComponentBody, - }, -); diff --git a/src/lib/server/actions/project/package/add-systems.action.ts b/src/lib/server/actions/project/package/add-systems.action.ts deleted file mode 100644 index 01d4c13..0000000 --- a/src/lib/server/actions/project/package/add-systems.action.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Expose } from 'class-transformer'; -import { IsNotEmpty, IsString } from 'class-validator'; - -import type { SystemPackage } from '$lib/server/project/package/package.type'; - -import { useActionHandler } from '@utils-server/request-handler'; - -export class AddSystemBody { - @Expose() - @IsString({ each: true }) - @IsNotEmpty() - systemNames!: [string, ...string[]]; -} - -export const addSystemsProjectAction = useActionHandler( - async ({ body, project }): Promise => { - return await Promise.all( - body.systemNames.map((systemName) => project.client.package.installSystem(systemName)), - ); - }, - { - body: AddSystemBody, - }, -); diff --git a/src/lib/server/actions/project/package/install-packages.action.ts b/src/lib/server/actions/project/package/install-packages.action.ts new file mode 100644 index 0000000..d699f77 --- /dev/null +++ b/src/lib/server/actions/project/package/install-packages.action.ts @@ -0,0 +1,22 @@ +import { Expose } from 'class-transformer'; +import { IsNotEmpty, IsString } from 'class-validator'; + +import type { ComponentPackage, SystemPackage } from '$lib/server/project/package/package.type'; + +import { useActionHandler } from '@utils-server/request-handler'; + +export class InstallPackagesBody { + @Expose() + @IsString({ each: true }) + @IsNotEmpty() + names!: [string, ...string[]]; +} + +export const installPackagesAction = useActionHandler( + async ({ body, project }): Promise<(ComponentPackage | SystemPackage)[]> => { + return await project.client.package.installPackages(body.names); + }, + { + body: InstallPackagesBody, + }, +); diff --git a/src/lib/server/actions/project/package/search-packages.action.ts b/src/lib/server/actions/project/package/search-packages.action.ts new file mode 100644 index 0000000..9a6f6a6 --- /dev/null +++ b/src/lib/server/actions/project/package/search-packages.action.ts @@ -0,0 +1,32 @@ +import { Expose } from 'class-transformer'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; + +import type { Package, PaginateResult } from '$lib/server/api'; + +import { useActionHandler } from '@utils-server/request-handler'; + +export class SearchPackagesBody { + @Expose() + @IsString() + @IsOptional() + search?: string; + + @Expose() + @IsNumber() + @IsOptional() + page?: number; + + @Expose() + @IsNumber() + @IsOptional() + limit?: number; +} + +export const searchPackagesAction = useActionHandler( + async ({ body, api }): Promise> => { + return api.packages.getAll(body); + }, + { + body: SearchPackagesBody, + }, +); diff --git a/src/lib/server/api/client.ts b/src/lib/server/api/client.ts index 21bcb10..8ea5654 100644 --- a/src/lib/server/api/client.ts +++ b/src/lib/server/api/client.ts @@ -7,11 +7,13 @@ import { HttpClient } from '@utils/http'; import { useTokenMiddleware } from './middlewares/refresh-token.middleware'; import { AuthRepository } from './repositories/auth.repository'; +import { PackageRepository } from './repositories/package.repository'; import { ProjectRepository } from './repositories/projects.repository'; import { RegistryRepository } from './repositories/registry.repository'; export interface Api { auth: AuthRepository; + packages: PackageRepository; projects: ProjectRepository; registry: RegistryRepository; } @@ -28,6 +30,7 @@ export const getNoAuthApi = (): Api => { return { auth: new AuthRepository(client, isOnline), + packages: new PackageRepository(client, isOnline), registry: new RegistryRepository(client, isOnline), } as Api; }; @@ -43,6 +46,7 @@ export const getApi = (cookies: Cookies): Api => { }).useMiddlewares(useTokenMiddleware(cookies)); return { auth: new AuthRepository(client), + packages: new PackageRepository(client), projects: new ProjectRepository(client), registry: new RegistryRepository(client), }; diff --git a/src/lib/server/api/repositories/package.repository.ts b/src/lib/server/api/repositories/package.repository.ts new file mode 100644 index 0000000..14d8c56 --- /dev/null +++ b/src/lib/server/api/repositories/package.repository.ts @@ -0,0 +1,10 @@ +import { getUrl } from '@utils/http'; + +import { BaseRepository } from '../base.repository'; +import type { Package, PaginateQuery, PaginateResult, SearchQuery } from '../types'; + +export class PackageRepository extends BaseRepository { + getAll(query?: PaginateQuery & SearchQuery): Promise> { + return this.get(getUrl(`/packages`, query), { offline: true }); + } +} diff --git a/src/lib/server/api/types/index.ts b/src/lib/server/api/types/index.ts index 24d88b4..83f4894 100644 --- a/src/lib/server/api/types/index.ts +++ b/src/lib/server/api/types/index.ts @@ -1,3 +1,5 @@ export * from './auth.type'; +export * from './package.type'; +export * from './paginate.type'; export * from './project.type'; export * from './registry.type'; diff --git a/src/lib/server/api/types/package.type.ts b/src/lib/server/api/types/package.type.ts new file mode 100644 index 0000000..5600cc7 --- /dev/null +++ b/src/lib/server/api/types/package.type.ts @@ -0,0 +1,9 @@ +import type { PackageTypeEnum } from '$lib/server/project/package'; + +export interface Package { + id: string; + name: string; + type: PackageTypeEnum; + description: string | null; + tags: string[]; +} diff --git a/src/lib/server/api/types/paginate.type.ts b/src/lib/server/api/types/paginate.type.ts new file mode 100644 index 0000000..e8bcd6f --- /dev/null +++ b/src/lib/server/api/types/paginate.type.ts @@ -0,0 +1,16 @@ +export interface PaginateQuery { + page?: number; + limit?: number; +} + +export interface SearchQuery { + search?: string; +} + +export type PaginateResult = { + data: T[]; + total: number; + page: number; + limit: number; + totalPages: number; +}; diff --git a/src/lib/server/project/package/manifest-resolver.ts b/src/lib/server/project/package/manifest-resolver.ts index 40d7353..c1c91ac 100644 --- a/src/lib/server/project/package/manifest-resolver.ts +++ b/src/lib/server/project/package/manifest-resolver.ts @@ -71,14 +71,14 @@ const getManifestFromNode = (source: ts.VariableDeclaration | null): any | null return parseElement(init); }; -const parseManifest = (title: string, source: ts.SourceFile): any | null => { +const parseManifest = (type: PackageTypeEnum, source: ts.SourceFile): any | null => { const id = getName(source); - const manifest = getManifestFromNode(findManifestNode(title, source)); + const manifest = getManifestFromNode(findManifestNode(MANIFEST_TITLES[type], source)); if (!id || !manifest) return null; - return { id, ...manifest }; + return { id, type, ...manifest }; }; export const resolveManifest = (type: PackageTypeEnum, content: string): any | null => { const source = ts.createSourceFile('tmp.ts', content, ts.ScriptTarget.ESNext, true); - return parseManifest(MANIFEST_TITLES[type], source); + return parseManifest(type, source); }; diff --git a/src/lib/server/project/package/package-handler.ts b/src/lib/server/project/package/package-handler.ts index 9361f20..fd8fdee 100644 --- a/src/lib/server/project/package/package-handler.ts +++ b/src/lib/server/project/package/package-handler.ts @@ -22,20 +22,19 @@ export class PackageHandler { this.handler = handler; } - async installComponent(name: string): Promise { - const rc = await this.handler._api.registry.getPackage(name); - if (rc.type !== 'component') throw new Error(`Can only add component: ${name} is a ${rc.type}`); - this.handler._cli.install([name], { server: this.handler._part === 'server' || undefined }); - - return this._getNewComponentPackage(rc.name, rc._file); - } - - async installSystem(name: string): Promise { - const rs = await this.handler._api.registry.getPackage(name); - if (rs.type !== 'system') throw new Error(`Can only add system: ${name} is a ${rs.type}`); - this.handler._cli.install([name], { server: this.handler._part === 'server' || undefined }); - - return this._getNewSystemPackage(rs.name, rs._file); + async installPackages( + names: [string, ...string[]], + ): Promise<(ComponentPackage | SystemPackage)[]> { + if (names.length === 0) return []; + this.handler._cli.install(names, { server: this.handler._part === 'server' || undefined }); + + return Promise.all( + names.map(async (name) => { + const r = await this.handler._api.registry.getPackage(name); + if (r.type === 'component') return this._getNewComponentPackage(r.name, r._file); + return this._getNewSystemPackage(r.name, r._file); + }), + ); } /** diff --git a/src/lib/server/project/package/package.type.ts b/src/lib/server/project/package/package.type.ts index 3fdfcb3..9b320b9 100644 --- a/src/lib/server/project/package/package.type.ts +++ b/src/lib/server/project/package/package.type.ts @@ -7,8 +7,11 @@ export enum PackageTypeEnum { SYSTEM = 'system', } -export type ComponentManifest = EditorComponentManifest & { id: string }; -export type SystemManifest = EditorSystemManifest & { id: string }; +export type ComponentManifest = EditorComponentManifest & { + id: string; + type: PackageTypeEnum.COMPONENT; +}; +export type SystemManifest = EditorSystemManifest & { id: string; type: PackageTypeEnum.SYSTEM }; export interface ComponentPackage { manifest: ComponentManifest; diff --git a/src/lib/utils/http/index.ts b/src/lib/utils/http/index.ts index 2d6ee12..3fbc758 100644 --- a/src/lib/utils/http/index.ts +++ b/src/lib/utils/http/index.ts @@ -4,3 +4,4 @@ export { type MiddlewareParams, type RequestOptions, } from './client'; +export { getUrl } from './query'; diff --git a/src/lib/utils/http/query.ts b/src/lib/utils/http/query.ts new file mode 100644 index 0000000..43bc500 --- /dev/null +++ b/src/lib/utils/http/query.ts @@ -0,0 +1,5 @@ +export const getUrl = (url: string, query?: Record) => { + const entries = Object.entries(query ?? {}).filter(([, value]) => value !== undefined); + if (entries.length === 0) return url; + return `${url}?${new URLSearchParams(entries).toString()}`; +}; diff --git a/src/routes/actions/project/package/+page.server.ts b/src/routes/actions/project/package/+page.server.ts index 2650b05..fbdc119 100644 --- a/src/routes/actions/project/package/+page.server.ts +++ b/src/routes/actions/project/package/+page.server.ts @@ -1,19 +1,19 @@ -import { addComponentsProjectAction } from '$lib/server/actions/project/package/add-components.action'; -import { addSystemsProjectAction } from '$lib/server/actions/project/package/add-systems.action'; import { createComponentProjectAction } from '$lib/server/actions/project/package/create-component.action'; import { createSystemProjectAction } from '$lib/server/actions/project/package/create-system.action'; import { getComponentsManifestsAction } from '$lib/server/actions/project/package/get-components-manifests.action'; import { getComponentsAction } from '$lib/server/actions/project/package/get-components.action'; import { getSystemsManifestsAction } from '$lib/server/actions/project/package/get-systems-manifests.action'; import { getSystemsAction } from '$lib/server/actions/project/package/get-systems.action'; +import { installPackagesAction } from '$lib/server/actions/project/package/install-packages.action'; +import { searchPackagesAction } from '$lib/server/actions/project/package/search-packages.action'; export const actions = { - 'add-components': addComponentsProjectAction, - 'add-systems': addSystemsProjectAction, 'create-component': createComponentProjectAction, 'create-system': createSystemProjectAction, 'get-components-manifests': getComponentsManifestsAction, 'get-systems-manifests': getSystemsManifestsAction, 'get-components': getComponentsAction, 'get-systems': getSystemsAction, + 'install-packages': installPackagesAction, + 'search-packages': searchPackagesAction, }; diff --git a/src/routes/dashboard/+page.svelte b/src/routes/dashboard/+page.svelte index 654732a..d337290 100644 --- a/src/routes/dashboard/+page.svelte +++ b/src/routes/dashboard/+page.svelte @@ -15,6 +15,10 @@ import { page } from '$app/state'; import { runSafe } from '@utils-client/error'; import { FullPageProjectSpinner } from '$lib/components/project-loader'; + import { MarketplaceDialog, setMarketplaceContext } from '$lib/components/marketplace'; + + let marketplaceOpen = $state(false); + setMarketplaceContext({ open: () => (marketplaceOpen = true) }); let tab = $derived($tabsStore.tabs.find((t) => t.id === $tabsStore.selectedTabId)); let Component = $derived(tab ? tabRegistry[tab.type]?.component : null); @@ -71,6 +75,7 @@ {#if loaded} +