refactor: Removed the use of d.ts files to support SDK generation for public plugins
This commit is contained in:
parent
06b7e4074d
commit
2683d46b16
114 changed files with 408 additions and 257 deletions
|
|
@ -1,6 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "com.simeonradivoev.gameflow-deck",
|
"name": "com.simeonradivoev.gameflow-deck",
|
||||||
"displayName": "Gameflow",
|
"displayName": "Gameflow",
|
||||||
|
"author": {
|
||||||
|
"name": "Simeon Radivoev",
|
||||||
|
"email": "work@simeonradivoev.com",
|
||||||
|
"url": "https://simeonradivoev.com"
|
||||||
|
},
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"description": "Game Launcher",
|
"description": "Game Launcher",
|
||||||
"icon": "./src/mainview/assets/icon.svg",
|
"icon": "./src/mainview/assets/icon.svg",
|
||||||
|
|
@ -43,7 +48,8 @@
|
||||||
"download:chromium": "bun scripts/download-chromium.ts --out=./bin/chromium",
|
"download:chromium": "bun scripts/download-chromium.ts --out=./bin/chromium",
|
||||||
"download:nwjs": "bun scripts/download-nw.ts",
|
"download:nwjs": "bun scripts/download-nw.ts",
|
||||||
"build:audiosprites": "bun ./scripts/generate-audio-sprites.ts",
|
"build:audiosprites": "bun ./scripts/generate-audio-sprites.ts",
|
||||||
"tsc": "tsc --noEmit"
|
"tsc": "tsc --noEmit",
|
||||||
|
"build:sdk": "bun ./scripts/build-sdk.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"7zip-bin": "^5.2.0",
|
"7zip-bin": "^5.2.0",
|
||||||
|
|
|
||||||
35
scripts/build-sdk.ts
Normal file
35
scripts/build-sdk.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import path from 'node:path';
|
||||||
|
import appPkg from '../package.json';
|
||||||
|
import sdkTsConfig from './sdk/sdk.tsconfig.json';
|
||||||
|
import sdkPackage from './sdk/package.json';
|
||||||
|
import { emptyDir } from 'fs-extra';
|
||||||
|
import { generateDtsBundle } from 'dts-bundle-generator';
|
||||||
|
|
||||||
|
async function generateApiDeclarations ()
|
||||||
|
{
|
||||||
|
const tmpConfigPath = "./scripts/sdk/sdk.tsconfig.json";
|
||||||
|
const outDir = path.join(path.dirname(tmpConfigPath), sdkTsConfig.compilerOptions.outDir);
|
||||||
|
await emptyDir(outDir);
|
||||||
|
|
||||||
|
const results = generateDtsBundle([{
|
||||||
|
filePath: './scripts/sdk/sdk.ts',
|
||||||
|
output: {
|
||||||
|
inlineDeclareGlobals: true,
|
||||||
|
sortNodes: true,
|
||||||
|
}
|
||||||
|
},], { preferredConfigPath: './scripts/sdk/sdk.tsconfig.json' });
|
||||||
|
|
||||||
|
await Bun.write('./dist-sdk/index.d.ts', results);
|
||||||
|
|
||||||
|
const pkg = {
|
||||||
|
...sdkPackage,
|
||||||
|
license: appPkg.license,
|
||||||
|
version: appPkg.version,
|
||||||
|
repository: appPkg.repository,
|
||||||
|
author: appPkg.author,
|
||||||
|
peerDependencies: appPkg.dependencies
|
||||||
|
};
|
||||||
|
await Bun.write(path.join(outDir, '..', 'package.json'), JSON.stringify(pkg, null, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
await generateApiDeclarations();
|
||||||
|
|
@ -22,7 +22,7 @@ function spawnServer ()
|
||||||
stderr: 'inherit',
|
stderr: 'inherit',
|
||||||
stdin: 'inherit',
|
stdin: 'inherit',
|
||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
killSignal: 'SIGUSR1',
|
killSignal: 'SIGKILL',
|
||||||
ipc (message, subprocess, handle)
|
ipc (message, subprocess, handle)
|
||||||
{
|
{
|
||||||
if (message === 'focus')
|
if (message === 'focus')
|
||||||
|
|
@ -91,7 +91,7 @@ if (!process.env.HEADLESS)
|
||||||
spawnBrowser()?.then(async e =>
|
spawnBrowser()?.then(async e =>
|
||||||
{
|
{
|
||||||
if (!server) return;
|
if (!server) return;
|
||||||
server.kill("SIGUSR1");
|
abortController.abort();
|
||||||
await server.exited;
|
await server.exited;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import audioSprite from 'audiosprite';
|
import audioSprite from 'audiosprite';
|
||||||
import { $ } from 'bun';
|
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { soundMap } from '../src/mainview/scripts/audio/audioConstants';
|
import { soundMap } from '../src/mainview/scripts/audio/audioConstants';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { $ } from "bun";
|
import { $ } from "bun";
|
||||||
|
|
||||||
const lockfile = Bun.argv[2] ?? "bun.lockb";
|
|
||||||
const output = Bun.argv[3] ?? ".config/flatpak/sources.gen.json";
|
const output = Bun.argv[3] ?? ".config/flatpak/sources.gen.json";
|
||||||
|
|
||||||
const text = await $`bun ./bun.lockb --hash: 0000000000000000-0000000000000000-0000000000000000-0000000000000000`.text();
|
const text = await $`bun ./bun.lockb --hash: 0000000000000000-0000000000000000-0000000000000000-0000000000000000`.text();
|
||||||
|
|
|
||||||
4
scripts/sdk/package.json
Normal file
4
scripts/sdk/package.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "gameflow-sdk",
|
||||||
|
"types": "index.d.ts"
|
||||||
|
}
|
||||||
18
scripts/sdk/sdk.ts
Normal file
18
scripts/sdk/sdk.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { SettingsType } from '@/shared/constants';
|
||||||
|
import Conf from 'conf';
|
||||||
|
import { AppEventMap } from '../../src/bun/types/types';
|
||||||
|
import EventEmitter from "node:events";
|
||||||
|
import { TaskQueue } from '@/bun/api/task-queue';
|
||||||
|
|
||||||
|
export * from '../../src/bun/types/types.schema';
|
||||||
|
export * from '../../src/bun/types/types';
|
||||||
|
export * from '../../src/bun/api/hooks/app';
|
||||||
|
export * from '../../src/shared/constants';
|
||||||
|
export * from '../../src/shared/types';
|
||||||
|
export * from '../../src/shared/utils';
|
||||||
|
|
||||||
|
export declare const config: Conf<SettingsType>;
|
||||||
|
export declare let events: EventEmitter<AppEventMap>;
|
||||||
|
export declare let taskQueue: TaskQueue;
|
||||||
|
|
||||||
|
export { };
|
||||||
47
scripts/sdk/sdk.tsconfig.json
Normal file
47
scripts/sdk/sdk.tsconfig.json
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": [
|
||||||
|
"ES2024"
|
||||||
|
],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"declaration": true,
|
||||||
|
"strict": true,
|
||||||
|
"outDir": "../../dist-sdk/sdk",
|
||||||
|
"types": [
|
||||||
|
"node"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"../../src/*"
|
||||||
|
],
|
||||||
|
"~/*": [
|
||||||
|
"../../*"
|
||||||
|
],
|
||||||
|
"@shared/*": [
|
||||||
|
"../../src/shared/*"
|
||||||
|
],
|
||||||
|
"@clients/*": [
|
||||||
|
"../../src/clients/*"
|
||||||
|
],
|
||||||
|
"@schema/*": [
|
||||||
|
"../../src/bun/api/schema/*"
|
||||||
|
],
|
||||||
|
"@queries/*": [
|
||||||
|
"../../src/mainview/scripts/queries/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"../../src/bun/api/hooks",
|
||||||
|
"../../src/bun/types",
|
||||||
|
"../../src/shared"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -24,6 +24,7 @@ import controls from './controls/controls';
|
||||||
import { RunAPIServer } from "./rpc";
|
import { RunAPIServer } from "./rpc";
|
||||||
import { RunBunServer } from "../server";
|
import { RunBunServer } from "../server";
|
||||||
import ReloadPluginsJob from "./jobs/reload-plugins-job";
|
import ReloadPluginsJob from "./jobs/reload-plugins-job";
|
||||||
|
import { AppEventMap } from "../types/types";
|
||||||
|
|
||||||
export let config: Conf<SettingsType>;
|
export let config: Conf<SettingsType>;
|
||||||
export let customEmulators: Conf<Record<string, string>>;
|
export let customEmulators: Conf<Record<string, string>>;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import si from 'systeminformation';
|
import si from 'systeminformation';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
|
import { Drive } from '@/shared/types';
|
||||||
|
|
||||||
async function getAccess (path: string)
|
async function getAccess (path: string)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import z from "zod";
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { config, events, plugins } from "../app";
|
import { config, events, plugins } from "../app";
|
||||||
import { getLocalGame, updateLocalLastPlayed } from "../games/services/statusService";
|
import { getLocalGame, updateLocalLastPlayed } from "../games/services/statusService";
|
||||||
|
import { SaveFileChange } from "@/shared/types";
|
||||||
|
|
||||||
// TODO: use the retroarch cores based on ES-DE
|
// TODO: use the retroarch cores based on ES-DE
|
||||||
export const cores: Record<string, string> = {
|
export const cores: Record<string, string> = {
|
||||||
|
|
@ -83,7 +84,7 @@ export default new Elysia({ prefix: '/emulatorjs' })
|
||||||
await plugins.hooks.games.postPlay.promise({
|
await plugins.hooks.games.postPlay.promise({
|
||||||
source,
|
source,
|
||||||
id,
|
id,
|
||||||
saveFolderPath: path.join(config.get('downloadPath'), "saves", "EMULATORJS"),
|
saveFolderSlots: { 'emulatorjs': { cwd: path.join(config.get('downloadPath'), "saves", "EMULATORJS") } },
|
||||||
gameInfo: { platformSlug: localGame?.platform.slug },
|
gameInfo: { platformSlug: localGame?.platform.slug },
|
||||||
changedSaveFiles: [],
|
changedSaveFiles: [],
|
||||||
validChangedSaveFiles: changedSaveFiles,
|
validChangedSaveFiles: changedSaveFiles,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import Elysia, { status } from "elysia";
|
import Elysia, { status } from "elysia";
|
||||||
import { plugins } from "../app";
|
import { plugins } from "../app";
|
||||||
|
import { FrontEndCollection } from "@/shared/types";
|
||||||
|
|
||||||
export default new Elysia()
|
export default new Elysia()
|
||||||
.get('/collections', async () =>
|
.get('/collections', async () =>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import { LaunchGameJob } from "../jobs/launch-game-job";
|
||||||
import { cores } from "../emulatorjs/emulatorjs";
|
import { cores } from "../emulatorjs/emulatorjs";
|
||||||
import { findEmulatorPluginIntegration } from "../store/services/emulatorsService";
|
import { findEmulatorPluginIntegration } from "../store/services/emulatorsService";
|
||||||
import { ImportJob } from "../jobs/import-job";
|
import { ImportJob } from "../jobs/import-job";
|
||||||
|
import { EmulatorSourceEntryType, EmulatorSystem, FrontEndFilterLists, FrontEndFilterSets, FrontEndGameType, FrontEndGameTypeDetailedEmulator, FrontEndGameTypeWithIds, FrontEndId, GameLookup } from "@/shared/types";
|
||||||
|
|
||||||
// A custom jimp that supports webp
|
// A custom jimp that supports webp
|
||||||
const Jimp = createJimp({
|
const Jimp = createJimp({
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { and, count, eq, getTableColumns, not, notExists, or } from "drizzle-orm
|
||||||
import { config, db, plugins } from "../app";
|
import { config, db, plugins } from "../app";
|
||||||
import * as schema from "@schema/app";
|
import * as schema from "@schema/app";
|
||||||
import { findPlatform } from "./services/utils";
|
import { findPlatform } from "./services/utils";
|
||||||
|
import { FrontEndPlatformType } from "@/shared/types";
|
||||||
|
|
||||||
export default new Elysia()
|
export default new Elysia()
|
||||||
.get('/platforms', async () =>
|
.get('/platforms', async () =>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { config, taskQueue } from '../../app';
|
||||||
import { LaunchGameJob } from '../../jobs/launch-game-job';
|
import { LaunchGameJob } from '../../jobs/launch-game-job';
|
||||||
import { getStoreEmulatorPackage } from '../../store/services/gamesService';
|
import { getStoreEmulatorPackage } from '../../store/services/gamesService';
|
||||||
import { getOrCachedScoopPackage } from '../../store/services/emulatorsService';
|
import { getOrCachedScoopPackage } from '../../store/services/emulatorsService';
|
||||||
|
import { CommandEntry, EmulatorSourceEntryType, FrontEndId } from '@/shared/types';
|
||||||
|
|
||||||
export async function launchCommand (validCommand: CommandEntry, id: FrontEndId, source?: string, sourceId?: string)
|
export async function launchCommand (validCommand: CommandEntry, id: FrontEndId, source?: string, sourceId?: string)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { LaunchGameJob } from "../../jobs/launch-game-job";
|
||||||
import * as appSchema from "@schema/app";
|
import * as appSchema from "@schema/app";
|
||||||
import { DownloadSourceSchema, RPC_URL } from "@/shared/constants";
|
import { DownloadSourceSchema, RPC_URL } from "@/shared/constants";
|
||||||
import { host } from "@/bun/utils/host";
|
import { host } from "@/bun/utils/host";
|
||||||
|
import { CommandEntry, FrontEndId, GameLookup, GameStatusType, LocalDownloadFileEntry } from "@/shared/types";
|
||||||
|
|
||||||
export class CommandSearchError extends Error
|
export class CommandSearchError extends Error
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { RPC_URL } from "@shared/constants";
|
||||||
import { hashFile } from "@/bun/utils";
|
import { hashFile } from "@/bun/utils";
|
||||||
import { host } from "@/bun/utils/host";
|
import { host } from "@/bun/utils/host";
|
||||||
import * as emulatorSchema from "@schema/emulators";
|
import * as emulatorSchema from "@schema/emulators";
|
||||||
|
import { DownloadFileEntry, FrontEndGameType, FrontEndGameTypeDetailed, GameLookup, LocalDownloadFileEntry, LocalGameMetadata } from "@/shared/types";
|
||||||
|
|
||||||
export async function calculateSize (installPath: string | null)
|
export async function calculateSize (installPath: string | null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { AuthHooks } from "./auth";
|
import AuthHooks from "./auth";
|
||||||
import { EmulatorHooks } from "./emulators";
|
import EmulatorHooks from "./emulators";
|
||||||
import { GameHooks } from "./games";
|
import GameHooks from "./games";
|
||||||
import { StoreHooks } from "./store";
|
import StoreHooks from "./store";
|
||||||
|
|
||||||
export class GameflowHooks
|
export default class GameflowHooks
|
||||||
{
|
{
|
||||||
games = new GameHooks();
|
games = new GameHooks();
|
||||||
emulators = new EmulatorHooks();
|
emulators = new EmulatorHooks();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
|
import { DownloadFileEntry } from "@/shared/types";
|
||||||
import { AsyncSeriesHook } from "tapable";
|
import { AsyncSeriesHook } from "tapable";
|
||||||
|
|
||||||
export class AuthHooks
|
export default class AuthHooks
|
||||||
{
|
{
|
||||||
loginComplete = new AsyncSeriesHook<[ctx: {
|
loginComplete = new AsyncSeriesHook<[ctx: {
|
||||||
service: string;
|
service: string;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,8 @@
|
||||||
import { EmulatorDownloadInfoType, EmulatorPackageType } from "@/shared/constants";
|
import { EmulatorPostInstallContext } from "@/bun/types/types";
|
||||||
|
import { DownloadFileEntry, EmulatorSourceEntryType, EmulatorSystem } from "@/shared/types";
|
||||||
import { AsyncSeriesBailHook, AsyncSeriesHook } from "tapable";
|
import { AsyncSeriesBailHook, AsyncSeriesHook } from "tapable";
|
||||||
|
|
||||||
interface EmulatorPostInstallContext
|
export default class EmulatorHooks
|
||||||
{
|
|
||||||
emulator: string;
|
|
||||||
emulatorPackage?: EmulatorPackageType;
|
|
||||||
path: string;
|
|
||||||
update: boolean;
|
|
||||||
info: EmulatorDownloadInfoType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EmulatorHooks
|
|
||||||
{
|
{
|
||||||
fetchBiosDownload = new AsyncSeriesBailHook<[ctx: {
|
fetchBiosDownload = new AsyncSeriesBailHook<[ctx: {
|
||||||
emulator: string;
|
emulator: string;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { EmulatorPackageType, GameListFilterType } from '@/shared/constants';
|
import { EmulatorPackageType, GameListFilterType } from '@/shared/constants';
|
||||||
import { SyncBailHook, AsyncSeriesHook, AsyncSeriesBailHook } from 'tapable';
|
import { CommandEntry, DownloadInfo, EmulatorSourceEntryType, EmulatorSupport, EmulatorSystem, FrontEndCollection, FrontEndFilterSets, FrontEndGameType, FrontEndGameTypeDetailed, FrontEndGameTypeWithIds, FrontEndId, FrontEndPlatformType, GameLookup, SaveFileChange, SaveSlots } from '@/shared/types';
|
||||||
|
import { SyncBailHook, AsyncSeriesHook, AsyncSeriesBailHook, Hook } from 'tapable';
|
||||||
|
|
||||||
export class GameHooks
|
export default class GameHooks
|
||||||
{
|
{
|
||||||
buildLaunchCommands = new AsyncSeriesBailHook<[ctx: {
|
buildLaunchCommands = new AsyncSeriesBailHook<[ctx: {
|
||||||
source: string | null;
|
source: string | null;
|
||||||
|
|
@ -121,7 +122,7 @@ export class GameHooks
|
||||||
postPlay = new AsyncSeriesHook<[ctx: {
|
postPlay = new AsyncSeriesHook<[ctx: {
|
||||||
source: string,
|
source: string,
|
||||||
id: string;
|
id: string;
|
||||||
saveFolderSlots?: Record<string, { cwd: string; }>;
|
saveFolderSlots?: SaveSlots;
|
||||||
changedSaveFiles: { subPath: string, cwd: string; }[],
|
changedSaveFiles: { subPath: string, cwd: string; }[],
|
||||||
validChangedSaveFiles: Record<string, SaveFileChange>,
|
validChangedSaveFiles: Record<string, SaveFileChange>,
|
||||||
command: CommandEntry;
|
command: CommandEntry;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { EmulatorDownloadInfoType } from "@/shared/constants";
|
import { EmulatorDownloadInfoType } from "@/shared/constants";
|
||||||
|
import { FrontEndEmulator, FrontEndEmulatorDetailed, FrontEndGameTypeDetailed } from "@/shared/types";
|
||||||
import { AsyncSeriesBailHook, AsyncSeriesHook } from "tapable";
|
import { AsyncSeriesBailHook, AsyncSeriesHook } from "tapable";
|
||||||
|
|
||||||
export class StoreHooks
|
export default class StoreHooks
|
||||||
{
|
{
|
||||||
fetchFeaturedGames = new AsyncSeriesHook<[ctx: { games: FrontEndGameTypeDetailed[]; }]>(['ctx']);
|
fetchFeaturedGames = new AsyncSeriesHook<[ctx: { games: FrontEndGameTypeDetailed[]; }]>(['ctx']);
|
||||||
fetchEmulators = new AsyncSeriesHook<[ctx: { emulators: FrontEndEmulator[]; search?: string; }]>(['ctx']);
|
fetchEmulators = new AsyncSeriesHook<[ctx: { emulators: FrontEndEmulator[]; search?: string; }]>(['ctx']);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { simulateProgress } from "@/bun/utils";
|
||||||
import { path7za } from "7zip-bin";
|
import { path7za } from "7zip-bin";
|
||||||
import { getEmulatorDownload, getEmulatorPath } from "../store/services/emulatorsService";
|
import { getEmulatorDownload, getEmulatorPath } from "../store/services/emulatorsService";
|
||||||
import { $ } from "bun";
|
import { $ } from "bun";
|
||||||
|
import { EmulatorSourceEntryType } from "@/shared/types";
|
||||||
|
|
||||||
type EmulatorDownloadStates = "download" | "extract";
|
type EmulatorDownloadStates = "download" | "extract";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { createLocalGame } from "../games/services/utils";
|
||||||
import { IJob, JobContext } from "../task-queue";
|
import { IJob, JobContext } from "../task-queue";
|
||||||
import * as schema from "@schema/app";
|
import * as schema from "@schema/app";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
import { GameLookup } from "@/shared/types";
|
||||||
|
|
||||||
export class ImportJob implements IJob<z.infer<typeof ImportJob.dataSchema>, string>
|
export class ImportJob implements IJob<z.infer<typeof ImportJob.dataSchema>, string>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { ensureDir, move } from "fs-extra";
|
||||||
import { path7za } from "7zip-bin";
|
import { path7za } from "7zip-bin";
|
||||||
import StreamZip from 'node-stream-zip';
|
import StreamZip from 'node-stream-zip';
|
||||||
import { which } from "bun";
|
import { which } from "bun";
|
||||||
|
import { DownloadInfo } from "@/shared/types";
|
||||||
|
|
||||||
interface JobConfig
|
interface JobConfig
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { IJob, JobContext } from "../task-queue";
|
import { IJob, JobContext } from "../task-queue";
|
||||||
import { ActiveGameSchema, ActiveGameType } from "@/bun/types/typesc.schema";
|
import { ActiveGameSchema, ActiveGameType } from "@/bun/types/types.schema";
|
||||||
import { config, db, events, plugins } from "../app";
|
import { config, db, events, plugins } from "../app";
|
||||||
import * as appSchema from "@schema/app";
|
import * as appSchema from "@schema/app";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { spawn } from 'node:child_process';
|
import { spawn } from 'node:child_process';
|
||||||
import fs from "node:fs/promises";
|
|
||||||
import { updateLocalLastPlayed } from "../games/services/statusService";
|
import { updateLocalLastPlayed } from "../games/services/statusService";
|
||||||
import { getErrorMessage } from "@/bun/utils";
|
import { getErrorMessage } from "@/bun/utils";
|
||||||
|
import { CommandEntry, FrontEndId, SaveSlots } from "@/shared/types";
|
||||||
|
|
||||||
export class LaunchGameJob implements IJob<z.infer<typeof LaunchGameJob.dataSchema>, string>
|
export class LaunchGameJob implements IJob<z.infer<typeof LaunchGameJob.dataSchema>, string>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { IJob, JobContext } from "../task-queue";
|
import { IJob, JobContext } from "../task-queue";
|
||||||
import { cleanPromise, cleanup, events, plugins } from "../app";
|
import { events } from "../app";
|
||||||
import fs from 'fs/promises';
|
|
||||||
import { Downloader } from "@/bun/utils/downloader";
|
import { Downloader } from "@/bun/utils/downloader";
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
import { FrontendNotification } from '@/shared/types';
|
||||||
import { events } from './app';
|
import { events } from './app';
|
||||||
|
|
||||||
export default function buildNotificationsStream ()
|
export default function buildNotificationsStream ()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { PluginContextType, PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { config } from "@/bun/api/app";
|
import { config } from "@/bun/api/app";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
import { config } from "@/bun/api/app";
|
import { config } from "@/bun/api/app";
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import { ensureDir } from "fs-extra";
|
import { ensureDir } from "fs-extra";
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
|
|
||||||
import { config } from "@/bun/api/app";
|
import { config } from "@/bun/api/app";
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import defaultConfig from './PCSX2.ini' with { type: 'file' };
|
import defaultConfig from './PCSX2.ini' with { type: 'file' };
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { ensureDir } from "fs-extra";
|
import { ensureDir } from "fs-extra";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import ini from 'ini';
|
import ini from 'ini';
|
||||||
|
import { EmulatorCapabilities } from "@/shared/types";
|
||||||
|
|
||||||
export default class PCSX2Integration implements PluginType
|
export default class PCSX2Integration implements PluginType
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import { config } from "@/bun/api/app";
|
import { config } from "@/bun/api/app";
|
||||||
import configFilePathWin32 from './win32/ppsspp.ini' with { type: 'file' };
|
import configFilePathWin32 from './win32/ppsspp.ini' with { type: 'file' };
|
||||||
|
|
@ -11,6 +11,7 @@ import { ensureDir } from "fs-extra";
|
||||||
import { homedir } from "node:os";
|
import { homedir } from "node:os";
|
||||||
import ini from 'ini';
|
import ini from 'ini';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
|
import { EmulatorCapabilities } from "@/shared/types";
|
||||||
|
|
||||||
export default class PPSSPPIntegration implements PluginType
|
export default class PPSSPPIntegration implements PluginType
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import { config } from "@/bun/api/app";
|
import { config } from "@/bun/api/app";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { platform } from "os";
|
|
||||||
|
|
||||||
const SECTOR_SIZE = 0x800;
|
const SECTOR_SIZE = 0x800;
|
||||||
const MAGIC = "MICROSOFT*XBOX*MEDIA";
|
const MAGIC = "MICROSOFT*XBOX*MEDIA";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import { GameflowHooks } from "@/bun/api/hooks/app";
|
import GameflowHooks from "@/bun/api/hooks/app";
|
||||||
import { config } from "@/bun/api/app";
|
import { config } from "@/bun/api/app";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { ensureDir } from "fs-extra";
|
import { ensureDir } from "fs-extra";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import { config, customEmulators, db, emulatorsDb } from "@/bun/api/app";
|
import { config, customEmulators, db, emulatorsDb } from "@/bun/api/app";
|
||||||
import * as emulatorSchema from '@schema/emulators';
|
import * as emulatorSchema from '@schema/emulators';
|
||||||
|
|
@ -13,6 +13,7 @@ import { findStoreEmulatorExec } from "@/bun/api/games/services/launchGameServic
|
||||||
import { which } from "bun";
|
import { which } from "bun";
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import { getLocalGameMatch } from "@/bun/api/games/services/utils";
|
import { getLocalGameMatch } from "@/bun/api/games/services/utils";
|
||||||
|
import { CommandEntry, EmulatorSourceEntryType } from "@/shared/types";
|
||||||
|
|
||||||
export default class IgdbIntegration implements PluginType
|
export default class IgdbIntegration implements PluginType
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import { config, db, events } from "@/bun/api/app";
|
import { config, db, events } from "@/bun/api/app";
|
||||||
import path, { dirname } from 'node:path';
|
import path from 'node:path';
|
||||||
import unzip from 'unzip-stream';
|
import unzip from 'unzip-stream';
|
||||||
import { chmodSync, ensureDir } from "fs-extra";
|
import { ensureDir } from "fs-extra";
|
||||||
import { Readable } from "node:stream";
|
import { Readable } from "node:stream";
|
||||||
import { pipeline } from "node:stream/promises";
|
import { pipeline } from "node:stream/promises";
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import { randomUUIDv7, sleep } from "bun";
|
import { randomUUIDv7 } from "bun";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { createInterface } from "node:readline";
|
import { createInterface } from "node:readline";
|
||||||
import { getLocalGameMatch } from "@/bun/api/games/services/utils";
|
import { getLocalGameMatch } from "@/bun/api/games/services/utils";
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import secrets from "@/bun/api/secrets";
|
import secrets from "@/bun/api/secrets";
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
import * as igdb from '@phalcode/ts-igdb-client';
|
import * as igdb from '@phalcode/ts-igdb-client';
|
||||||
import { checkLoginAndRefreshTwitch } from "@/bun/api/auth";
|
import { checkLoginAndRefreshTwitch } from "@/bun/api/auth";
|
||||||
|
import { GameLookup } from "@/shared/types";
|
||||||
|
|
||||||
export default class IgdbIntegration implements PluginType
|
export default class IgdbIntegration implements PluginType
|
||||||
{
|
{
|
||||||
|
|
@ -80,7 +81,7 @@ export default class IgdbIntegration implements PluginType
|
||||||
first_release_date: g.first_release_date ? g.first_release_date * 1000 : undefined,
|
first_release_date: g.first_release_date ? g.first_release_date * 1000 : undefined,
|
||||||
average_rating: g.rating ?? undefined,
|
average_rating: g.rating ?? undefined,
|
||||||
keywords: g.keywords?.map(k => k.name!) ?? [],
|
keywords: g.keywords?.map(k => k.name!) ?? [],
|
||||||
igdb_id: g.id,
|
igdb_id: g.id ?? undefined,
|
||||||
platforms: g.platforms?.map(p => ({ id: p.id!, name: p.abbreviation, displayName: p.name!, slug: p.slug! })) ?? [],
|
platforms: g.platforms?.map(p => ({ id: p.id!, name: p.abbreviation, displayName: p.name!, slug: p.slug! })) ?? [],
|
||||||
slug: g.slug
|
slug: g.slug
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import { DetailedRomSchema, getCollectionApiCollectionsIdGet, getCollectionsApiCollectionsGet, getCurrentUserApiUsersMeGet, getPlatformApiPlatformsIdGet, getPlatformFirmwareApiFirmwareGet, getPlatformsApiPlatformsGet, getRomApiRomsIdGet, getRomByMetadataProviderApiRomsByMetadataProviderGet, getRomContentApiRomsIdContentFileNameGet, getRomFiltersApiRomsFiltersGet, getRomsApiRomsGet, getSavesSummaryApiSavesSummaryGet, PlatformSchema, SimpleRomSchema, updateRomUserApiRomsIdPropsPut } from "@/clients/romm";
|
import { DetailedRomSchema, getCollectionApiCollectionsIdGet, getCollectionsApiCollectionsGet, getCurrentUserApiUsersMeGet, getPlatformApiPlatformsIdGet, getPlatformFirmwareApiFirmwareGet, getPlatformsApiPlatformsGet, getRomApiRomsIdGet, getRomByMetadataProviderApiRomsByMetadataProviderGet, getRomContentApiRomsIdContentFileNameGet, getRomFiltersApiRomsFiltersGet, getRomsApiRomsGet, getSavesSummaryApiSavesSummaryGet, PlatformSchema, SimpleRomSchema, updateRomUserApiRomsIdPropsPut } from "@/clients/romm";
|
||||||
import { config, events } from "@/bun/api/app";
|
import { config, events } from "@/bun/api/app";
|
||||||
|
|
@ -14,6 +14,7 @@ import { client } from "@/clients/romm/client.gen";
|
||||||
import { validateGameSource } from "@/bun/api/games/services/statusService";
|
import { validateGameSource } from "@/bun/api/games/services/statusService";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { checkLoginAndRefreshRomm } from "@/bun/api/auth";
|
import { checkLoginAndRefreshRomm } from "@/bun/api/auth";
|
||||||
|
import { DownloadFileEntry, DownloadInfo, FrontEndCollection, FrontEndGameType, FrontEndGameTypeDetailed, FrontEndGameTypeDetailedAchievement, FrontEndGameTypeWithIds, FrontEndPlatformType } from "@/shared/types";
|
||||||
|
|
||||||
const SettingsSchema = z.object({
|
const SettingsSchema = z.object({
|
||||||
savesSync: z.boolean().default(false).describe("Experimental save sync support")
|
savesSync: z.boolean().default(false).describe("Experimental save sync support")
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { shuffleInPlace } from "@/bun/utils";
|
||||||
import mustache from "mustache";
|
import mustache from "mustache";
|
||||||
import { getEmulatorDownload, getEmulatorPath } from "@/bun/api/store/services/emulatorsService";
|
import { getEmulatorDownload, getEmulatorPath } from "@/bun/api/store/services/emulatorsService";
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
|
import { CommandEntry, EmulatorSourceEntryType, EmulatorSystem, FrontEndEmulator, FrontEndFilterSets, FrontEndGameType, FrontEndGameTypeDetailed, SaveFileChange } from "@/shared/types";
|
||||||
|
|
||||||
export async function getStoreGames (gamesManifest: any[], filter?: { limit?: number; offset?: number; })
|
export async function getStoreGames (gamesManifest: any[], filter?: { limit?: number; offset?: number; })
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { PluginLoadingContextType, PluginType } from "@/bun/types/typesc.schema";
|
import { PluginLoadingContextType, PluginType } from "@/bun/types/types.schema";
|
||||||
import desc from './package.json';
|
import desc from './package.json';
|
||||||
import path, { } from 'node:path';
|
import path, { } from 'node:path';
|
||||||
import { buildStoreFrontendEmulatorSystems, getAllStoreEmulatorPackages, getStoreEmulatorPackage, getStoreFolder } from "@/bun/api/store/services/gamesService";
|
import { buildStoreFrontendEmulatorSystems, getAllStoreEmulatorPackages, getStoreEmulatorPackage, getStoreFolder } from "@/bun/api/store/services/gamesService";
|
||||||
|
|
@ -12,6 +12,7 @@ import { getSourceGameDetailed } from "@/bun/api/games/services/utils";
|
||||||
import UpdateStoreJob from "@/bun/api/jobs/update-store";
|
import UpdateStoreJob from "@/bun/api/jobs/update-store";
|
||||||
import { getEmulatorDownload, getEmulatorPath } from "@/bun/api/store/services/emulatorsService";
|
import { getEmulatorDownload, getEmulatorPath } from "@/bun/api/store/services/emulatorsService";
|
||||||
import { buildFilters, buildLaunchCommand, buildSaves, convertStoreEmulatorToFrontend, convertStoreToFrontend, convertStoreToFrontendDetailed, getExistingStoreEmulatorDownload, getShuffledStoreGames, getStoreGame, getValidDownloads } from "./services";
|
import { buildFilters, buildLaunchCommand, buildSaves, convertStoreEmulatorToFrontend, convertStoreToFrontend, convertStoreToFrontendDetailed, getExistingStoreEmulatorDownload, getShuffledStoreGames, getStoreGame, getValidDownloads } from "./services";
|
||||||
|
import { DownloadInfo, FrontEndEmulatorDetailed, FrontEndGameTypeWithIds } from "@/shared/types";
|
||||||
|
|
||||||
export default class RommIntegration implements PluginType
|
export default class RommIntegration implements PluginType
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { GameflowHooks } from "../hooks/app";
|
import GameflowHooks from "../hooks/app";
|
||||||
import { PluginDescriptionType, PluginLoadingContextType, PluginType } from "../../types/typesc.schema";
|
import { PluginDescriptionType, PluginLoadingContextType, PluginType } from "../../types/types.schema";
|
||||||
import { config } from "../app";
|
import { config } from "../app";
|
||||||
import Conf from "conf";
|
import Conf from "conf";
|
||||||
import projectPackage from '~/package.json';
|
import projectPackage from '~/package.json';
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { EventEmitter } from "node:stream";
|
import { PluginSourceType } from "@/shared/types";
|
||||||
|
|
||||||
export const pluginZodRegistry = z.registry<{
|
export const pluginZodRegistry = z.registry<{
|
||||||
requiresRestart?: boolean;
|
requiresRestart?: boolean;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { plugins, taskQueue } from "../app";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { toggleElementInConfig } from "@/bun/utils";
|
import { toggleElementInConfig } from "@/bun/utils";
|
||||||
import ReloadPluginsJob from "../jobs/reload-plugins-job";
|
import ReloadPluginsJob from "../jobs/reload-plugins-job";
|
||||||
|
import { FrontendPlugin } from "@/shared/types";
|
||||||
|
|
||||||
export default new Elysia({ prefix: '/plugins' })
|
export default new Elysia({ prefix: '/plugins' })
|
||||||
.get('/', async () =>
|
.get('/', async () =>
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,15 @@ import igdb from './builtin/sources/com.simeonradivoev.gameflow.igdb/package.jso
|
||||||
import store from './builtin/sources/com.simeonradivoev.gameflow.store/package.json';
|
import store from './builtin/sources/com.simeonradivoev.gameflow.store/package.json';
|
||||||
import es from './builtin/launchers/com.simeonradivoev.gameflow.es/package.json';
|
import es from './builtin/launchers/com.simeonradivoev.gameflow.es/package.json';
|
||||||
import rclone from './builtin/other/com.simeonradivoev.gameflow.rclone/package.json';
|
import rclone from './builtin/other/com.simeonradivoev.gameflow.rclone/package.json';
|
||||||
import { PluginDescriptionSchema, PluginDescriptionType, PluginSchema } from "@/bun/types/typesc.schema";
|
import { PluginDescriptionSchema, PluginDescriptionType, PluginSchema } from "@/bun/types/types.schema";
|
||||||
|
import path from 'node:path';
|
||||||
|
import { getStoreRootFolder } from "../store/services/gamesService";
|
||||||
|
|
||||||
|
type PluginEntry = PluginDescriptionType & { load: () => Promise<any>; };
|
||||||
|
|
||||||
export default async function register (pluginManager: PluginManager)
|
export default async function register (pluginManager: PluginManager)
|
||||||
{
|
{
|
||||||
const plugins: (PluginDescriptionType & { main: string; load: () => Promise<any>; })[] = [
|
const plugins: PluginEntry[] = [
|
||||||
{ ...pcsx2, load: () => import('./builtin/emulators/com.simeonradivoev.gameflow.pcsx2/pcsx2') },
|
{ ...pcsx2, load: () => import('./builtin/emulators/com.simeonradivoev.gameflow.pcsx2/pcsx2') },
|
||||||
{ ...ppsspp, load: () => import('./builtin/emulators/com.simeonradivoev.gameflow.ppsspp/ppsspp') },
|
{ ...ppsspp, load: () => import('./builtin/emulators/com.simeonradivoev.gameflow.ppsspp/ppsspp') },
|
||||||
{ ...dolphin, load: () => import('./builtin/emulators/com.simeonradivoev.gameflow.dolphin/dolphin') },
|
{ ...dolphin, load: () => import('./builtin/emulators/com.simeonradivoev.gameflow.dolphin/dolphin') },
|
||||||
|
|
@ -29,6 +33,33 @@ export default async function register (pluginManager: PluginManager)
|
||||||
{ ...rclone, load: () => import('./builtin/other/com.simeonradivoev.gameflow.rclone/rclone') },
|
{ ...rclone, load: () => import('./builtin/other/com.simeonradivoev.gameflow.rclone/rclone') },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const storePackageFile = path.join(getStoreRootFolder(), 'package.json');
|
||||||
|
const storePackage = await Bun.file(storePackageFile).json();
|
||||||
|
|
||||||
|
if (storePackage.dependencies)
|
||||||
|
{
|
||||||
|
const storePlugins = await Promise.all(Object.keys(storePackage.dependencies).map(async p =>
|
||||||
|
{
|
||||||
|
const pluginPath = path.join(getStoreRootFolder(), 'node_modules', p);
|
||||||
|
const pluginPackageFile = Bun.file(path.join(pluginPath, 'package.json'));
|
||||||
|
if (await pluginPackageFile.exists())
|
||||||
|
{
|
||||||
|
const pluginPackage = await PluginDescriptionSchema.safeParseAsync(await pluginPackageFile.json());
|
||||||
|
if (pluginPackage.success)
|
||||||
|
{
|
||||||
|
const mainPath = path.join(pluginPath, pluginPackage.data.main);
|
||||||
|
if (await Bun.file(mainPath).exists())
|
||||||
|
{
|
||||||
|
const entry: PluginEntry = { ...pluginPackage.data, load: () => import(mainPath) };
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
plugins.push(...storePlugins.filter(p => !!p));
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(plugins.filter(p =>
|
await Promise.all(plugins.filter(p =>
|
||||||
{
|
{
|
||||||
if (process.env.PLUGIN_WHITELIST && !process.env.PLUGIN_WHITELIST.includes(p.name))
|
if (process.env.PLUGIN_WHITELIST && !process.env.PLUGIN_WHITELIST.includes(p.name))
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { LocalGameMetadata } from "@/shared/types";
|
||||||
import { sql, relations } from "drizzle-orm";
|
import { sql, relations } from "drizzle-orm";
|
||||||
import { integer, text, sqliteTable, blob } from "drizzle-orm/sqlite-core";
|
import { integer, text, sqliteTable, blob } from "drizzle-orm/sqlite-core";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { cores } from '../emulatorjs/emulatorjs';
|
||||||
import { SERVER_URL } from '@/shared/constants';
|
import { SERVER_URL } from '@/shared/constants';
|
||||||
import { host } from '@/bun/utils/host';
|
import { host } from '@/bun/utils/host';
|
||||||
import { findEmulatorPluginIntegration } from '../store/services/emulatorsService';
|
import { findEmulatorPluginIntegration } from '../store/services/emulatorsService';
|
||||||
|
import { EmulatorSourceEntryType, FrontEndEmulator } from '@/shared/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get emulators based on local games. Only the ones we probably need.
|
* Get emulators based on local games. Only the ones we probably need.
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { EmulatorDownloadInfoType, EmulatorPackageType, ScoopPackageSchema } fro
|
||||||
import { config, plugins } from "../../app";
|
import { config, plugins } from "../../app";
|
||||||
import { getOrCached, getOrCachedGithubRelease } from "../../cache";
|
import { getOrCached, getOrCachedGithubRelease } from "../../cache";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
import { EmulatorSourceEntryType, EmulatorSupport } from "@/shared/types";
|
||||||
|
|
||||||
export function findEmulatorPluginIntegration (name: string, validSources: (EmulatorSourceEntryType | undefined)[]): EmulatorSupport[]
|
export function findEmulatorPluginIntegration (name: string, validSources: (EmulatorSourceEntryType | undefined)[]): EmulatorSupport[]
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
import { EmulatorPackageSchema, EmulatorPackageType, GithubManifestSchema, StoreGameSchema } from "@/shared/constants";
|
import { EmulatorPackageSchema, EmulatorPackageType } from "@/shared/constants";
|
||||||
import { CACHE_KEYS, getOrCached } from "../../cache";
|
|
||||||
import { and, eq, or } from "drizzle-orm";
|
import { and, eq, or } from "drizzle-orm";
|
||||||
import { config, emulatorsDb } from '../../app';
|
import { config, emulatorsDb } from '../../app';
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import * as emulatorSchema from '@schema/emulators';
|
import * as emulatorSchema from '@schema/emulators';
|
||||||
import { shuffleInPlace } from "@/bun/utils";
|
import { EmulatorSystem } from "@/shared/types";
|
||||||
import { Glob } from "bun";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function getStoreRootFolder ()
|
export function getStoreRootFolder ()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { getStoreFolder } from "./services/gamesService";
|
||||||
import { EmulatorDownloadJob } from "../jobs/emulator-download-job";
|
import { EmulatorDownloadJob } from "../jobs/emulator-download-job";
|
||||||
import { BiosDownloadJob } from "../jobs/bios-download-job";
|
import { BiosDownloadJob } from "../jobs/bios-download-job";
|
||||||
import { findEmulatorPluginIntegration, getEmulatorPath } from "./services/emulatorsService";
|
import { findEmulatorPluginIntegration, getEmulatorPath } from "./services/emulatorsService";
|
||||||
|
import { EmulatorSourceEntryType, FrontEndEmulator, FrontEndGameTypeDetailed } from "@/shared/types";
|
||||||
|
|
||||||
export const store = new Elysia({ prefix: '/api/store' })
|
export const store = new Elysia({ prefix: '/api/store' })
|
||||||
.get('/emulators', async ({ query }) =>
|
.get('/emulators', async ({ query }) =>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import ReloadPluginsJob from "./jobs/reload-plugins-job";
|
||||||
import { semver } from "bun";
|
import { semver } from "bun";
|
||||||
import { getOrCachedGithubRelease } from "./cache";
|
import { getOrCachedGithubRelease } from "./cache";
|
||||||
import SelfUpdateJob from "./jobs/self-update-job";
|
import SelfUpdateJob from "./jobs/self-update-job";
|
||||||
|
import { DownloadsDrive } from "@/shared/types";
|
||||||
|
|
||||||
async function checkUpdate (force?: boolean)
|
async function checkUpdate (force?: boolean)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { JobStatus } from '@/shared/types';
|
||||||
import EventEmitter from 'node:events';
|
import EventEmitter from 'node:events';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
|
|
||||||
|
|
|
||||||
19
src/bun/types/helpers.d.ts
vendored
Normal file
19
src/bun/types/helpers.d.ts
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
declare module '*.bat' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.sh' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.ini' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.bin' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
42
src/bun/types/types.d.ts
vendored
42
src/bun/types/types.d.ts
vendored
|
|
@ -1,42 +0,0 @@
|
||||||
declare interface ObjectConstructor
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Groups members of an iterable according to the return value of the passed callback.
|
|
||||||
* @param items An iterable.
|
|
||||||
* @param keySelector A callback which will be invoked for each item in items.
|
|
||||||
*/
|
|
||||||
groupBy<K extends PropertyKey, T> (
|
|
||||||
items: Iterable<T>,
|
|
||||||
keySelector: (item: T, index: number) => K,
|
|
||||||
): Partial<Record<K, T[]>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface MapConstructor
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Groups members of an iterable according to the return value of the passed callback.
|
|
||||||
* @param items An iterable.
|
|
||||||
* @param keySelector A callback which will be invoked for each item in items.
|
|
||||||
*/
|
|
||||||
groupBy<K, T> (
|
|
||||||
items: Iterable<T>,
|
|
||||||
keySelector: (item: T, index: number) => K,
|
|
||||||
): Map<K, T[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface AppEventMap
|
|
||||||
{
|
|
||||||
exitapp: [];
|
|
||||||
notification: [FrontendNotification];
|
|
||||||
focus: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '*.bat' {
|
|
||||||
const content: string;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '*.sh' {
|
|
||||||
const content: string;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { GameflowHooks } from "../api/hooks/app";
|
import GameflowHooks from "../api/hooks/app";
|
||||||
import Conf from "conf";
|
import Conf from "conf";
|
||||||
import { $ZodRegistry } from "zod/v4/core";
|
import { $ZodRegistry } from "zod/v4/core";
|
||||||
import EventEmitter from "node:events";
|
|
||||||
|
|
||||||
export const PluginContextSchema = z.object({
|
export const PluginContextSchema = z.object({
|
||||||
hooks: z.instanceof(GameflowHooks)
|
hooks: z.instanceof(GameflowHooks)
|
||||||
|
|
@ -22,6 +21,7 @@ export const PluginDescriptionSchema = z.object({
|
||||||
icon: z.url().optional(),
|
icon: z.url().optional(),
|
||||||
keywords: z.array(z.string()).optional(),
|
keywords: z.array(z.string()).optional(),
|
||||||
category: z.string().default("other"),
|
category: z.string().default("other"),
|
||||||
|
main: z.string(),
|
||||||
canDisable: z.boolean().default(true).optional()
|
canDisable: z.boolean().default(true).optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
18
src/bun/types/types.ts
Normal file
18
src/bun/types/types.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { EmulatorDownloadInfoType, EmulatorPackageType } from "@/shared/constants";
|
||||||
|
import { FrontendNotification } from "@/shared/types";
|
||||||
|
|
||||||
|
export interface AppEventMap
|
||||||
|
{
|
||||||
|
exitapp: [];
|
||||||
|
notification: [FrontendNotification];
|
||||||
|
focus: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EmulatorPostInstallContext
|
||||||
|
{
|
||||||
|
emulator: string;
|
||||||
|
emulatorPackage?: EmulatorPackageType;
|
||||||
|
path: string;
|
||||||
|
update: boolean;
|
||||||
|
info: EmulatorDownloadInfoType;
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import { SettingsType } from '@/shared/constants';
|
||||||
import { config } from './api/app';
|
import { config } from './api/app';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import packageDef from '~/package.json';
|
import packageDef from '~/package.json';
|
||||||
|
import { KeysWithValueAssignableTo } from '@/shared/types';
|
||||||
|
|
||||||
export function checkRunning (pid: number)
|
export function checkRunning (pid: number)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import fs from 'node:fs/promises';
|
||||||
import { createWriteStream } from "node:fs";
|
import { createWriteStream } from "node:fs";
|
||||||
import { config, jar } from "../api/app";
|
import { config, jar } from "../api/app";
|
||||||
import { moveAllFiles } from "../utils";
|
import { moveAllFiles } from "../utils";
|
||||||
|
import { DownloadFileEntry } from "@/shared/types";
|
||||||
|
|
||||||
export interface ProgressStats
|
export interface ProgressStats
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -35,18 +35,6 @@ interface BrowserResult
|
||||||
source: GetBrowserSource;
|
source: GetBrowserSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PLATFORM_MAP: Record<string, string> = {
|
|
||||||
linux: "linux",
|
|
||||||
win32: "windows",
|
|
||||||
darwin: 'macos'
|
|
||||||
};
|
|
||||||
|
|
||||||
const ARCH_MAP: Record<string, Record<string, string>> = {
|
|
||||||
linux: { x64: "x86_64", arm64: "arm64" },
|
|
||||||
darwin: { x64: "x86_64", arm64: "arm64" },
|
|
||||||
win32: { x64: "x64", arm64: "arm64" },
|
|
||||||
};
|
|
||||||
|
|
||||||
/** The expected binary path per platform after extraction */
|
/** The expected binary path per platform after extraction */
|
||||||
async function getBundledBinaryPath (outDir: string, version: string, platform: string, arch: string): Promise<string | undefined>
|
async function getBundledBinaryPath (outDir: string, version: string, platform: string, arch: string): Promise<string | undefined>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { doesFocusableExist, FocusDetails, getCurrentFocusKey } from "@noriginmedia/norigin-spatial-navigation";
|
import { doesFocusableExist, FocusDetails, getCurrentFocusKey } from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import { useEffect, useLayoutEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
export function AutoFocus (data: {
|
export function AutoFocus (data: {
|
||||||
parentKey?: string;
|
parentKey?: string;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { FocusDetails, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
import { useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { JSX } from "react";
|
import { JSX } from "react";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import
|
||||||
FocusContext,
|
FocusContext,
|
||||||
useFocusable,
|
useFocusable,
|
||||||
} from "@noriginmedia/norigin-spatial-navigation";
|
} from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import { GameMeta } from "../../shared/constants";
|
|
||||||
import CardElement, { GameCardParams } from "./CardElement";
|
import CardElement, { GameCardParams } from "./CardElement";
|
||||||
import { JSX } from "react";
|
import { JSX } from "react";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { FocusContext, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
import { FocusContext, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import { Home, TriangleAlert } from "lucide-react";
|
import { Home, TriangleAlert } from "lucide-react";
|
||||||
import { GamePadButtonCode, useShortcutContext, useShortcuts } from "../scripts/shortcuts";
|
import { GamePadButtonCode, useShortcuts } from "../scripts/shortcuts";
|
||||||
import { FloatingShortcuts } from "./Shortcuts";
|
import { FloatingShortcuts } from "./Shortcuts";
|
||||||
import { Button } from "./options/Button";
|
import { Button } from "./options/Button";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@ function List (data: {
|
||||||
function NewFolderInput (data: { id: string, name: string | undefined, setName: (name: string) => void; className?: string; })
|
function NewFolderInput (data: { id: string, name: string | undefined, setName: (name: string) => void; className?: string; })
|
||||||
{
|
{
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const { control } = useActiveControl();
|
|
||||||
const { ref, focused, focusSelf } = useFocusable({
|
const { ref, focused, focusSelf } = useFocusable({
|
||||||
focusKey: data.id,
|
focusKey: data.id,
|
||||||
onEnterPress: () => inputRef.current?.focus(),
|
onEnterPress: () => inputRef.current?.focus(),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { FileQuestion, HardDrive, Store } from "lucide-react";
|
||||||
import { JSX } from "react";
|
import { JSX } from "react";
|
||||||
import { FOCUS_KEYS } from "../scripts/types";
|
import { FOCUS_KEYS } from "../scripts/types";
|
||||||
import { useRouter } from "@tanstack/react-router";
|
import { useRouter } from "@tanstack/react-router";
|
||||||
|
import { FrontEndGameType, FrontEndId } from "@/shared/types";
|
||||||
|
|
||||||
export default function FrontEndGameCard (data: { index: number, game: FrontEndGameType; showSource?: boolean; } & FocusParams & InteractParams)
|
export default function FrontEndGameCard (data: { index: number, game: FrontEndGameType; showSource?: boolean; } & FocusParams & InteractParams)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||||
import { GameMetaExtra, CardList } from "./CardList";
|
import { GameMetaExtra, CardList } from "./CardList";
|
||||||
import { DefaultRommStaleTime, GameListFilterType, RPC_URL } from "@shared/constants";
|
import { DefaultRommStaleTime, GameListFilterType, RPC_URL } from "@shared/constants";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
import { HardDrive } from "lucide-react";
|
import { HardDrive } from "lucide-react";
|
||||||
import { JSX, Ref, useContext, useEffect } from "react";
|
import { JSX, useContext } from "react";
|
||||||
import { useLocalSetting } from "../scripts/utils";
|
import { useLocalSetting } from "../scripts/utils";
|
||||||
import { AnimatedBackgroundContext } from "../scripts/contexts";
|
import { AnimatedBackgroundContext } from "../scripts/contexts";
|
||||||
import { allGamesQuery } from "@queries/romm";
|
import { allGamesQuery } from "@queries/romm";
|
||||||
|
import { FrontEndGameType, FrontEndId } from "@/shared/types";
|
||||||
|
|
||||||
export interface GameListParams extends FocusParams
|
export interface GameListParams extends FocusParams
|
||||||
{
|
{
|
||||||
|
|
@ -95,7 +96,7 @@ export function GameList (data: GameListParams)
|
||||||
|
|
||||||
const previewUrls = g.path_covers.map(c =>
|
const previewUrls = g.path_covers.map(c =>
|
||||||
{
|
{
|
||||||
const url = new URL(`${RPC_URL(__HOST__)}${c}`);
|
const url = c.startsWith("http") ? new URL(c) : new URL(`${RPC_URL(__HOST__)}${c}`);
|
||||||
url.searchParams.delete('ts');
|
url.searchParams.delete('ts');
|
||||||
return url;
|
return url;
|
||||||
});
|
});
|
||||||
|
|
@ -103,7 +104,7 @@ export function GameList (data: GameListParams)
|
||||||
let platformUrl: URL | undefined = undefined;
|
let platformUrl: URL | undefined = undefined;
|
||||||
if (g.path_platform_cover)
|
if (g.path_platform_cover)
|
||||||
{
|
{
|
||||||
platformUrl = new URL(`${RPC_URL(__HOST__)}${g.path_platform_cover}`);
|
platformUrl = g.path_platform_cover.startsWith("http") ? new URL(g.path_platform_cover) : new URL(`${RPC_URL(__HOST__)}${g.path_platform_cover}`);
|
||||||
platformUrl.searchParams.set('width', "64");
|
platformUrl.searchParams.set('width', "64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ const KeyElements: Record<string, JSX.Element> = {
|
||||||
'←': <ArrowLeft />,
|
'←': <ArrowLeft />,
|
||||||
'→': <ArrowRight />,
|
'→': <ArrowRight />,
|
||||||
};
|
};
|
||||||
const DZ = 0.22, TH = 0.85, NS = 'http://www.w3.org/2000/svg';
|
const DZ = 0.22;
|
||||||
|
|
||||||
function ang (x: number, y: number)
|
function ang (x: number, y: number)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ function SearchInput (data: {
|
||||||
onSubmit: (search: string | undefined) => void;
|
onSubmit: (search: string | undefined) => void;
|
||||||
} & FocusParams)
|
} & FocusParams)
|
||||||
{
|
{
|
||||||
const { control } = useActiveControl();
|
|
||||||
const { ref, focusKey } = useFocusable({
|
const { ref, focusKey } = useFocusable({
|
||||||
onBlur: () => inputRef.current?.blur(),
|
onBlur: () => inputRef.current?.blur(),
|
||||||
onFocus: (l, p, d) =>
|
onFocus: (l, p, d) =>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { FocusContext, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
import { FocusContext, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import { Home, TriangleAlert } from "lucide-react";
|
import { Home, TriangleAlert } from "lucide-react";
|
||||||
import { GamePadButtonCode, useShortcutContext, useShortcuts } from "../scripts/shortcuts";
|
import { GamePadButtonCode, useShortcuts } from "../scripts/shortcuts";
|
||||||
import { Button } from "./options/Button";
|
import { Button } from "./options/Button";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useRouter } from "@tanstack/react-router";
|
import { useRouter } from "@tanstack/react-router";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { RPC_URL } from "@/shared/constants";
|
import { RPC_URL } from "@/shared/constants";
|
||||||
|
import { FrontendNotification } from "@/shared/types";
|
||||||
import { Clock, CloudUpload, Save } from "lucide-react";
|
import { Clock, CloudUpload, Save } from "lucide-react";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import toast, { ToastOptions } from "react-hot-toast";
|
import toast, { ToastOptions } from "react-hot-toast";
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ import { useNavigate } from "@tanstack/react-router";
|
||||||
import { DefaultRommStaleTime, RPC_URL } from "@shared/constants";
|
import { DefaultRommStaleTime, RPC_URL } from "@shared/constants";
|
||||||
import { CardList, GameMetaExtra } from "./CardList";
|
import { CardList, GameMetaExtra } from "./CardList";
|
||||||
import { rommApi } from "../scripts/clientApi";
|
import { rommApi } from "../scripts/clientApi";
|
||||||
import { JSX, useMemo, useState } from "react";
|
import { JSX, useMemo } from "react";
|
||||||
import { Gamepad2, HardDrive } from "lucide-react";
|
import { HardDrive } from "lucide-react";
|
||||||
import { mobileCheck } from "../scripts/utils";
|
import { mobileCheck } from "../scripts/utils";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import placeholder from '../assets/256x256.png?url';
|
import placeholder from '../assets/256x256.png?url';
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { useFocusable, FocusContext } from "@noriginmedia/norigin-spatial-naviga
|
||||||
import { ArrowDownAz, ClockArrowDown, CalendarArrowDown, Rocket, HardDrive, SortDesc, User, Drama, FunnelX, Store } from "lucide-react";
|
import { ArrowDownAz, ClockArrowDown, CalendarArrowDown, Rocket, HardDrive, SortDesc, User, Drama, FunnelX, Store } from "lucide-react";
|
||||||
import { sourceIconMap } from "./Constants";
|
import { sourceIconMap } from "./Constants";
|
||||||
import { useContextDialog, ContextList, DialogEntry } from "./ContextDialog";
|
import { useContextDialog, ContextList, DialogEntry } from "./ContextDialog";
|
||||||
|
import { FrontEndFilterLists } from "@/shared/types";
|
||||||
|
|
||||||
function FilterButton (data: {
|
function FilterButton (data: {
|
||||||
id: string,
|
id: string,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Ref, RefObject } from 'react';
|
import { Ref } from 'react';
|
||||||
import './dots.css';
|
import './dots.css';
|
||||||
|
|
||||||
export default function DotsLoading (data: { ref?: Ref<any>; })
|
export default function DotsLoading (data: { ref?: Ref<any>; })
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
import { FrontEndGameTypeDetailed, FrontEndGameTypeDetailedAchievement } from "@/shared/types";
|
||||||
import { useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
import { useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import { Medal } from "lucide-react";
|
import { Medal } from "lucide-react";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import ActionButton from "./ActionButton";
|
||||||
import { useLocalStorage } from "usehooks-ts";
|
import { useLocalStorage } from "usehooks-ts";
|
||||||
import FocusTooltip from "../FocusTooltip";
|
import FocusTooltip from "../FocusTooltip";
|
||||||
import { useBlocker, useNavigate, useRouter } from "@tanstack/react-router";
|
import { useBlocker, useNavigate, useRouter } from "@tanstack/react-router";
|
||||||
|
import { FrontEndGameTypeDetailed } from "@/shared/types";
|
||||||
|
|
||||||
function AchievementsInfo (data: { game: FrontEndGameTypeDetailed; } & InteractParams)
|
function AchievementsInfo (data: { game: FrontEndGameTypeDetailed; } & InteractParams)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { scrollIntoViewHandler } from "@/mainview/scripts/utils";
|
||||||
import { RPC_URL } from "@/shared/constants";
|
import { RPC_URL } from "@/shared/constants";
|
||||||
import { FocusContext, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
import { FocusContext, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Clock, CloudBackup, CloudDownload, CloudUpload, Gamepad2, HardDrive, Store, TriangleAlert } from "lucide-react";
|
import { Clock, CloudDownload, CloudUpload, HardDrive, TriangleAlert } from "lucide-react";
|
||||||
import prettyBytes from "pretty-bytes";
|
import prettyBytes from "pretty-bytes";
|
||||||
import { JSX } from "react";
|
import { JSX } from "react";
|
||||||
import ActionButtons from "./ActionButtons";
|
import ActionButtons from "./ActionButtons";
|
||||||
|
|
@ -10,6 +10,7 @@ import prettyMilliseconds from 'pretty-ms';
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { validateSourceQuery } from "@/mainview/scripts/queries/romm";
|
import { validateSourceQuery } from "@/mainview/scripts/queries/romm";
|
||||||
import { sourceIconMap } from "../Constants";
|
import { sourceIconMap } from "../Constants";
|
||||||
|
import { FrontEndGameTypeDetailed } from "@/shared/types";
|
||||||
|
|
||||||
export function DetailElement (data: { icon: JSX.Element; tooltip?: string | null, children?: any | any[]; })
|
export function DetailElement (data: { icon: JSX.Element; tooltip?: string | null, children?: any | any[]; })
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { gameLookup } from "@/mainview/scripts/queries/romm";
|
|
||||||
import { FocusContext, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
import { FocusContext, useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { Check, Search } from "lucide-react";
|
import { Check, Search } from "lucide-react";
|
||||||
|
|
@ -6,6 +6,8 @@ import HeaderSearchField from "../HeaderSearchField";
|
||||||
import { GamePadButtonCode, useShortcuts } from "@/mainview/scripts/shortcuts";
|
import { GamePadButtonCode, useShortcuts } from "@/mainview/scripts/shortcuts";
|
||||||
import { scrollIntoViewHandler } from "@/mainview/scripts/utils";
|
import { scrollIntoViewHandler } from "@/mainview/scripts/utils";
|
||||||
import { FOCUS_KEYS } from "@/mainview/scripts/types";
|
import { FOCUS_KEYS } from "@/mainview/scripts/types";
|
||||||
|
import { FrontEndId, GameLookup } from "@/shared/types";
|
||||||
|
import { gameLookupQuery } from "@/mainview/scripts/queries/romm";
|
||||||
|
|
||||||
function Result (data: {
|
function Result (data: {
|
||||||
match: GameLookup;
|
match: GameLookup;
|
||||||
|
|
@ -54,7 +56,7 @@ function SearchField (data: { setSearch: (search: string | undefined) => void; s
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function GameLookup (data: {
|
export default function GameLookupElement (data: {
|
||||||
search: string | undefined,
|
search: string | undefined,
|
||||||
setSearch: (search: string | undefined) => void,
|
setSearch: (search: string | undefined) => void,
|
||||||
onSelect: (match: GameLookup) => void;
|
onSelect: (match: GameLookup) => void;
|
||||||
|
|
@ -62,7 +64,7 @@ export default function GameLookup (data: {
|
||||||
selected?: FrontEndId;
|
selected?: FrontEndId;
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
const { data: lookups, isFetching } = useQuery({ ...gameLookup(data.search), staleTime: 1000 * 60 * 60 });
|
const { data: lookups, isFetching } = useQuery({ ...gameLookupQuery(data.search), staleTime: 1000 * 60 * 60 });
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<SearchField setSearch={data.setSearch} search={data.search} />
|
<SearchField setSearch={data.setSearch} search={data.search} />
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import ActionButton from "./ActionButton";
|
||||||
import { useRouter } from "@tanstack/react-router";
|
import { useRouter } from "@tanstack/react-router";
|
||||||
import { DownloadSourceType } from "@/shared/constants";
|
import { DownloadSourceType } from "@/shared/constants";
|
||||||
import { GamePadButtonCode, Shortcut, useShortcuts } from "@/mainview/scripts/shortcuts";
|
import { GamePadButtonCode, Shortcut, useShortcuts } from "@/mainview/scripts/shortcuts";
|
||||||
|
import { CommandEntry, FrontEndGameTypeDetailed } from "@/shared/types";
|
||||||
|
|
||||||
export default function MainActions (data: { game?: FrontEndGameTypeDetailed, source: string, id: string; })
|
export default function MainActions (data: { game?: FrontEndGameTypeDetailed, source: string, id: string; })
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { PathSettingsOptionBase, PathSettingsOptionParams } from "./PathSettings
|
||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import { changeDownloadsMutation, getSettingQuery } from "@queries/settings";
|
import { changeDownloadsMutation, getSettingQuery } from "@queries/settings";
|
||||||
import { SettingsType } from "@/shared/constants";
|
import { SettingsType } from "@/shared/constants";
|
||||||
|
import { KeysWithValueAssignableTo } from "@/shared/types";
|
||||||
|
|
||||||
export default function DownloadDirectoryOption (data: PathSettingsOptionParams & { id: KeysWithValueAssignableTo<SettingsType, string>; })
|
export default function DownloadDirectoryOption (data: PathSettingsOptionParams & { id: KeysWithValueAssignableTo<SettingsType, string>; })
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { useFocusable } from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import { CheckIcon, X } from "lucide-react";
|
import { CheckIcon, X } from "lucide-react";
|
||||||
import { oneShot } from "@/mainview/scripts/audio/audio";
|
import { oneShot } from "@/mainview/scripts/audio/audio";
|
||||||
import { GamePadButtonCode, Shortcut, useShortcuts } from "@/mainview/scripts/shortcuts";
|
import { GamePadButtonCode, Shortcut, useShortcuts } from "@/mainview/scripts/shortcuts";
|
||||||
import useActiveControl from "@/mainview/scripts/gamepads";
|
|
||||||
|
|
||||||
export function OptionInput (data: {
|
export function OptionInput (data: {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -35,7 +34,6 @@ export function OptionInput (data: {
|
||||||
}
|
}
|
||||||
oneShot('click');
|
oneShot('click');
|
||||||
};
|
};
|
||||||
const { control } = useActiveControl();
|
|
||||||
const [inputFocused, setInputFocused] = useState(false);
|
const [inputFocused, setInputFocused] = useState(false);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const { ref, focusKey } = useFocusable({
|
const { ref, focusKey } = useFocusable({
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { ContextDialog } from "../ContextDialog";
|
||||||
import FilePicker from "../FilePicker";
|
import FilePicker from "../FilePicker";
|
||||||
import { setFocus } from "@noriginmedia/norigin-spatial-navigation";
|
import { setFocus } from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import { getSettingQuery, setSettingMutation } from "@queries/settings";
|
import { getSettingQuery, setSettingMutation } from "@queries/settings";
|
||||||
|
import { KeysWithValueAssignableTo } from "@/shared/types";
|
||||||
|
|
||||||
export interface PathSettingsOptionParams
|
export interface PathSettingsOptionParams
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import { OptionSpace } from "./OptionSpace";
|
import { OptionSpace } from "./OptionSpace";
|
||||||
import { getSettingQuery, setSettingMutation } from "@queries/settings";
|
import { getSettingQuery, setSettingMutation } from "@queries/settings";
|
||||||
import { OptionDropdown } from "./OptionDropdown";
|
import { OptionDropdown } from "./OptionDropdown";
|
||||||
|
import { KeysWithValueAssignableTo } from "@/shared/types";
|
||||||
|
|
||||||
export function SettingsDropdown (data: {
|
export function SettingsDropdown (data: {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import { OptionSpace } from "./OptionSpace";
|
import { OptionSpace } from "./OptionSpace";
|
||||||
import { OptionInput } from "./OptionInput";
|
import { OptionInput } from "./OptionInput";
|
||||||
import { getSettingQuery, setSettingMutation } from "@queries/settings";
|
import { getSettingQuery, setSettingMutation } from "@queries/settings";
|
||||||
|
import { KeysWithValueAssignableTo } from "@/shared/types";
|
||||||
|
|
||||||
export function SettingsOption (data: {
|
export function SettingsOption (data: {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { StoreEmulatorCard } from "./StoreEmulatorCard";
|
||||||
import { FOCUS_KEYS } from "@/mainview/scripts/types";
|
import { FOCUS_KEYS } from "@/mainview/scripts/types";
|
||||||
import Carousel from "../Carousel";
|
import Carousel from "../Carousel";
|
||||||
import { useRouter } from "@tanstack/react-router";
|
import { useRouter } from "@tanstack/react-router";
|
||||||
|
import { FrontEndEmulator } from "@/shared/types";
|
||||||
|
|
||||||
function SeeAllCard (data: { id: string; onAction: () => void; onFocus?: (details: { node: HTMLElement, instant?: boolean; }) => void; })
|
function SeeAllCard (data: { id: string; onAction: () => void; onFocus?: (details: { node: HTMLElement, instant?: boolean; }) => void; })
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import FrontEndGameCard from "../FrontEndGameCard";
|
||||||
import { FOCUS_KEYS } from "@/mainview/scripts/types";
|
import { FOCUS_KEYS } from "@/mainview/scripts/types";
|
||||||
import Carousel from "../Carousel";
|
import Carousel from "../Carousel";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
import { FrontEndGameType, FrontEndId } from "@/shared/types";
|
||||||
|
|
||||||
export function GamesSection (data: {
|
export function GamesSection (data: {
|
||||||
games?: FrontEndGameType[];
|
games?: FrontEndGameType[];
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { GamePadButtonCode, useShortcuts } from "@/mainview/scripts/shortcuts";
|
||||||
import { RPC_URL } from "@/shared/constants";
|
import { RPC_URL } from "@/shared/constants";
|
||||||
import { FOCUS_KEYS } from "@/mainview/scripts/types";
|
import { FOCUS_KEYS } from "@/mainview/scripts/types";
|
||||||
import { oneShot } from "@/mainview/scripts/audio/audio";
|
import { oneShot } from "@/mainview/scripts/audio/audio";
|
||||||
|
import { FrontEndEmulator } from "@/shared/types";
|
||||||
|
|
||||||
// ── Single missing-emulator card ───────────────────────────────────────────
|
// ── Single missing-emulator card ───────────────────────────────────────────
|
||||||
interface MissingCardProps
|
interface MissingCardProps
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { JSX } from "react";
|
||||||
import { oneShot } from "@/mainview/scripts/audio/audio";
|
import { oneShot } from "@/mainview/scripts/audio/audio";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { getUpdateInfoForEmulator } from "@/mainview/scripts/queries/store";
|
import { getUpdateInfoForEmulator } from "@/mainview/scripts/queries/store";
|
||||||
|
import { FrontEndEmulator } from "@/shared/types";
|
||||||
|
|
||||||
export const emulatorStatusIcons: Record<string, JSX.Element> = {
|
export const emulatorStatusIcons: Record<string, JSX.Element> = {
|
||||||
store: <Store />,
|
store: <Store />,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import z from 'zod';
|
||||||
import { RefObject, useEffect, useRef, useState } from 'react';
|
import { RefObject, useEffect, useRef, useState } from 'react';
|
||||||
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
||||||
import { ButtonStyle } from '../components/options/Button';
|
import { ButtonStyle } from '../components/options/Button';
|
||||||
import { CloudDownload, DoorOpen, RefreshCw, Save, Undo } from 'lucide-react';
|
import { CloudDownload, DoorOpen, RefreshCw, Undo } from 'lucide-react';
|
||||||
import { GamePadButtonCode, useShortcuts } from '../scripts/shortcuts';
|
import { GamePadButtonCode, useShortcuts } from '../scripts/shortcuts';
|
||||||
import { FloatingShortcuts } from '../components/Shortcuts';
|
import { FloatingShortcuts } from '../components/Shortcuts';
|
||||||
import { useEventListener } from 'usehooks-ts';
|
import { useEventListener } from 'usehooks-ts';
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import { Calendar, Folder, Gamepad2, Image, Info, TriangleAlert, Trophy } from "
|
||||||
import { HeaderUI, StickyHeaderUI } from "../../components/Header";
|
import { HeaderUI, StickyHeaderUI } from "../../components/Header";
|
||||||
import { AnimatedBackground } from "../../components/AnimatedBackground";
|
import { AnimatedBackground } from "../../components/AnimatedBackground";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import Shortcuts, { FloatingShortcuts } from "../../components/Shortcuts";
|
import { FloatingShortcuts } from "../../components/Shortcuts";
|
||||||
import { GamePadButtonCode, useShortcutContext, useShortcuts } from "@/mainview/scripts/shortcuts";
|
import { GamePadButtonCode, useShortcuts } from "@/mainview/scripts/shortcuts";
|
||||||
import Screenshots from "@/mainview/components/Screenshots";
|
import Screenshots from "@/mainview/components/Screenshots";
|
||||||
import { HandleGoBack, scrollIntoViewHandler, useOnNavigateBack } from "@/mainview/scripts/utils";
|
import { HandleGoBack, scrollIntoViewHandler, useOnNavigateBack } from "@/mainview/scripts/utils";
|
||||||
import { FilterUI } from "@/mainview/components/Filters";
|
import { FilterUI } from "@/mainview/components/Filters";
|
||||||
|
|
@ -23,8 +23,8 @@ import { GamesSection } from "@/mainview/components/store/GamesSection";
|
||||||
import Details from "@/mainview/components/game/Details";
|
import Details from "@/mainview/components/game/Details";
|
||||||
import { AutoFocus } from "@/mainview/components/AutoFocus";
|
import { AutoFocus } from "@/mainview/components/AutoFocus";
|
||||||
import SelectMenu from "@/mainview/components/SelectMenu";
|
import SelectMenu from "@/mainview/components/SelectMenu";
|
||||||
import { en } from "zod/v4/locales";
|
|
||||||
import { IGDBIcon } from "@/mainview/scripts/brandIcons";
|
import { IGDBIcon } from "@/mainview/scripts/brandIcons";
|
||||||
|
import { FrontEndGameTypeDetailed } from "@/shared/types";
|
||||||
|
|
||||||
export const Route = createFileRoute("/game/$source/$id")({
|
export const Route = createFileRoute("/game/$source/$id")({
|
||||||
loader: async ({ params, context }) =>
|
loader: async ({ params, context }) =>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { AutoFocus } from '@/mainview/components/AutoFocus';
|
import { AutoFocus } from '@/mainview/components/AutoFocus';
|
||||||
import { OptionElement } from '@/mainview/components/ContextDialog';
|
import { OptionElement } from '@/mainview/components/ContextDialog';
|
||||||
import GameLookup from '@/mainview/components/game/GameLookup';
|
import GameLookupElement from '@/mainview/components/game/GameLookup';
|
||||||
import { StickyHeaderUI } from '@/mainview/components/Header';
|
import { StickyHeaderUI } from '@/mainview/components/Header';
|
||||||
import LoadingScreen from '@/mainview/components/LoadingScreen';
|
import LoadingScreen from '@/mainview/components/LoadingScreen';
|
||||||
import { Button } from '@/mainview/components/options/Button';
|
import { Button } from '@/mainview/components/options/Button';
|
||||||
|
|
@ -208,7 +208,7 @@ function Lookup ()
|
||||||
navigate({ to: '/game/add', search: { ...state, selectedGame: { source, id }, platformId: undefined, search, step: 2 }, replace: true });
|
navigate({ to: '/game/add', search: { ...state, selectedGame: { source, id }, platformId: undefined, search, step: 2 }, replace: true });
|
||||||
oneShot('openGeneric');
|
oneShot('openGeneric');
|
||||||
};
|
};
|
||||||
return <GameLookup
|
return <GameLookupElement
|
||||||
showPlatforms
|
showPlatforms
|
||||||
selected={state.selectedGame}
|
selected={state.selectedGame}
|
||||||
search={search}
|
search={search}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { AnimatedBackground } from '@/mainview/components/AnimatedBackground';
|
import { AnimatedBackground } from '@/mainview/components/AnimatedBackground';
|
||||||
import { AutoFocus } from '@/mainview/components/AutoFocus';
|
import { AutoFocus } from '@/mainview/components/AutoFocus';
|
||||||
import GameLookup from '@/mainview/components/game/GameLookup';
|
import GameLookupElement from '@/mainview/components/game/GameLookup';
|
||||||
import { StickyHeaderUI } from '@/mainview/components/Header';
|
import { StickyHeaderUI } from '@/mainview/components/Header';
|
||||||
import { FloatingShortcuts } from '@/mainview/components/Shortcuts';
|
import { FloatingShortcuts } from '@/mainview/components/Shortcuts';
|
||||||
import { customUpdateMutation, gameInvalidationQuery, gameQuery } from '@/mainview/scripts/queries/romm';
|
import { customUpdateMutation, gameInvalidationQuery, gameQuery } from '@/mainview/scripts/queries/romm';
|
||||||
|
|
@ -20,7 +20,6 @@ function RouteComponent ()
|
||||||
{
|
{
|
||||||
const { source, id } = Route.useParams();
|
const { source, id } = Route.useParams();
|
||||||
const [search, setSearch] = useState<string | undefined>(undefined);
|
const [search, setSearch] = useState<string | undefined>(undefined);
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { data: game } = useQuery(gameQuery(source, id));
|
const { data: game } = useQuery(gameQuery(source, id));
|
||||||
|
|
@ -47,7 +46,7 @@ function RouteComponent ()
|
||||||
<FocusContext value={focusKey}>
|
<FocusContext value={focusKey}>
|
||||||
<div className='flex flex-col z-10 overflow-y-scroll'>
|
<div className='flex flex-col z-10 overflow-y-scroll'>
|
||||||
<StickyHeaderUI ref={ref} />
|
<StickyHeaderUI ref={ref} />
|
||||||
<GameLookup
|
<GameLookupElement
|
||||||
search={search}
|
search={search}
|
||||||
setSearch={setSearch}
|
setSearch={setSearch}
|
||||||
onSelect={l =>
|
onSelect={l =>
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,17 @@ import
|
||||||
{
|
{
|
||||||
Gamepad2,
|
Gamepad2,
|
||||||
Settings,
|
Settings,
|
||||||
MessageSquare,
|
|
||||||
Image,
|
|
||||||
Search,
|
Search,
|
||||||
Power,
|
Power,
|
||||||
OctagonAlert,
|
OctagonAlert,
|
||||||
Maximize,
|
Maximize,
|
||||||
Store,
|
Store,
|
||||||
LayoutGrid,
|
LayoutGrid,
|
||||||
PlusCircle,
|
|
||||||
Plus,
|
|
||||||
LucideIcon,
|
LucideIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import
|
import
|
||||||
{
|
{
|
||||||
createFileRoute,
|
createFileRoute,
|
||||||
PathParamOptions,
|
|
||||||
ToPathOption,
|
|
||||||
useRouter,
|
useRouter,
|
||||||
} from "@tanstack/react-router";
|
} from "@tanstack/react-router";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
@ -41,11 +35,11 @@ import SaveScroll from "../components/SaveScroll";
|
||||||
import { ErrorBoundary, useErrorBoundary } from "react-error-boundary";
|
import { ErrorBoundary, useErrorBoundary } from "react-error-boundary";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { PlatformsList } from "../components/PlatformsList";
|
import { PlatformsList } from "../components/PlatformsList";
|
||||||
import { GamePadButtonCode, useShortcutContext, useShortcuts } from "../scripts/shortcuts";
|
import { GamePadButtonCode, useShortcuts } from "../scripts/shortcuts";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import CollectionList from "../components/CollectionList";
|
import CollectionList from "../components/CollectionList";
|
||||||
import { zodValidator } from '@tanstack/zod-adapter';
|
import { zodValidator } from '@tanstack/zod-adapter';
|
||||||
import { mobileCheck, scrollIntoNearestParent, scrollIntoViewHandler, useDragScroll } from "../scripts/utils";
|
import { mobileCheck, scrollIntoViewHandler, useDragScroll } from "../scripts/utils";
|
||||||
import { AnimatedBackgroundContext } from "../scripts/contexts";
|
import { AnimatedBackgroundContext } from "../scripts/contexts";
|
||||||
import Carousel from "../components/Carousel";
|
import Carousel from "../components/Carousel";
|
||||||
import { closeMutation } from "@queries/system";
|
import { closeMutation } from "@queries/system";
|
||||||
|
|
@ -56,6 +50,7 @@ import SelectMenu from "../components/SelectMenu";
|
||||||
import HeaderSearchField from "../components/HeaderSearchField";
|
import HeaderSearchField from "../components/HeaderSearchField";
|
||||||
import CardElement from "../components/CardElement";
|
import CardElement from "../components/CardElement";
|
||||||
import { Router } from "..";
|
import { Router } from "..";
|
||||||
|
import { FrontEndId } from "@/shared/types";
|
||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
export const Route = createFileRoute("/")({
|
||||||
component: ConsoleHomeUI,
|
component: ConsoleHomeUI,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
import { AnimatedBackground } from '@/mainview/components/AnimatedBackground';
|
import { AnimatedBackground } from '@/mainview/components/AnimatedBackground';
|
||||||
import { createFileRoute, useBlocker, useRouter } from '@tanstack/react-router';
|
import { createFileRoute, useBlocker, useRouter } from '@tanstack/react-router';
|
||||||
import DotsLoading from '../components/backgrounds/dots';
|
import DotsLoading from '../components/backgrounds/dots';
|
||||||
import { GamePadButtonCode, useShortcutContext, useShortcuts } from '../scripts/shortcuts';
|
import { GamePadButtonCode, useShortcuts } from '../scripts/shortcuts';
|
||||||
import { useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
import { useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
||||||
import Shortcuts, { FloatingShortcuts } from '../components/Shortcuts';
|
import { FloatingShortcuts } from '../components/Shortcuts';
|
||||||
import { useJobStatus } from '../scripts/utils';
|
import { useJobStatus } from '../scripts/utils';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { rommApi } from '../scripts/clientApi';
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/launcher/$source/$id')({
|
export const Route = createFileRoute('/launcher/$source/$id')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
|
|
||||||
|
|
||||||
import { Button } from '@/mainview/components/options/Button';
|
|
||||||
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
||||||
import { checkUpdateMutation, hasUpdateQuery, systemInfoQuery, updateMutation } from '@queries/system';
|
import { systemInfoQuery } from '@queries/system';
|
||||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import { ArrowUpCircle, CircleFadingArrowUp, RefreshCcw } from 'lucide-react';
|
|
||||||
import prettyBytes from 'pretty-bytes';
|
import prettyBytes from 'pretty-bytes';
|
||||||
|
|
||||||
export const Route = createFileRoute('/settings/about')({
|
export const Route = createFileRoute('/settings/about')({
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { systemApi } from '@/mainview/scripts/clientApi';
|
||||||
import useActiveControl from '@/mainview/scripts/gamepads';
|
import useActiveControl from '@/mainview/scripts/gamepads';
|
||||||
import { changeDownloadsMutation } from '@queries/settings';
|
import { changeDownloadsMutation } from '@queries/settings';
|
||||||
import { downloadDrivesQuery } from '@/mainview/scripts/queries/system';
|
import { downloadDrivesQuery } from '@/mainview/scripts/queries/system';
|
||||||
|
import { DownloadsDrive } from '@/shared/types';
|
||||||
|
|
||||||
export const Route = createFileRoute('/settings/directories')({
|
export const Route = createFileRoute('/settings/directories')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import { FOCUS_KEYS } from '@/mainview/scripts/types';
|
||||||
import { scrollIntoNearestParent, scrollIntoViewHandler, useDragScroll } from '@/mainview/scripts/utils';
|
import { scrollIntoNearestParent, scrollIntoViewHandler, useDragScroll } from '@/mainview/scripts/utils';
|
||||||
import { SettingsOption } from '@/mainview/components/options/SettingsOption';
|
import { SettingsOption } from '@/mainview/components/options/SettingsOption';
|
||||||
import { SettingsDropdown } from '@/mainview/components/options/SettingsDropdown';
|
import { SettingsDropdown } from '@/mainview/components/options/SettingsDropdown';
|
||||||
|
import { FrontEndEmulator } from '@/shared/types';
|
||||||
|
|
||||||
export const Route = createFileRoute('/settings/emulators')({
|
export const Route = createFileRoute('/settings/emulators')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@ import { OptionDropdown } from '@/mainview/components/options/OptionDropdown';
|
||||||
import { OptionInput } from '@/mainview/components/options/OptionInput';
|
import { OptionInput } from '@/mainview/components/options/OptionInput';
|
||||||
import { OptionSpace } from '@/mainview/components/options/OptionSpace';
|
import { OptionSpace } from '@/mainview/components/options/OptionSpace';
|
||||||
import { RoundButton } from '@/mainview/components/RoundButton';
|
import { RoundButton } from '@/mainview/components/RoundButton';
|
||||||
import { getAllPluginsQuery, getPluginDetailsQuery } from '@/mainview/scripts/queries/plugins';
|
import { getPluginDetailsQuery } from '@/mainview/scripts/queries/plugins';
|
||||||
import { getPluginActionsQuery, getPluginSettingQuery, getPluginSettingsDefinitionQuery, pluginActionMutation, setPluginSettingMutation } from '@/mainview/scripts/queries/settings';
|
import { getPluginActionsQuery, getPluginSettingQuery, getPluginSettingsDefinitionQuery, pluginActionMutation, setPluginSettingMutation } from '@/mainview/scripts/queries/settings';
|
||||||
import { GamePadButtonCode, useShortcuts } from '@/mainview/scripts/shortcuts';
|
import { GamePadButtonCode, useShortcuts } from '@/mainview/scripts/shortcuts';
|
||||||
import { scrollIntoViewHandler } from '@/mainview/scripts/utils';
|
import { scrollIntoViewHandler } from '@/mainview/scripts/utils';
|
||||||
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
||||||
import { useMutation, useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
||||||
import { JSONSchema7 } from 'json-schema';
|
import { JSONSchema7 } from 'json-schema';
|
||||||
import { ArrowLeft, CirclePlay, Play, Settings2, SettingsIcon } from 'lucide-react';
|
import { ArrowLeft, CirclePlay, Settings2 } from 'lucide-react';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
export const Route = createFileRoute('/settings/plugin/$source')({
|
export const Route = createFileRoute('/settings/plugin/$source')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { AutoFocus } from '@/mainview/components/AutoFocus';
|
import { AutoFocus } from '@/mainview/components/AutoFocus';
|
||||||
import { pluginCategoryIcons, pluginCategoryPriorities } from '@/mainview/components/Constants';
|
import { pluginCategoryIcons, pluginCategoryPriorities } from '@/mainview/components/Constants';
|
||||||
import { Button } from '@/mainview/components/options/Button';
|
|
||||||
import { OptionInput } from '@/mainview/components/options/OptionInput';
|
import { OptionInput } from '@/mainview/components/options/OptionInput';
|
||||||
import { OptionSpace } from '@/mainview/components/options/OptionSpace';
|
import { OptionSpace } from '@/mainview/components/options/OptionSpace';
|
||||||
import { RoundButton } from '@/mainview/components/RoundButton';
|
import { RoundButton } from '@/mainview/components/RoundButton';
|
||||||
import { enablePluginMutation, getAllPluginsQuery } from '@/mainview/scripts/queries/plugins';
|
import { enablePluginMutation, getAllPluginsQuery } from '@/mainview/scripts/queries/plugins';
|
||||||
import { GamePadButtonCode, Shortcut } from '@/mainview/scripts/shortcuts';
|
import { GamePadButtonCode, Shortcut } from '@/mainview/scripts/shortcuts';
|
||||||
|
import { FrontendPlugin } from '@/shared/types';
|
||||||
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
import { FocusContext, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
||||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import
|
||||||
{
|
{
|
||||||
Outlet,
|
Outlet,
|
||||||
createFileRoute,
|
createFileRoute,
|
||||||
useMatch,
|
|
||||||
useMatchRoute,
|
useMatchRoute,
|
||||||
useRouter,
|
useRouter,
|
||||||
useRouterState,
|
useRouterState,
|
||||||
|
|
@ -29,8 +28,8 @@ import { JSX, useMemo } from "react";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { SettingsSchema } from "../../../shared/constants";
|
import { SettingsSchema } from "../../../shared/constants";
|
||||||
import { GamePadButtonCode, useShortcutContext, useShortcuts } from "@/mainview/scripts/shortcuts";
|
import { GamePadButtonCode, useShortcuts } from "@/mainview/scripts/shortcuts";
|
||||||
import Shortcuts, { FloatingShortcuts } from "@/mainview/components/Shortcuts";
|
import Shortcuts from "@/mainview/components/Shortcuts";
|
||||||
import { HandleGoBack } from "@/mainview/scripts/utils";
|
import { HandleGoBack } from "@/mainview/scripts/utils";
|
||||||
import { AutoFocus } from "@/mainview/components/AutoFocus";
|
import { AutoFocus } from "@/mainview/components/AutoFocus";
|
||||||
import { oneShot } from "@/mainview/scripts/audio/audio";
|
import { oneShot } from "@/mainview/scripts/audio/audio";
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ import
|
||||||
FocusContext,
|
FocusContext,
|
||||||
} from "@noriginmedia/norigin-spatial-navigation";
|
} from "@noriginmedia/norigin-spatial-navigation";
|
||||||
import { createFileRoute, useNavigate, useRouter } from "@tanstack/react-router";
|
import { createFileRoute, useNavigate, useRouter } from "@tanstack/react-router";
|
||||||
import { GamePadButtonCode, useShortcutContext, useShortcuts } from "@/mainview/scripts/shortcuts";
|
import { GamePadButtonCode, useShortcuts } from "@/mainview/scripts/shortcuts";
|
||||||
import Shortcuts, { FloatingShortcuts } from "@/mainview/components/Shortcuts";
|
import { FloatingShortcuts } from "@/mainview/components/Shortcuts";
|
||||||
import { AnimatedBackground } from "@/mainview/components/AnimatedBackground";
|
import { AnimatedBackground } from "@/mainview/components/AnimatedBackground";
|
||||||
import { rommApi, systemApi } from "@/mainview/scripts/clientApi";
|
import { rommApi, systemApi } from "@/mainview/scripts/clientApi";
|
||||||
import { Button } from "@/mainview/components/options/Button";
|
import { Button } from "@/mainview/components/options/Button";
|
||||||
import { ChevronDown, CircleFadingArrowUp, CloudUpload, Cpu, Download, Fullscreen, Gamepad2, Info, Monitor, Puzzle, Save, Settings, Settings2, Terminal, Trash2, TriangleAlert, WandSparkles } from "lucide-react";
|
import { ChevronDown, CircleFadingArrowUp, CloudUpload, Cpu, Download, Fullscreen, Gamepad2, Info, Monitor, Puzzle, Settings, Settings2, Terminal, Trash2, TriangleAlert, WandSparkles } from "lucide-react";
|
||||||
import { ContextList, DialogEntry, useContextDialog } from "@/mainview/components/ContextDialog";
|
import { ContextList, DialogEntry, useContextDialog } from "@/mainview/components/ContextDialog";
|
||||||
import { RPC_URL } from "@/shared/constants";
|
import { RPC_URL } from "@/shared/constants";
|
||||||
import Screenshots from "@/mainview/components/Screenshots";
|
import Screenshots from "@/mainview/components/Screenshots";
|
||||||
|
|
@ -29,6 +29,7 @@ import FocusTooltip from "@/mainview/components/FocusTooltip";
|
||||||
import { AutoFocus } from "@/mainview/components/AutoFocus";
|
import { AutoFocus } from "@/mainview/components/AutoFocus";
|
||||||
import { FilterUI } from "@/mainview/components/Filters";
|
import { FilterUI } from "@/mainview/components/Filters";
|
||||||
import Markdown from "react-markdown";
|
import Markdown from "react-markdown";
|
||||||
|
import { FrontEndEmulatorDetailed } from "@/shared/types";
|
||||||
|
|
||||||
export const Route = createFileRoute('/store/details/emulator/$id')({
|
export const Route = createFileRoute('/store/details/emulator/$id')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
import { createFileRoute, useSearch } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import { Joystick } from 'lucide-react';
|
import { Joystick } from 'lucide-react';
|
||||||
import { useContext, useEffect } from 'react';
|
import { useContext, useEffect } from 'react';
|
||||||
import { FocusContext, getCurrentFocusKey, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
import { FocusContext, getCurrentFocusKey, useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue