feat: Implemented local game import (with a wizard)

feat: Implemented a radial virtual gamepad keyboard.
fix: Fixed shortcuts for file explorer
This commit is contained in:
Simeon Radivoev 2026-05-04 14:59:43 +03:00
parent e54a6ac8f0
commit 06b7e4074d
Signed by: simeonradivoev
GPG key ID: 7611A451D2A5D37A
66 changed files with 2216 additions and 416 deletions

View file

@ -1,8 +1,5 @@
import { and } from 'drizzle-orm';
import EventEmitter from 'node:events';
import z, { any } from 'zod';
import z from 'zod';
export class TaskQueue
{
@ -10,7 +7,16 @@ export class TaskQueue
private queue?: JobContext<IJob<any, string>, any, string>[] = [];
private events?: EventEmitter<EventsList> = new EventEmitter<EventsList>();
public enqueue<T> (id: string, job: T): T extends IJob<infer TData, infer TState extends string>
constructor()
{
// we need a default error listener or app crashes
this.events?.addListener('error', e =>
{
console.error(e);
});
}
public enqueue<T> (id: string, job: T, throwOnError?: boolean): T extends IJob<infer TData, infer TState extends string>
? Promise<TData>
: never
{
@ -35,7 +41,7 @@ export class TaskQueue
{
job.job.start();
this.activeQueue.push(job.job);
job.job.promise.promise.finally(() =>
job.job.promise.promise.catch(e => { }).finally(() =>
{
const index = this.activeQueue.indexOf(job.job);
this.activeQueue.splice(index, 1);
@ -235,26 +241,21 @@ export class JobContext<T extends IJob<TData, TState>, TData, TState extends str
}
} catch (error)
{
try
if (error instanceof Event)
{
if (error instanceof Event)
if (error.target instanceof AbortSignal)
{
if (error.target instanceof AbortSignal)
{
} else
{
console.error(error);
}
this.m_promise.resolve(undefined);
} else
{
console.error(error);
this.events.emit('error', { id: this.m_id, job: this, error });
this.error = error;
this.m_promise.reject(error);
}
} finally
} else
{
this.m_promise.resolve(undefined);
this.events.emit('error', { id: this.m_id, job: this, error });
this.error = error;
this.m_promise.reject(error);
}
} finally