-
+
+
{#if selected}
diff --git a/src/lib/components/Widget/ecs-tree/scenes/systems/scene-systems-list.svelte b/src/lib/components/Widget/ecs-tree/scenes/systems/scene-system-list.svelte
similarity index 97%
rename from src/lib/components/Widget/ecs-tree/scenes/systems/scene-systems-list.svelte
rename to src/lib/components/Widget/ecs-tree/scenes/systems/scene-system-list.svelte
index acfe1ba..60636e9 100644
--- a/src/lib/components/Widget/ecs-tree/scenes/systems/scene-systems-list.svelte
+++ b/src/lib/components/Widget/ecs-tree/scenes/systems/scene-system-list.svelte
@@ -8,6 +8,7 @@
} from '$lib/components/ui/context-menu';
import { Button } from '$lib/components/ui/button';
import DialogAddSceneSystem from './dialog-add-scene-system.svelte';
+ import SceneSystemRow from './scene-system-row.svelte';
import type { SceneSystemManager, System } from '$lib/client/ecs';
import { useProject } from '$lib/client/project';
import type { Writable } from 'svelte/store';
@@ -97,6 +98,7 @@
dropTarget.pos === 'before'}
{@const isDropAfter =
!isNext(dragId, sysName, 'after') && dropTarget?.id === sysName && dropTarget.pos === 'after'}
+ {@const handle = manager.get(sysName)}
@@ -138,8 +140,7 @@
handleDrop(sysName);
}}
>
-
- {sysName}
+
+ import type { SceneSystemHandle } from '$lib/client/ecs';
+
+ interface Props {
+ handle: SceneSystemHandle;
+ }
+
+ const { handle }: Props = $props();
+
+ const system = $derived(handle.store);
+
+
+
+{$system.name}
diff --git a/src/lib/components/project-loader/full-page-project-spinner.svelte b/src/lib/components/project-loader/full-page-project-spinner.svelte
new file mode 100644
index 0000000..7e400d0
--- /dev/null
+++ b/src/lib/components/project-loader/full-page-project-spinner.svelte
@@ -0,0 +1,7 @@
+
+
+
diff --git a/src/lib/components/project-loader/index.ts b/src/lib/components/project-loader/index.ts
new file mode 100644
index 0000000..ae20eda
--- /dev/null
+++ b/src/lib/components/project-loader/index.ts
@@ -0,0 +1,5 @@
+import FullPageProjectSpinner from './full-page-project-spinner.svelte';
+import ProgressBar from './progress-bar.svelte';
+import ProjectSpinner from './project-spinner.svelte';
+
+export { ProgressBar, ProjectSpinner, FullPageProjectSpinner };
diff --git a/src/lib/components/ProjectLoader/ProgressBar.svelte b/src/lib/components/project-loader/progress-bar.svelte
similarity index 89%
rename from src/lib/components/ProjectLoader/ProgressBar.svelte
rename to src/lib/components/project-loader/progress-bar.svelte
index 8f4067c..88c692a 100644
--- a/src/lib/components/ProjectLoader/ProgressBar.svelte
+++ b/src/lib/components/project-loader/progress-bar.svelte
@@ -1,5 +1,5 @@
+
+
+
+
+ Project loading...
+ Retrieving save
+
+
+
+
+
+
diff --git a/src/lib/components/ui/progress/index.ts b/src/lib/components/ui/progress/index.ts
new file mode 100644
index 0000000..c965294
--- /dev/null
+++ b/src/lib/components/ui/progress/index.ts
@@ -0,0 +1,7 @@
+import Root from './progress.svelte';
+
+export {
+ Root,
+ //
+ Root as Progress,
+};
diff --git a/src/lib/components/ui/progress/progress.svelte b/src/lib/components/ui/progress/progress.svelte
new file mode 100644
index 0000000..0266042
--- /dev/null
+++ b/src/lib/components/ui/progress/progress.svelte
@@ -0,0 +1,30 @@
+
+
+
+
+
diff --git a/src/lib/server/actions/project/complete.action.ts b/src/lib/server/actions/project/complete.action.ts
new file mode 100644
index 0000000..844bacb
--- /dev/null
+++ b/src/lib/server/actions/project/complete.action.ts
@@ -0,0 +1,45 @@
+import { resolveSessionFunctions } from '$lib/server/actions/project/load.action';
+import { loadProject } from '$lib/server/project';
+
+import { useActionHandler } from '@utils-server/request-handler';
+
+export class CompleteProjectBody {
+ gatewayId!: string;
+
+ language?: 'js' | 'ts';
+ multiplayerServer?: boolean;
+}
+
+export const completeProjectAction = useActionHandler(
+ async (handler) => {
+ const { body, cli, api } = handler;
+
+ const project = await api.projects.getProject(body.gatewayId);
+ const resolver = await resolveSessionFunctions.gatewayId(body.gatewayId, handler);
+
+ const parts = resolver.path.split('/');
+ const end = parts.pop();
+
+ cli.new({
+ editor: true,
+ directory: parts.join('/'),
+ name: project.name,
+ path: end,
+ packageManager: 'bun',
+ language: body.language,
+ server: body.multiplayerServer,
+ docker: false,
+ lint: false,
+ initFunctions: true,
+ strict: false,
+ git: false,
+ });
+
+ return await loadProject(resolver, handler);
+ },
+ {
+ body: CompleteProjectBody,
+ onlineOnly: true,
+ projectOptional: true,
+ },
+);
diff --git a/src/lib/server/actions/project/load.action.ts b/src/lib/server/actions/project/load.action.ts
index 604101d..233797f 100644
--- a/src/lib/server/actions/project/load.action.ts
+++ b/src/lib/server/actions/project/load.action.ts
@@ -52,7 +52,7 @@ const resolveSessionFromPath = async (
return { path };
};
-const resolveSessionFunctions: Record<
+export const resolveSessionFunctions: Record<
keyof Omit,
(el: string, options: Handler) => Promise
> = {
diff --git a/src/lib/server/actions/project/package/get-components-manifests.action.ts b/src/lib/server/actions/project/package/get-components-manifests.action.ts
index 679bded..d62e7a4 100644
--- a/src/lib/server/actions/project/package/get-components-manifests.action.ts
+++ b/src/lib/server/actions/project/package/get-components-manifests.action.ts
@@ -1,4 +1,4 @@
-import type { EditorComponentManifest } from '@nanoforge-dev/ecs-lib';
+import type { ComponentManifest } from '$lib/server/project/package';
import { useActionHandler } from '@utils-server/request-handler';
@@ -7,7 +7,7 @@ export class GetComponentManifestBody {
}
export const getComponentsManifestsAction = useActionHandler(
- async ({ body, project }): Promise => {
+ async ({ body, project }): Promise => {
return await Promise.all(
body.componentPaths.map((path) => project.client.package.getComponentManifest(path)),
);
diff --git a/src/lib/server/actions/project/package/get-systems-manifests.action.ts b/src/lib/server/actions/project/package/get-systems-manifests.action.ts
index 78bbbdd..bd1eef9 100644
--- a/src/lib/server/actions/project/package/get-systems-manifests.action.ts
+++ b/src/lib/server/actions/project/package/get-systems-manifests.action.ts
@@ -1,4 +1,4 @@
-import type { EditorSystemManifest } from '@nanoforge-dev/ecs-lib';
+import type { SystemManifest } from '$lib/server/project/package';
import { useActionHandler } from '@utils-server/request-handler';
@@ -7,7 +7,7 @@ export class GetSystemManifestBody {
}
export const getSystemsManifestsAction = useActionHandler(
- async ({ body, project }): Promise => {
+ async ({ body, project }): Promise => {
return await Promise.all(
body.systemPaths.map((path) => project.client.package.getSystemManifest(path)),
);
diff --git a/src/lib/server/actions/project/save/set-save.action.ts b/src/lib/server/actions/project/save/set-save.action.ts
index ee46390..b553fd6 100644
--- a/src/lib/server/actions/project/save/set-save.action.ts
+++ b/src/lib/server/actions/project/save/set-save.action.ts
@@ -1,4 +1,4 @@
-import { type Save } from '@utils/types';
+import type { Save } from '@utils/types';
import { useActionHandler } from '@utils-server/request-handler';
diff --git a/src/lib/server/api/base.repository.ts b/src/lib/server/api/base.repository.ts
index bd48cd9..a5e5ee8 100644
--- a/src/lib/server/api/base.repository.ts
+++ b/src/lib/server/api/base.repository.ts
@@ -60,7 +60,7 @@ export class BaseRepository {
const res = await this._client[request](path, options);
if (!res.ok)
throw new Error(`Request failed with status code ${res.status}`, {
- cause: res,
+ cause: await res.json(),
});
return (await res.json()) as R;
}
diff --git a/src/lib/server/api/client.ts b/src/lib/server/api/client.ts
index d92da89..21bcb10 100644
--- a/src/lib/server/api/client.ts
+++ b/src/lib/server/api/client.ts
@@ -1,6 +1,7 @@
import type { Cookies } from '@sveltejs/kit';
import { env } from '$env/dynamic/private';
+import { PUBLIC_MODE } from '$env/static/public';
import { HttpClient } from '@utils/http';
@@ -23,7 +24,7 @@ export const getNoAuthApi = (): Api => {
'Content-Type': 'application/json',
},
});
- const isOnline = env.PUBLIC_MODE === 'ONLINE';
+ const isOnline = PUBLIC_MODE === 'ONLINE';
return {
auth: new AuthRepository(client, isOnline),
@@ -32,7 +33,7 @@ export const getNoAuthApi = (): Api => {
};
export const getApi = (cookies: Cookies): Api => {
- if (env.PUBLIC_MODE !== 'ONLINE') throw new Error('API is only available in online mode');
+ if (PUBLIC_MODE !== 'ONLINE') throw new Error('API is only available in online mode');
if (!env.API_KEY) throw new Error('API_KEY is not defined');
const client = new HttpClient(env.API_URL ?? DEFAULT_API_URL, {
headers: {
diff --git a/src/lib/server/api/repositories/projects.repository.ts b/src/lib/server/api/repositories/projects.repository.ts
index 01c6de6..33ffe22 100644
--- a/src/lib/server/api/repositories/projects.repository.ts
+++ b/src/lib/server/api/repositories/projects.repository.ts
@@ -7,6 +7,6 @@ export class ProjectRepository extends BaseRepository {
}
getProjects(): Promise {
- return this.get(`/projects}`);
+ return this.get(`/projects`);
}
}
diff --git a/src/lib/server/file-system/index.ts b/src/lib/server/file-system/index.ts
index a4b242d..2b2f418 100644
--- a/src/lib/server/file-system/index.ts
+++ b/src/lib/server/file-system/index.ts
@@ -1,3 +1,3 @@
export { FileSystem } from './file-system';
export { directoryContentToFileEntries, type ProjectDirectory } from './project-directory';
-export { type ProjectFile } from './project-file';
+export type { ProjectFile } from './project-file';
diff --git a/src/lib/server/git/git.ts b/src/lib/server/git/git.ts
index b99ce7b..303f126 100644
--- a/src/lib/server/git/git.ts
+++ b/src/lib/server/git/git.ts
@@ -1,4 +1,5 @@
import { $ } from 'bun';
+import { existsSync } from 'fs';
import { resolve } from 'path';
import { env } from '$env/dynamic/private';
@@ -14,24 +15,29 @@ export class Git {
async clone(url: string, options?: { sshKey?: string }): Promise {
const path = await this.resolvePath(url);
- await this.runCommand('clone', `${url} ${path}`, { ...options });
+ if (existsSync(path)) return path;
+ await this.runCommand('clone', [url, path], { ...options });
return path;
}
private async runCommand(
command: string,
- params: string,
+ params: string[],
options?: { path?: string; sshKey?: string },
) {
- let sshPath = undefined;
+ let sshPath: string | undefined;
if (options?.sshKey) {
sshPath = await this.createSshKeyFile(options.sshKey);
}
- const sshConfig = sshPath ? `-c core.sshCommand="ssh -i ${sshPath}" ` : '';
+
const cwd = resolve(this._rootPath, options?.path ?? '');
+ const sshEnv = sshPath ? { GIT_SSH_COMMAND: `ssh -i ${sshPath}` } : {};
- await $`git ${command} ${sshConfig}${params}`.cwd(cwd);
- if (sshPath) await this.deleteSshKeyFile(sshPath);
+ try {
+ await $`git ${command} ${params}`.cwd(cwd).env({ ...process.env, ...sshEnv });
+ } finally {
+ if (sshPath) await this.deleteSshKeyFile(sshPath);
+ }
}
private async createSshKeyFile(sshKey: string): Promise {
diff --git a/src/lib/server/project/library/library-handler.ts b/src/lib/server/project/library/library-handler.ts
index 7373816..5eaebad 100644
--- a/src/lib/server/project/library/library-handler.ts
+++ b/src/lib/server/project/library/library-handler.ts
@@ -1,8 +1,8 @@
-import { type ProjectHandler } from '$lib/server/project';
+import type { ProjectHandler } from '$lib/server/project';
import { formatFrom } from '@utils/format';
-import { type LibraryPackage } from './library.type';
+import type { LibraryPackage } from './library.type';
export class LibraryHandler {
private readonly handler: ProjectHandler;
diff --git a/src/lib/server/project/load-project.ts b/src/lib/server/project/load-project.ts
index a387c8d..dc28a3e 100644
--- a/src/lib/server/project/load-project.ts
+++ b/src/lib/server/project/load-project.ts
@@ -8,7 +8,7 @@ import { getProject } from '$lib/server/session/project/project-functions';
import { Exception } from '@utils/exception';
-import { type Handler } from '@utils-server/request-handler';
+import type { Handler } from '@utils-server/request-handler';
import type { Project } from './project.type';
diff --git a/src/lib/server/project/loader/loader.ts b/src/lib/server/project/loader/loader.ts
index 2ec3b53..f06f669 100644
--- a/src/lib/server/project/loader/loader.ts
+++ b/src/lib/server/project/loader/loader.ts
@@ -6,7 +6,7 @@ import {
directoryContentToFileEntries,
} from '$lib/server/file-system';
-import { type ProjectHandler } from '../project-handler';
+import type { ProjectHandler } from '../project-handler';
import { resolveEnv } from './env';
import type { Manifest } from './types';
diff --git a/src/lib/server/project/package/manifest-resolver.ts b/src/lib/server/project/package/manifest-resolver.ts
index 74f666e..40d7353 100644
--- a/src/lib/server/project/package/manifest-resolver.ts
+++ b/src/lib/server/project/package/manifest-resolver.ts
@@ -19,6 +19,20 @@ const findManifestNode = (title: string, source: ts.SourceFile): ts.VariableDecl
return res;
};
+const getName = (source: ts.SourceFile): string | null => {
+ let res = null;
+ source.forEachChild((node) => {
+ if (!ts.isExportAssignment(node)) return;
+ if (
+ !node.getChildren().some((n) => n.getText() === 'export') ||
+ !node.getChildren().some((n) => n.getText() === 'default')
+ )
+ return;
+ res = node.expression.getFirstToken()?.getText();
+ });
+ return res;
+};
+
const parseProperty = (prop: ObjectLiteralElementLike): any => {
if (!ts.isPropertyAssignment(prop)) return {};
@@ -58,7 +72,10 @@ const getManifestFromNode = (source: ts.VariableDeclaration | null): any | null
};
const parseManifest = (title: string, source: ts.SourceFile): any | null => {
- return getManifestFromNode(findManifestNode(title, source));
+ const id = getName(source);
+ const manifest = getManifestFromNode(findManifestNode(title, source));
+ if (!id || !manifest) return null;
+ return { id, ...manifest };
};
export const resolveManifest = (type: PackageTypeEnum, content: string): any | null => {
diff --git a/src/lib/server/project/package/package-handler.ts b/src/lib/server/project/package/package-handler.ts
index b751f8c..e9c4a46 100644
--- a/src/lib/server/project/package/package-handler.ts
+++ b/src/lib/server/project/package/package-handler.ts
@@ -1,14 +1,19 @@
-import type { EditorComponentManifest, EditorSystemManifest } from '@nanoforge-dev/ecs-lib';
import { join } from 'path';
import { FileSystemError } from '$lib/server/file-system/file-system-error';
import type { DirectoryContent } from '$lib/server/file-system/project-directory';
-import { type ProjectHandler } from '$lib/server/project';
+import type { ProjectHandler } from '$lib/server/project';
import { formatFrom } from '@utils/format';
import { resolveManifest } from './manifest-resolver';
-import { type ComponentPackage, PackageTypeEnum, type SystemPackage } from './package.type';
+import {
+ type ComponentManifest,
+ type ComponentPackage,
+ PackageTypeEnum,
+ type SystemManifest,
+ type SystemPackage,
+} from './package.type';
export class PackageHandler {
private readonly handler: ProjectHandler;
@@ -69,7 +74,7 @@ export class PackageHandler {
*
* @returns Manifest of the component
*/
- getComponentManifest(path: string): EditorComponentManifest {
+ getComponentManifest(path: string): ComponentManifest {
return this._getPackageManifest(PackageTypeEnum.COMPONENT, path);
}
@@ -81,7 +86,7 @@ export class PackageHandler {
*
* @returns Manifest of the system
*/
- getSystemManifest(path: string): EditorSystemManifest {
+ getSystemManifest(path: string): SystemManifest {
return this._getPackageManifest(PackageTypeEnum.SYSTEM, path);
}
@@ -111,7 +116,7 @@ export class PackageHandler {
const res: any = {
manifest,
save: {
- name: manifest.name,
+ name: manifest.id,
path,
},
};
diff --git a/src/lib/server/project/package/package.type.ts b/src/lib/server/project/package/package.type.ts
index 6858487..3fdfcb3 100644
--- a/src/lib/server/project/package/package.type.ts
+++ b/src/lib/server/project/package/package.type.ts
@@ -7,12 +7,15 @@ export enum PackageTypeEnum {
SYSTEM = 'system',
}
+export type ComponentManifest = EditorComponentManifest & { id: string };
+export type SystemManifest = EditorSystemManifest & { id: string };
+
export interface ComponentPackage {
- manifest: EditorComponentManifest;
+ manifest: ComponentManifest;
save: SaveComponent;
}
export interface SystemPackage {
- manifest: EditorSystemManifest;
+ manifest: SystemManifest;
save: SaveSystem;
}
diff --git a/src/lib/server/project/project-handler.ts b/src/lib/server/project/project-handler.ts
index dc3cdd2..1213a0f 100644
--- a/src/lib/server/project/project-handler.ts
+++ b/src/lib/server/project/project-handler.ts
@@ -1,13 +1,13 @@
import { join } from 'path';
-import { type Api } from '$lib/server/api';
-import { type Cli } from '$lib/server/cli';
+import type { Api } from '$lib/server/api';
+import type { Cli } from '$lib/server/cli';
import { FileSystem } from '$lib/server/file-system';
import { Exception } from '@utils/exception';
import type { Part } from '@utils/types';
-import { type Context, type Handler } from '@utils-server/request-handler';
+import type { Context, Handler } from '@utils-server/request-handler';
import { LibraryHandler } from './library/library-handler';
import { Loader } from './loader';
diff --git a/src/lib/server/project/save/save-handler.ts b/src/lib/server/project/save/save-handler.ts
index 53af39b..3954049 100644
--- a/src/lib/server/project/save/save-handler.ts
+++ b/src/lib/server/project/save/save-handler.ts
@@ -1,4 +1,4 @@
-import { type ProjectHandler } from '$lib/server/project';
+import type { ProjectHandler } from '$lib/server/project';
import type { Save } from '@utils/types';
diff --git a/src/lib/server/session/session-functions.ts b/src/lib/server/session/session-functions.ts
index ab417d4..5332e5e 100644
--- a/src/lib/server/session/session-functions.ts
+++ b/src/lib/server/session/session-functions.ts
@@ -1,4 +1,4 @@
-import { type Session as SvelteSession } from 'svelte-kit-sessions';
+import type { Session as SvelteSession } from 'svelte-kit-sessions';
import { Exception } from '@utils/exception';
diff --git a/src/lib/server/utils/request-handler/context.ts b/src/lib/server/utils/request-handler/context.ts
index 2163d91..39f00ab 100644
--- a/src/lib/server/utils/request-handler/context.ts
+++ b/src/lib/server/utils/request-handler/context.ts
@@ -1,6 +1,6 @@
import type { RequestEvent } from '@sveltejs/kit';
-import { env } from '$env/dynamic/private';
+import { PUBLIC_MODE } from '$env/static/public';
import {
type Session,
@@ -20,7 +20,7 @@ export const getContext = async (event: RequestEvent): Promise => {
const project = resolveProject(event.request.headers, session) as SessionProject;
return {
- online: env.PUBLIC_MODE === 'ONLINE',
+ online: PUBLIC_MODE === 'ONLINE',
session,
project,
};
diff --git a/src/lib/server/utils/request-handler/types.ts b/src/lib/server/utils/request-handler/types.ts
index 945b41f..e660e66 100644
--- a/src/lib/server/utils/request-handler/types.ts
+++ b/src/lib/server/utils/request-handler/types.ts
@@ -1,6 +1,6 @@
import type { ClassType, MaybePromise } from '@utils/types';
-import { type Handler } from './handler';
+import type { Handler } from './handler';
export type Callback = (opts: Handler) => MaybePromise
{:else}
-