Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@
"@commitlint/cli": "catalog:ci",
"@commitlint/config-conventional": "catalog:ci",
"@favware/cliff-jumper": "catalog:ci",
"@iconify-json/clarity": "catalog:icons",
"@iconify-json/ic": "catalog:icons",
"@iconify-json/icomoon-free": "^1.2.1",
"@iconify-json/material-icon-theme": "catalog:icons",
"@iconify-json/solar": "catalog:icons",
"@inlang/paraglide-js": "catalog:i18n",
Expand Down
672 changes: 348 additions & 324 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

23 changes: 2 additions & 21 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ catalogs:
i18n:
'@inlang/paraglide-js': ^2.18.1
icons:
'@iconify-json/clarity': ^1.2.4
'@iconify-json/ic': ^1.2.4
'@iconify-json/icomoon-free': ^1.2.1
'@iconify-json/material-icon-theme': ^1.2.66
'@iconify-json/solar': ^1.2.5
'@lucide/svelte': ^1.16.0
Expand Down Expand Up @@ -75,24 +77,3 @@ catalogs:
playwright: ^1.60.0
vitest: ^4.1.7
vitest-browser-svelte: ^2.1.1

minimumReleaseAgeExclude:
- '@iconify-json/material-icon-theme@1.2.66'
- '@nanoforge-dev/actions@2.0.0'
- '@nanoforge-dev/common@1.3.0'
- '@nanoforge-dev/ecs-lib@1.3.0'
- '@typescript-eslint/eslint-plugin@8.60.0'
- '@typescript-eslint/parser@8.60.0'
- '@typescript-eslint/project-service@8.60.0'
- '@typescript-eslint/scope-manager@8.60.0'
- '@typescript-eslint/tsconfig-utils@8.60.0'
- '@typescript-eslint/type-utils@8.60.0'
- '@typescript-eslint/types@8.60.0'
- '@typescript-eslint/typescript-estree@8.60.0'
- '@typescript-eslint/utils@8.60.0'
- '@typescript-eslint/visitor-keys@8.60.0'
- typescript-eslint@8.60.0

onlyBuiltDependencies:
- bun
- esbuild
8 changes: 8 additions & 0 deletions src/lib/client/action/repositories/library.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { BaseRepository } from '../base.repository';
import type { InstallLibraryActionInput, InstallLibraryPackageResult } from '../types';

export class ProjectLibraryRepository extends BaseRepository {
install(input: InstallLibraryActionInput): Promise<InstallLibraryPackageResult[]> {
return this.run(`/actions/project/library?/install`, input);
}
}
20 changes: 14 additions & 6 deletions src/lib/client/action/repositories/package.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ import { BaseRepository } from '../base.repository';
import type {
AddComponentsActionInput,
AddSystemsActionInput,
ComponentPackageResult,
CreateComponentActionInput,
CreateSystemActionInput,
GetComponentsManifestsActionInput,
GetSystemsManifestsActionInput,
NewComponentPackageResult,
NewSystemPackageResult,
SystemPackageResult,
} from '../types';

export class ProjectPackageRepository extends BaseRepository {
addComponents(input: AddComponentsActionInput): Promise<NewComponentPackageResult[]> {
addComponents(input: AddComponentsActionInput): Promise<ComponentPackageResult[]> {
return this.run(`/actions/project/package?/add-components`, input);
}

addSystems(input: AddSystemsActionInput): Promise<NewSystemPackageResult[]> {
addSystems(input: AddSystemsActionInput): Promise<SystemPackageResult[]> {
return this.run(`/actions/project/package?/add-systems`, input);
}

createComponent(input: CreateComponentActionInput): Promise<NewComponentPackageResult> {
createComponent(input: CreateComponentActionInput): Promise<ComponentPackageResult> {
return this.run(`/actions/project/package?/create-component`, input);
}

createSystem(input: CreateSystemActionInput): Promise<NewSystemPackageResult> {
createSystem(input: CreateSystemActionInput): Promise<SystemPackageResult> {
return this.run(`/actions/project/package?/create-system`, input);
}

Expand All @@ -38,4 +38,12 @@ export class ProjectPackageRepository extends BaseRepository {
getSystemsManifests(input: GetSystemsManifestsActionInput): Promise<EditorSystemManifest[]> {
return this.run(`/actions/project/package?/get-systems-manifests`, input);
}

getComponents(): Promise<ComponentPackageResult[]> {
return this.run(`/actions/project/package?/get-components`);
}

getSystems(): Promise<SystemPackageResult[]> {
return this.run(`/actions/project/package?/get-systems`);
}
}
1 change: 1 addition & 0 deletions src/lib/client/action/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './fs.type';
export * from './library.type';
export * from './loader.type';
export * from './project.type';
export * from './package.type';
Expand Down
6 changes: 6 additions & 0 deletions src/lib/client/action/types/library.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { InstallLibraryBody } from '$lib/server/actions/project/library/install-library.action';
import type { LibraryPackage } from '$lib/server/project/library';

export type InstallLibraryActionInput = InstallLibraryBody;

export type InstallLibraryPackageResult = LibraryPackage;
9 changes: 3 additions & 6 deletions src/lib/client/action/types/package.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ import type { CreateComponentBody } from '$lib/server/actions/project/package/cr
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 {
NewComponentPackage,
NewSystemPackage,
} from '$lib/server/project/package/package.type';
import type { ComponentPackage, SystemPackage } from '$lib/server/project/package/package.type';

export type NewComponentPackageResult = NewComponentPackage;
export type ComponentPackageResult = ComponentPackage;

export type NewSystemPackageResult = NewSystemPackage;
export type SystemPackageResult = SystemPackage;

export type AddComponentsActionInput = AddComponentBody;

Expand Down
40 changes: 40 additions & 0 deletions src/lib/client/ecs/component/component-handle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { type Writable, get, writable } from 'svelte/store';

import { resolveStore } from '../utils';
import type { ComponentManager } from './component-manager';
import type { Component } from './component.type';

const _storage = writable<Record<string, Writable<Component>>>({});

export class ComponentHandle {
private _manager: ComponentManager;
private readonly _store: Writable<Component>;
public readonly id: string;

static reset() {
_storage.set({});
}

constructor(manager: ComponentManager, component: Component) {
this._manager = manager;
this.id = component.id;

this._store = resolveStore(_storage, this.id, component);
}

get store() {
return this._store;
}

get data() {
return get(this._store);
}

update(component: Partial<Component>) {
this._store.set({ ...get(this._store), ...component });
}

delete() {
this._manager.delete(this.id);
}
}
75 changes: 75 additions & 0 deletions src/lib/client/ecs/component/component-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { type Unsubscriber, get, writable } from 'svelte/store';

import { getId, resetSubscriptions } from '../utils';
import { ComponentHandle } from './component-handle';
import type { Component } from './component.type';

const _storage = writable<Component[]>([]);

const _subscriptions = writable<Record<string, Unsubscriber | null>>({});

export class ComponentManager {
static reset() {
_storage.set([]);
resetSubscriptions(_subscriptions);
}

constructor(components: Component[]) {
_storage.set(components);
}

get store() {
return _storage;
}

get data() {
return get(_storage);
}

add(component: Omit<Component, 'id' | 'path'> & Partial<Pick<Component, 'path'>>) {
const components = get(_storage);
const id = getId(this.data, component.name);
components.push({ ...component, id, path: component.path ?? `components/${id}.ts` });
_storage.set(components);
return id;
}

get(id: string): ComponentHandle {
const component = get(_storage).find((component) => component.id === id);
if (!component) throw new Error(`Component with id ${id} not found`);
const handle = new ComponentHandle(this, component);

this._subscribe(id, handle);

return handle;
}

delete(id: string) {
const components = get(_storage);
_storage.set(components.filter((component) => component.id !== id));

const subscriptions = get(_subscriptions);
if (subscriptions[id]) {
subscriptions[id]();
subscriptions[id] = null;
_subscriptions.set(subscriptions);
}
}

private _subscribe(id: string, handle: ComponentHandle) {
setTimeout(() => {
const subscriptions = get(_subscriptions);
if (subscriptions[id]) return;
subscriptions[id] = handle.store.subscribe((component) => this._update(id, component));
_subscriptions.set(subscriptions);
}, 0);
}

private _update(id: string, component: Component) {
const components = get(_storage);
const index = components.findIndex((s) => s.id === id);
if (index === -1) return;
components[index] = component;
_storage.set(components);
}
}
10 changes: 10 additions & 0 deletions src/lib/client/ecs/component/component.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { EditorComponentManifest } from '@nanoforge-dev/ecs-lib';

export type ComponentParam = EditorComponentManifest['params'][number];

export interface Component {
id: string;
name: string;
path: string;
params: ComponentParam[];
}
91 changes: 91 additions & 0 deletions src/lib/client/ecs/ecs-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
componentsTransformer,
librariesTransformer,
scenesTransformer,
systemsTransformer,
} from '$lib/client/ecs/transformers';
import type { Project } from '$lib/client/project';

import { ComponentHandle } from './component/component-handle';
import { ComponentManager } from './component/component-manager';
import { LibraryHandle } from './library/library-handle';
import { LibraryManager } from './library/library-manager';
import { EntityComponentManager } from './scene/entity/component/component-manager';
import { ComponentParamHandle } from './scene/entity/component/component-param-handle';
import { ComponentParamManager } from './scene/entity/component/component-param-manager';
import { SceneEntityHandle } from './scene/entity/entity-handle';
import { SceneEntityManager } from './scene/entity/entity-manager';
import { SceneHandle } from './scene/scene-handle';
import { SceneManager } from './scene/scene-manager';
import { SceneSystemManager } from './scene/system/system-manager';
import { SystemHandle } from './system/system-handle';
import { SystemManager } from './system/system-manager';

export class ECSHandler {
private readonly _project: Project;

private _sceneManager: SceneManager | undefined;
private _componentManager: ComponentManager | undefined;
private _systemManager: SystemManager | undefined;
private _libraryManager: LibraryManager | undefined;

static reset() {
ComponentHandle.reset();
ComponentManager.reset();
SystemManager.reset();
SystemHandle.reset();
LibraryManager.reset();
LibraryHandle.reset();
SceneHandle.reset();
SceneManager.reset();
SceneSystemManager.reset();
SceneEntityHandle.reset();
SceneEntityManager.reset();
EntityComponentManager.reset();
ComponentParamHandle.reset();
ComponentParamManager.reset();
}

constructor(project: Project) {
this._project = project;
}

async init() {
/**
* @todo
* This init is only made for the current version of the editor
* When scenes will be implemented, this init must be changed accordingly
*/

const components = await this._project.actions.package.getComponents();
const systems = await this._project.actions.package.getSystems();
// Cannot use save handler as it needs to use ecs handler to subscribe to ecs changes
const save = await this._project.actions.save.get();

this._componentManager = new ComponentManager(componentsTransformer(components));
this._systemManager = new SystemManager(systemsTransformer(systems));
this._libraryManager = new LibraryManager(librariesTransformer(save));
// Scene Manager needs others managers to be initialized
this._sceneManager = new SceneManager(this, scenesTransformer(save), ['default'], 'default');
}

get scenes(): SceneManager {
if (!this._sceneManager) throw new Error('ECS handler not initialized');
return this._sceneManager;
}

get components(): ComponentManager {
if (!this._componentManager) throw new Error('ECS handler not initialized');
return this._componentManager;
}

get systems(): SystemManager {
if (!this._systemManager) throw new Error('ECS handler not initialized');
return this._systemManager;
}

get libraries(): LibraryManager {
if (!this._libraryManager) throw new Error('ECS handler not initialized');
return this._libraryManager;
}
}
30 changes: 30 additions & 0 deletions src/lib/client/ecs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export { ECSHandler } from './ecs-handler';

export type { ComponentHandle } from './component/component-handle';
export type { ComponentManager } from './component/component-manager';
export type { Component, ComponentParam } from './component/component.type';

export type { LibraryManager } from './library/library-manager';
export type { LibraryHandle } from './library/library-handle';
export type { Library } from './library/library.type';

export type { SystemManager } from './system/system-manager';
export type { SystemHandle } from './system/system-handle';
export type { System } from './system/system.type';

export type { SceneHandle } from './scene/scene-handle';
export type { SceneManager } from './scene/scene-manager';
export type { Scene } from './scene/scene.type';

export type { SceneSystemManager } from './scene/system/system-manager';
export type { SceneSystemHandle } from './scene/system/system-handle';

export type { SceneEntityHandle } from './scene/entity/entity-handle';
export type { SceneEntityManager } from './scene/entity/entity-manager';
export type { Entity } from './scene/entity/entity.type';

export type { EntityComponentHandle } from './scene/entity/component/component-handle';
export type { EntityComponentManager } from './scene/entity/component/component-manager';

export type { ComponentParamHandle } from './scene/entity/component/component-param-handle';
export type { ComponentParamManager } from './scene/entity/component/component-param-manager';
Loading
Loading