diff --git a/assets/jsons/translations/de.json b/assets/jsons/translations/de.json index a6c92687..d3772125 100644 --- a/assets/jsons/translations/de.json +++ b/assets/jsons/translations/de.json @@ -24,7 +24,9 @@ "unknown": "Ein unbekannter Fehler ist aufgetreten ¯\\_(ツ)_/¯", "warning": "Achtung", "continue": "Weiter", - "shared": "Geteilt" + "shared": "Geteilt", + "pin": "Anheften", + "unpin": "Lösen" }, "title-bar": { "outdated": "veraltet" @@ -48,11 +50,13 @@ "debug-description": "Aktiviert das Ausgabeprotokollfenster für IPA. Dies zeigt die Debug-Konsole, welche die Mods verwenden.", "outdated-tippy": "Diese Version ist veraltet, und einige Mods oder Funktionen funktionieren möglicherweise nicht wie erwartet. Es wird empfohlen, die empfohlene Version ({recommendedVersion}) von Beat Saber zu verwenden, um die neuesten Funktionen und Fehlerbehebungen zu genießen.", "advanced-launch": { - "button": "Erweiterter Start", + "button": "Startoptionen", "placeholder": "Weitere Argumente, bspw: --revert; --nowait" }, "skipsteam": "Steam überspringen", - "skipsteam-description": "Verhindert, dass Steam automatisch mit Beat Saber geöffnet wird. Aktivieren Sie dies, wenn Sie eine andere VR-Laufzeit wie WiVRn oder Monado verwenden, mit der SteamVR interferieren könnte." + "skipsteam-description": "Verhindert, dass Steam automatisch mit Beat Saber geöffnet wird. Aktivieren Sie dies, wenn Sie eine andere VR-Laufzeit wie WiVRn oder Monado verwenden, mit der SteamVR interferieren könnte.", + "map-editor": "Karteneditor", + "map-editor-description": "Starte den offiziellen Beat Saber-Karteneditor anstelle des Spiels." }, "maps": { "search-bar": { diff --git a/assets/jsons/translations/en.json b/assets/jsons/translations/en.json index 4715bc5b..1ef9eab9 100644 --- a/assets/jsons/translations/en.json +++ b/assets/jsons/translations/en.json @@ -24,7 +24,9 @@ "unknown": "An unknown error occurred ¯\\_(ツ)_/¯", "warning": "Warning", "continue": "Continue", - "shared": "Shared" + "shared": "Shared", + "pin": "Pin", + "unpin": "Unpin" }, "title-bar": { "outdated": "outdated" @@ -48,11 +50,13 @@ "debug-description": "Enables the output log window for IPA. This will show the debug console that mods use.", "outdated-tippy": "This version is outdated, and some mods or features may no longer work as expected. Prefer using the recommended version ({recommendedVersion}) of Beat Saber to enjoy the latest features and bugfixes.", "advanced-launch": { - "button": "Advanced launch", + "button": "Launch options", "placeholder": "Additional arguments ex: --revert; --nowait" }, "skipsteam": "Skip Steam", - "skipsteam-description": "Stops Steam from opening automatically with Beat Saber, enable if you are using a different VR runtime like WiVRn or Monado that SteamVR may interfere with." + "skipsteam-description": "Stops Steam from opening automatically with Beat Saber, enable if you are using a different VR runtime like WiVRn or Monado that SteamVR may interfere with.", + "map-editor": "Map Editor", + "map-editor-description": "Start the official Beat Saber map editor instead of the game." }, "maps": { "search-bar": { diff --git a/assets/jsons/translations/es.json b/assets/jsons/translations/es.json index a3685349..553790e0 100644 --- a/assets/jsons/translations/es.json +++ b/assets/jsons/translations/es.json @@ -24,7 +24,9 @@ "unknown": "Se ha producido un error desconocido ¯\\_(ツ)_/¯", "warning": "Advertencia", "continue": "Continuar", - "shared": "Compartido" + "shared": "Compartido", + "pin": "Fijar", + "unpin": "Desfijar" }, "title-bar": { "outdated": "obsoleto" @@ -48,7 +50,7 @@ "debug-description": "Habilita la ventana de registro de salida para IPA. Esto mostrará la consola de depuración que usan los mods.", "outdated-tippy": "Esta versión está desactualizada, y algunos mods o funciones pueden no funcionar como se espera. Es mejor usar la versión recomendada ({recommendedVersion}) de Beat Saber para disfrutar de las últimas características y correcciones.", "advanced-launch": { - "button": "Opciones avanzadas", + "button": "Opciones de lanzamiento", "placeholder": "Argumentos adicionales ej: --revert; --no-wait" } }, @@ -62,7 +64,9 @@ "delete-duplicate-maps": "Borrar duplicados" }, "skipsteam": "Saltar Steam", - "skipsteam-description": "Evita que Steam se abra automáticamente con Beat Saber, habilita esto si estás usando un runtime de VR diferente como WiVRn o Monado que SteamVR podría interferir." + "skipsteam-description": "Evita que Steam se abra automáticamente con Beat Saber, habilita esto si estás usando un runtime de VR diferente como WiVRn o Monado que SteamVR podría interferir.", + "map-editor": "Editor de mapas", + "map-editor-description": "Inicia el editor oficial de mapas de Beat Saber en lugar del juego." }, "tabs": { "maps": { diff --git a/assets/jsons/translations/fr.json b/assets/jsons/translations/fr.json index 53ba74ab..ec4d562e 100644 --- a/assets/jsons/translations/fr.json +++ b/assets/jsons/translations/fr.json @@ -24,7 +24,9 @@ "unknown": "Une erreur inconnue s'est produite ¯\\_(ツ)_/¯", "warning": "Attention", "continue": "Continuer", - "shared": "Partagé" + "shared": "Partagé", + "pin": "Épingler", + "unpin": "Désépingler" }, "title-bar": { "outdated": "obsolète" @@ -48,11 +50,13 @@ "debug-description": "Active la fenêtre de log pour IPA. Cela affichera la console de débogage utilisée par les mods.", "outdated-tippy": "Cette version est obsolète et certains mods ou fonctionnalités peuvent ne plus fonctionner comme prévu. Préférez utiliser la version recommandée ({recommendedVersion}) de Beat Saber pour profiter des dernières fonctionnalités et correctifs.", "advanced-launch": { - "button": "Lancement avancé", + "button": "Options de lancement", "placeholder": "Arguments supplémentaires ex: --revert; --nowait" }, "skipsteam": "Ignorer Steam", - "skipsteam-description": "Empêche Steam de s'ouvrir automatiquement avec Beat Saber, activez-le si vous utilisez un autre runtime VR comme WiVRn ou Monado avec lequel SteamVR pourrait interférer." + "skipsteam-description": "Empêche Steam de s'ouvrir automatiquement avec Beat Saber, activez-le si vous utilisez un autre runtime VR comme WiVRn ou Monado avec lequel SteamVR pourrait interférer.", + "map-editor": "Éditeur de map", + "map-editor-description": "Lance l'éditeur officiel de map de Beat Saber au lieu du jeu." }, "maps": { "search-bar": { diff --git a/assets/jsons/translations/ja.json b/assets/jsons/translations/ja.json index 54ffa8f5..d7d95c14 100644 --- a/assets/jsons/translations/ja.json +++ b/assets/jsons/translations/ja.json @@ -24,7 +24,9 @@ "unknown": "不明なエラーが発生しました (´・ω・`)", "warning": "警告", "continue": "続ける", - "shared": "共有" + "shared": "共有", + "pin": "固定", + "unpin": "固定解除" }, "title-bar": { "outdated": "時代遅れ" @@ -48,7 +50,7 @@ "debug-description": "IPAの出力ログウィンドウを有効にします。これはMODのデバッグコンソールを表示します。", "outdated-tippy": "このバージョンは古いため、一部のMODや機能が期待通りに動作しない可能性があります。最新の機能やバグ修正を楽しむには、推奨バージョン ({recommendedVersion}) のBeat Saberを使用することをお勧めします。", "advanced-launch": { - "button": "高度な起動", + "button": "起動オプション", "placeholder": "追加引数 例:--revert; --nowait" }, "skipsteam": "Steamをスキップ", diff --git a/assets/jsons/translations/ko.json b/assets/jsons/translations/ko.json index e3cd35ca..581aca3b 100644 --- a/assets/jsons/translations/ko.json +++ b/assets/jsons/translations/ko.json @@ -24,7 +24,9 @@ "unknown": "알 수 없는 오류가 발생했습니다 (´・ω・`)", "warning": "경고", "continue": "계속", - "shared": "공유" + "shared": "공유", + "pin": "고정", + "unpin": "고정 해제" }, "title-bar": { "outdated": "구식" @@ -48,11 +50,13 @@ "debug-description": "IPA 출력 로그 창을 활성화하여 MOD의 디버그 콘솔을 표시합니다.", "outdated-tippy": "이 버전은 오래되어 일부 모드나 기능이 예상대로 작동하지 않을 수 있습니다. 최신 기능과 버그 수정을 사용하려면 권장 버전({recommendedVersion})의 Beat Saber를 사용하는 것이 좋습니다.", "advanced-launch": { - "button": "고급 실행", + "button": "실행 옵션", "placeholder": "추가 인수 예: --revert; --nowait" }, "skipsteam": "Steam 실행 건너뛰기", - "skipsteam-description": "Beat Saber와 함께 Steam이 자동으로 열리는 것을 방지합니다. SteamVR이 간섭할 수 있는 WiVRn 또는 Monado와 같은 다른 VR 런타임을 사용하는 경우 활성화하세요." + "skipsteam-description": "Beat Saber와 함께 Steam이 자동으로 열리는 것을 방지합니다. SteamVR이 간섭할 수 있는 WiVRn 또는 Monado와 같은 다른 VR 런타임을 사용하는 경우 활성화하세요.", + "map-editor": "맵 편집기", + "map-editor-description": "게임 대신 공식 Beat Saber 맵 편집기를 실행해." }, "maps": { "search-bar": { diff --git a/assets/jsons/translations/ru.json b/assets/jsons/translations/ru.json index 45eecce3..09a9ea1f 100644 --- a/assets/jsons/translations/ru.json +++ b/assets/jsons/translations/ru.json @@ -24,7 +24,9 @@ "unknown": "Неизвестная ошибка ¯\\_(ツ)_/¯", "warning": "Внимание", "continue": "Продолжить", - "shared": "Общий" + "shared": "Общий", + "pin": "Закрепить", + "unpin": "Открепить" }, "title-bar": { "outdated": "устаревший" @@ -48,11 +50,13 @@ "debug-description": "Включает окно логов для IPA. В окне будут отображаться логи модов.", "outdated-tippy": "Эта версия устарела, и некоторые моды или функции могут работать не так, как ожидалось. Рекомендуется использовать рекомендуемую версию ({recommendedVersion}) Beat Saber, чтобы воспользоваться последними функциями и исправлениями ошибок.", "advanced-launch": { - "button": "Расширенный запуск", + "button": "Параметры запуска", "placeholder": "Параметры запуска, например: --revert; --nowait" }, "skipsteam": "Пропустить Steam", - "skipsteam-description": "Предотвращает автоматическое открытие Steam с Beat Saber, включите, если вы используете другую VR-среду, такую как WiVRn или Monado, с которой SteamVR может мешать." + "skipsteam-description": "Предотвращает автоматическое открытие Steam с Beat Saber, включите, если вы используете другую VR-среду, такую как WiVRn или Monado, с которой SteamVR может мешать.", + "map-editor": "Редактор карт", + "map-editor-description": "Запусти официальный редактор карт Beat Saber вместо игры." }, "maps": { "search-bar": { diff --git a/assets/jsons/translations/zh-tw.json b/assets/jsons/translations/zh-tw.json index fa0661d7..6bf2364b 100644 --- a/assets/jsons/translations/zh-tw.json +++ b/assets/jsons/translations/zh-tw.json @@ -24,7 +24,9 @@ "unknown": "發生了一個錯誤 ¯\\_(ツ)_/¯", "warning": "警告", "continue": "繼續", - "shared": "共享" + "shared": "共享", + "pin": "固定", + "unpin": "取消固定" }, "title-bar": { "outdated": "過時" @@ -48,11 +50,13 @@ "debug-description": "為 IPA 啟用輸出日誌窗口。 會顯示所用 Mod 的 debug 輸出", "outdated-tippy": "此版本已過時,某些模組或功能可能無法按預期運作。建議使用推薦版本的 Beat Saber ({recommendedVersion}),以享受最新功能和修復。", "advanced-launch": { - "button": "高級啟動", + "button": "啟動選項", "placeholder": "額外啟動參數,例如: --revert; --nowait" }, "skipsteam": "跳過 Steam", - "skipsteam-description": "防止 Steam 與 Beat Saber 自動打開,如果您使用的是其他 VR 執行時,如 WiVRn 或 Monado,SteamVR 可能會干擾,請啟用此選項。" + "skipsteam-description": "防止 Steam 與 Beat Saber 自動打開,如果您使用的是其他 VR 執行時,如 WiVRn 或 Monado,SteamVR 可能會干擾,請啟用此選項。", + "map-editor": "地圖編輯器", + "map-editor-description": "啟動官方的 Beat Saber 地圖編輯器,而不是遊戲。" }, "maps": { "search-bar": { diff --git a/assets/jsons/translations/zh.json b/assets/jsons/translations/zh.json index 9dc8c9c5..a84ec492 100644 --- a/assets/jsons/translations/zh.json +++ b/assets/jsons/translations/zh.json @@ -24,7 +24,9 @@ "Unknown": "发生了一个错误 ¯\\_(ツ)_/¯", "warning": "警告", "continue": "继续", - "shared": "共享" + "shared": "共享", + "pin": "固定", + "unpin": "取消固定" }, "title-bar": { "outdated": "过时" @@ -48,11 +50,13 @@ "debug-description": "为 IPA 启用输出日志窗口。 会显示所用 Mod 的 debug 输出", "outdated-tippy": "此版本已过时,某些模组或功能可能无法按预期运行。建议使用推荐版本的 Beat Saber ({recommendedVersion}),以享受最新功能和修复。", "advanced-launch": { - "button": "高级启动", + "button": "启动选项", "placeholder": "额外启动参数,例如: --revert; --nowait" }, "skipsteam": "跳过 Steam", - "skipsteam-description": "防止 Steam 与 Beat Saber 自动打开,如果您使用的是其他 VR 运行时,如 WiVRn 或 Monado,SteamVR 可能会干扰,请启用此选项。" + "skipsteam-description": "防止 Steam 与 Beat Saber 自动打开,如果您使用的是其他 VR 运行时,如 WiVRn 或 Monado,SteamVR 可能会干扰,请启用此选项。", + "map-editor": "地图编辑器", + "map-editor-description": "启动官方的 Beat Saber 地图编辑器,而不是游戏。" }, "maps": { "search-bar": { diff --git a/src/main/services/bs-launcher/abstract-launcher.service.ts b/src/main/services/bs-launcher/abstract-launcher.service.ts index 6ed46121..9f6d8a84 100644 --- a/src/main/services/bs-launcher/abstract-launcher.service.ts +++ b/src/main/services/bs-launcher/abstract-launcher.service.ts @@ -7,6 +7,7 @@ import { sToMs } from "../../../shared/helpers/time.helpers"; import { LinuxService } from "../linux.service"; import { bsmSpawn } from "main/helpers/os.helpers"; import { IS_FLATPAK } from "main/constants"; +import { LaunchMods } from "shared/models/bs-launch/launch-option.interface"; export abstract class AbstractLauncherService { @@ -24,16 +25,20 @@ export abstract class AbstractLauncherService { if(!launchOptions.version.steam && !launchOptions.version.oculus){ launchArgs.push("--no-yeet") } - if (launchOptions.oculus) { + if(launchOptions.launchMods?.includes(LaunchMods.OCULUS)) { launchArgs.push("-vrmode"); launchArgs.push("oculus"); } - if (launchOptions.desktop) { + if(launchOptions.launchMods?.includes(LaunchMods.FPFC)) { launchArgs.push("fpfc"); } - if (launchOptions.debug) { + if(launchOptions.launchMods?.includes(LaunchMods.DEBUG)) { launchArgs.push("--verbose"); } + if(launchOptions.launchMods?.includes(LaunchMods.EDITOR)) { + launchArgs.push("editor"); + } + if (launchOptions.additionalArgs) { launchArgs.push(...launchOptions.additionalArgs); } diff --git a/src/main/services/bs-launcher/bs-launcher.service.ts b/src/main/services/bs-launcher/bs-launcher.service.ts index 832b484e..5afe996e 100644 --- a/src/main/services/bs-launcher/bs-launcher.service.ts +++ b/src/main/services/bs-launcher/bs-launcher.service.ts @@ -20,6 +20,7 @@ import { SteamLauncherService } from "./steam-launcher.service"; import { OculusLauncherService } from "./oculus-launcher.service"; import { BSVersion } from "shared/bs-version.interface"; import { BsStore } from "../../../shared/models/bs-store.enum"; +import { LaunchMod, LaunchMods } from "shared/models/bs-launch/launch-option.interface"; export class BSLauncherService { private static instance: BSLauncherService; @@ -94,6 +95,14 @@ export class BSLauncherService { } private shortcutParamsToLaunchOption(params: ShortcutParams): LaunchOption{ + + const launchMods: LaunchMod[] = []; + + if(params.oculusMode === "true"){ launchMods.push(LaunchMods.OCULUS); } + if(params.desktopMode === "true"){ launchMods.push(LaunchMods.FPFC); } + if(params.debug === "true"){ launchMods.push(LaunchMods.DEBUG); } + if(params.skipSteam === "true"){ launchMods.push(LaunchMods.SKIP_STEAM); } + const res: LaunchOption = { version: { BSVersion: params.version, @@ -102,11 +111,8 @@ export class BSLauncherService { oculus: params.versionOculus === "true", ino: +params.versionIno }, - oculus: params.oculusMode === "true", - desktop: params.desktopMode === "true", - debug: params.debug === "true", additionalArgs: params.additionalArgs, - skipSteam: params.skipSteam === "true", + launchMods, }; return res; @@ -120,11 +126,11 @@ export class BSLauncherService { if(launchOptions.version.oculus){ res.versionOculus = `${launchOptions.version.oculus}`; } if(launchOptions.version.ino){ res.versionIno = `${launchOptions.version.ino}`; } - if(launchOptions.oculus){ res.oculusMode = "true"; } - if(launchOptions.desktop){ res.desktopMode = "true"; } - if(launchOptions.debug){ res.debug = "true"; } + if(launchOptions.launchMods?.includes(LaunchMods.OCULUS)){ res.oculusMode = "true"; } + if(launchOptions.launchMods?.includes(LaunchMods.FPFC)){ res.desktopMode = "true"; } + if(launchOptions.launchMods?.includes(LaunchMods.DEBUG)){ res.debug = "true"; } if(launchOptions.additionalArgs){ res.additionalArgs = launchOptions.additionalArgs; } - if(launchOptions.skipSteam){ res.skipSteam = "true"; } + if(launchOptions.launchMods?.includes(LaunchMods.SKIP_STEAM)){ res.skipSteam = "true"; } return res; } diff --git a/src/main/services/bs-launcher/steam-launcher.service.ts b/src/main/services/bs-launcher/steam-launcher.service.ts index e8a7106b..fdae1e1c 100644 --- a/src/main/services/bs-launcher/steam-launcher.service.ts +++ b/src/main/services/bs-launcher/steam-launcher.service.ts @@ -10,6 +10,7 @@ import { AbstractLauncherService } from "./abstract-launcher.service"; import { CustomError } from "../../../shared/models/exceptions/custom-error.class"; import { UtilsService } from "../utils.service"; import { exec } from "child_process"; +import { LaunchMods } from "shared/models/bs-launch/launch-option.interface"; export class SteamLauncherService extends AbstractLauncherService implements StoreLauncherInterface{ @@ -73,7 +74,7 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto throw CustomError.fromError(new Error(`Path not exist : ${bsExePath}`), BSLaunchError.BS_NOT_FOUND); } - const skipSteam: boolean = launchOptions.skipSteam ?? false; + const skipSteam: boolean = launchOptions.launchMods?.includes(LaunchMods.SKIP_STEAM) ?? false; // Open Steam if not running if(!skipSteam && !(await this.steam.isSteamRunning())){ @@ -87,12 +88,12 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto obs.next({type: BSLaunchWarning.UNABLE_TO_LAUNCH_STEAM}); }); } - else if (launchOptions.skipSteam) { + else { obs.next({ type: BSLaunchEvent.SKIPPING_STEAM_LAUNCH}); } // Backup SteamVR when desktop mode is enabled - if(launchOptions.desktop){ + if(launchOptions.launchMods?.includes(LaunchMods.FPFC)){ await this.backupSteamVR().catch(() => { return this.restoreSteamVR(); }); diff --git a/src/renderer/components/nav-bar/nav-bar-items/bs-version-item.component.tsx b/src/renderer/components/nav-bar/nav-bar-items/bs-version-item.component.tsx index 8c4b3f52..8117d0aa 100644 --- a/src/renderer/components/nav-bar/nav-bar-items/bs-version-item.component.tsx +++ b/src/renderer/components/nav-bar/nav-bar-items/bs-version-item.component.tsx @@ -2,7 +2,7 @@ import { BSVersion } from "shared/bs-version.interface"; import { Link, useLocation } from "react-router-dom"; import { useState } from "react"; import { distinctUntilChanged, lastValueFrom, map, of, Subscription, switchMap, take } from "rxjs"; -import { BSLauncherService, LaunchMods } from "renderer/services/bs-launcher.service"; +import { BSLauncherService } from "renderer/services/bs-launcher.service"; import { ConfigurationService } from "renderer/services/configuration.service"; import { BSUninstallerService } from "renderer/services/bs-uninstaller.service"; import { BSVersionManagerService } from "renderer/services/bs-version-manager.service"; @@ -60,10 +60,7 @@ export function BsVersionItem(props: { version: BSVersion }) { if(equal(downloadingVersion, props.version)){ return; } const launch$ = launcherService.launch({ version: state, - oculus: !!configService.get(LaunchMods.OCULUS_MOD), - desktop: !!configService.get(LaunchMods.DESKTOP_MOD), - debug: !!configService.get(LaunchMods.DEBUG_MOD), - skipSteam: !!configService.get(LaunchMods.SKIPSTEAM_MOD), + launchMods: configService.get("launch-mods") ?? [], }); return lastValueFrom(launch$).catch(() => {}); }; diff --git a/src/renderer/components/svgs/bsm-icon.component.tsx b/src/renderer/components/svgs/bsm-icon.component.tsx index bb0a666f..237616e3 100644 --- a/src/renderer/components/svgs/bsm-icon.component.tsx +++ b/src/renderer/components/svgs/bsm-icon.component.tsx @@ -16,7 +16,7 @@ import { TaskIcon } from "./icons/task-icon.component"; import { CopyIcon } from "./icons/copy-icon.component"; import { SteamIcon } from "./icons/steam-icon.component"; import { CSSProperties, memo } from "react"; -import EditIcon from "./icons/edit-icon.component"; +import { EditIcon } from "./icons/edit-icon.component"; import { ExportIcon } from "./icons/export-icon.component"; import PatreonIcon from "./icons/patreon-icon.component"; import { SearchIcon } from "./icons/search-icon.component"; diff --git a/src/renderer/components/svgs/icons/desktop-icon.component.tsx b/src/renderer/components/svgs/icons/desktop-icon.component.tsx index 367ba71f..56aa8af5 100644 --- a/src/renderer/components/svgs/icons/desktop-icon.component.tsx +++ b/src/renderer/components/svgs/icons/desktop-icon.component.tsx @@ -1,9 +1,9 @@ -import { CSSProperties } from "react"; +import { createSvgIcon } from "../svg-icon.type"; -export function DesktopIcon(props: { className?: string; style?: CSSProperties }) { +export const DesktopIcon = createSvgIcon((props, ref) => { return ( - + ); -} +}) diff --git a/src/renderer/components/svgs/icons/edit-icon.component.tsx b/src/renderer/components/svgs/icons/edit-icon.component.tsx index 9523437e..6f8cc7d2 100644 --- a/src/renderer/components/svgs/icons/edit-icon.component.tsx +++ b/src/renderer/components/svgs/icons/edit-icon.component.tsx @@ -1,9 +1,9 @@ -import { CSSProperties } from "react"; +import { createSvgIcon } from "../svg-icon.type"; -export default function EditIcon(props: { className?: string; style?: CSSProperties }) { +export const EditIcon = createSvgIcon((props, ref) => { return ( - + ); -} +}) diff --git a/src/renderer/components/svgs/icons/oculus-icon.component.tsx b/src/renderer/components/svgs/icons/oculus-icon.component.tsx index dc2484fc..d2736bc5 100644 --- a/src/renderer/components/svgs/icons/oculus-icon.component.tsx +++ b/src/renderer/components/svgs/icons/oculus-icon.component.tsx @@ -1,12 +1,12 @@ -import { HTMLProps } from "react"; +import { createSvgIcon } from "../svg-icon.type"; -export function OculusIcon(props: HTMLProps) { +export const OculusIcon = createSvgIcon((props, ref) => { return ( - + ); -} +}) diff --git a/src/renderer/components/svgs/icons/pin-icon.component.tsx b/src/renderer/components/svgs/icons/pin-icon.component.tsx new file mode 100644 index 00000000..42a580aa --- /dev/null +++ b/src/renderer/components/svgs/icons/pin-icon.component.tsx @@ -0,0 +1,9 @@ +import { createSvgIcon } from "../svg-icon.type"; + +export const PinIcon = createSvgIcon((props, ref) => { + return ( + + + + ) +}); diff --git a/src/renderer/components/svgs/icons/terminal-icon.component.tsx b/src/renderer/components/svgs/icons/terminal-icon.component.tsx index a553809c..ac930525 100644 --- a/src/renderer/components/svgs/icons/terminal-icon.component.tsx +++ b/src/renderer/components/svgs/icons/terminal-icon.component.tsx @@ -1,9 +1,9 @@ -import { CSSProperties } from "react"; +import { createSvgIcon } from "../svg-icon.type"; -export function TerminalIcon(props: { className?: string; style?: CSSProperties }) { +export const TerminalIcon = createSvgIcon((props, ref) => { return ( - + ); -} +}) diff --git a/src/renderer/components/svgs/icons/unpin-icon.component.tsx b/src/renderer/components/svgs/icons/unpin-icon.component.tsx new file mode 100644 index 00000000..bf38bd5b --- /dev/null +++ b/src/renderer/components/svgs/icons/unpin-icon.component.tsx @@ -0,0 +1,9 @@ +import { createSvgIcon } from "../svg-icon.type" + +export const UnpinIcon = createSvgIcon((props, ref) => { + return ( + + + + ) +}) diff --git a/src/renderer/components/version-viewer/slides/launch/launch-mod-toogle.component.tsx b/src/renderer/components/version-viewer/slides/launch/launch-mod-toogle.component.tsx index 990c0083..e560a7a2 100644 --- a/src/renderer/components/version-viewer/slides/launch/launch-mod-toogle.component.tsx +++ b/src/renderer/components/version-viewer/slides/launch/launch-mod-toogle.component.tsx @@ -1,33 +1,34 @@ import Tippy from "@tippyjs/react"; import { GlowEffect } from "renderer/components/shared/glow-effect.component"; -import { BsmIcon, BsmIconType } from "renderer/components/svgs/bsm-icon.component"; +import { BsmIcon } from "renderer/components/svgs/bsm-icon.component"; +import { SvgIcon } from "renderer/components/svgs/svg-icon.type"; import { useTranslation } from "renderer/hooks/use-translation.hook"; type Props = { onClick: (active: boolean) => void; active: boolean; text: string; - icon: BsmIconType; + icon?: SvgIcon; infoText?: string; }; -export function LaunchModToogle({ onClick, active, text, icon, infoText }: Props) { +export function LaunchModToogle({ onClick, active, text, icon: Icon, infoText }: Props) { const t = useTranslation(); return ( -
onClick(!active)}> +
+ ); } diff --git a/src/renderer/components/version-viewer/slides/launch/launch-options-panel.component.tsx b/src/renderer/components/version-viewer/slides/launch/launch-options-panel.component.tsx new file mode 100644 index 00000000..db2cf231 --- /dev/null +++ b/src/renderer/components/version-viewer/slides/launch/launch-options-panel.component.tsx @@ -0,0 +1,71 @@ +import Tippy from "@tippyjs/react"; +import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component"; +import { PinIcon } from "renderer/components/svgs/icons/pin-icon.component"; +import { UnpinIcon } from "renderer/components/svgs/icons/unpin-icon.component"; +import { SvgIcon } from "renderer/components/svgs/svg-icon.type"; +import { cn } from "renderer/helpers/css-class.helpers"; +import { useTranslationV2 } from "renderer/hooks/use-translation.hook"; +import { LaunchMod } from "shared/models/bs-launch/launch-option.interface"; + +type Props = { + readonly className?: string; + readonly open: boolean; + readonly launchArgs?: string; + readonly launchMods?: LaunchModItemProps[]; + readonly onLaunchArgsChange?: (args: string) => void; +}; + +export function LaunchOptionsPanel({ className, open, launchArgs, launchMods, onLaunchArgsChange }: Props) { + + const {text: t} = useTranslationV2(); + + return ( +
+
+
+ onLaunchArgsChange(e.target.value)}/> +
+ {launchMods?.map((mod) => ( + + ))} +
+
+
+
+ ) +} + +export type LaunchModItemProps = { + readonly id: LaunchMod; + readonly icon?: SvgIcon; + readonly label: string; + readonly description: string; + readonly active: boolean; + readonly visible?: boolean; + readonly pinned?: boolean; + readonly onChange?: (val: boolean) => void; + readonly onPinChange?: (val: boolean) => void; +} + +export function LaunchModItem({ id, icon: Icon, label, description, active, visible, pinned, onChange, onPinChange }: LaunchModItemProps) { + + const { text: t } = useTranslationV2(); + + return ( + + + + )} + + + ); + +} diff --git a/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx b/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx index bd84b713..b705b132 100644 --- a/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx +++ b/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx @@ -1,8 +1,8 @@ -import { ChangeEvent, useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { BsmButton } from "renderer/components/shared/bsm-button.component"; import { useObservable } from "renderer/hooks/use-observable.hook"; import { useTranslationV2 } from "renderer/hooks/use-translation.hook"; -import { BSLauncherService, LaunchMods } from "renderer/services/bs-launcher.service"; +import { BSLauncherService } from "renderer/services/bs-launcher.service"; import { ConfigurationService } from "renderer/services/configuration.service"; import { BSVersion } from "shared/bs-version.interface"; import { LaunchModToogle } from "./launch-mod-toogle.component"; @@ -19,6 +19,13 @@ import { BSVersionManagerService } from "renderer/services/bs-version-manager.se import { safeLt } from "shared/helpers/semver.helpers"; import { WarningIcon } from "renderer/components/svgs/icons/warning-icon.component"; import Tippy from "@tippyjs/react"; +import { LaunchModItemProps, LaunchOptionsPanel } from "./launch-options-panel.component"; +import { LaunchMod, LaunchMods } from "shared/models/bs-launch/launch-option.interface"; +import { OculusIcon } from "renderer/components/svgs/icons/oculus-icon.component"; +import { DesktopIcon } from "renderer/components/svgs/icons/desktop-icon.component"; +import { TerminalIcon } from "renderer/components/svgs/icons/terminal-icon.component"; +import { DefaultConfigKey } from "renderer/config/default-configuration.config"; +import { EditIcon } from "renderer/components/svgs/icons/edit-icon.component"; type Props = { version: BSVersion }; @@ -30,13 +37,12 @@ export function LaunchSlide({ version }: Props) { const bsDownloader = useService(BsDownloaderService); const versions = useService(BSVersionManagerService); - const [oculusMode, setOculusMode] = useState(!!configService.get(LaunchMods.OCULUS_MOD)); - const [desktopMode, setDesktopMode] = useState(!!configService.get(LaunchMods.DESKTOP_MOD)); - const [debugMode, setDebugMode] = useState(!!configService.get(LaunchMods.DEBUG_MOD)); const [advancedLaunch, setAdvancedLaunch] = useState(false); const [additionalArgsString, setAdditionalArgsString] = useState(configService.get("additionnal-args") || ""); - const [skipSteamMode, setSkipSteamMode] = useState(!!configService.get(LaunchMods.SKIPSTEAM_MOD)); const versionDownloading = useObservable(() => bsDownloader.downloadingVersion$); + const [activeLaunchMods, setActiveLaunchMods] = useState(configService.get("launch-mods") ?? []); + const [pinnedLaunchMods, setPinnedLaunchMods] = useState(configService.get("pinned-launch-mods" as DefaultConfigKey) ?? []); + const [launchModItems, setLaunchModItems] = useState([]); const versionRunning = useObservable(() => bsLauncherService.versionRunning$); @@ -45,36 +51,81 @@ export function LaunchSlide({ version }: Props) { }, [additionalArgsString]); useEffect(() => { - if(desktopMode){ return; } - bsLauncherService.restoreSteamVR(); - }, [desktopMode]); + configService.set("pinned-launch-mods", pinnedLaunchMods); + }, [pinnedLaunchMods]) - const setMode = (mode: LaunchMods, value: boolean) => { + useEffect(() => { + configService.set("launch-mods", activeLaunchMods); - if (mode === LaunchMods.DEBUG_MOD) { - setDebugMode(value); - } else if (mode === LaunchMods.OCULUS_MOD) { - setOculusMode(value); - } else if (mode === LaunchMods.DESKTOP_MOD) { - setDesktopMode(value); - } else if (mode === LaunchMods.SKIPSTEAM_MOD) { - setSkipSteamMode(value); + if(!activeLaunchMods.includes("fpfc")){ + bsLauncherService.restoreSteamVR(); } - configService.set(mode, value); - }; + }, [activeLaunchMods]); - const handleAdditionalArgsChange = (e: ChangeEvent) => setAdditionalArgsString(() => e.target.value); + useEffect(() => { + setLaunchModItems(() => [ + { + id: LaunchMods.OCULUS, + icon: OculusIcon, + label: t("pages.version-viewer.launch-mods.oculus"), + description: t("pages.version-viewer.launch-mods.oculus-description"), + active: activeLaunchMods.includes(LaunchMods.OCULUS), + visible: !(version.metadata?.store === BsStore.OCULUS), + pinned: pinnedLaunchMods.includes(LaunchMods.OCULUS), + onChange: checked => checked ? setActiveLaunchMods(prev => [...prev, LaunchMods.OCULUS]) : setActiveLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.OCULUS)), + onPinChange: pinned => pinned ? setPinnedLaunchMods(prev => [...prev, LaunchMods.OCULUS]) : setPinnedLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.OCULUS)), + }, + { + id: LaunchMods.FPFC, + icon: DesktopIcon, + label: t("pages.version-viewer.launch-mods.desktop"), + description: t("pages.version-viewer.launch-mods.desktop-description"), + active: activeLaunchMods.includes(LaunchMods.FPFC), + pinned: pinnedLaunchMods.includes(LaunchMods.FPFC), + onChange: checked => checked ? setActiveLaunchMods(prev => [...prev, LaunchMods.FPFC]) : setActiveLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.FPFC)), + onPinChange: pinned => pinned ? setPinnedLaunchMods(prev => [...prev, LaunchMods.FPFC]) : setPinnedLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.FPFC)), + }, + { + id: LaunchMods.DEBUG, + icon: TerminalIcon, + label: t("pages.version-viewer.launch-mods.debug"), + description: t("pages.version-viewer.launch-mods.debug-description"), + active: activeLaunchMods.includes(LaunchMods.DEBUG), + pinned: pinnedLaunchMods.includes(LaunchMods.DEBUG), + onChange: checked => checked ? setActiveLaunchMods(prev => [...prev, LaunchMods.DEBUG]) : setActiveLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.DEBUG)), + onPinChange: pinned => pinned ? setPinnedLaunchMods(prev => [...prev, LaunchMods.DEBUG]) : setPinnedLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.DEBUG)), + }, + { + id: LaunchMods.SKIP_STEAM, + label: t("pages.version-viewer.launch-mods.skipsteam"), + description: t("pages.version-viewer.launch-mods.skipsteam-description"), + active: activeLaunchMods.includes(LaunchMods.SKIP_STEAM), + pinned: pinnedLaunchMods.includes(LaunchMods.SKIP_STEAM), + onChange: checked => checked ? setActiveLaunchMods(prev => [...prev, LaunchMods.SKIP_STEAM]) : setActiveLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.SKIP_STEAM)), + onPinChange: pinned => pinned ? setPinnedLaunchMods(prev => [...prev, LaunchMods.SKIP_STEAM]) : setPinnedLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.SKIP_STEAM)), + }, + { + id: LaunchMods.EDITOR, + icon: EditIcon, + label: t("pages.version-viewer.launch-mods.map-editor"), + description: t("pages.version-viewer.launch-mods.map-editor-description"), + active: activeLaunchMods.includes(LaunchMods.EDITOR), + pinned: pinnedLaunchMods.includes(LaunchMods.EDITOR), + visible: !safeLt(version.BSVersion, "1.23.0"), + onChange: checked => checked ? setActiveLaunchMods(prev => [...prev, LaunchMods.EDITOR]) : setActiveLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.EDITOR)), + onPinChange: pinned => pinned ? setPinnedLaunchMods(prev => [...prev, LaunchMods.EDITOR]) : setPinnedLaunchMods(prev => prev.filter(mod => mod !== LaunchMods.EDITOR)), + + } + ]); + }, [activeLaunchMods, pinnedLaunchMods, version]); const launch = () => { const additionalArgs = additionalArgsString?.split(";").map(arg => arg.trim()).filter(arg => arg.length > 0); const launch$ = bsLauncherService.launch({ version, - oculus: version.oculus ? false : oculusMode, - desktop: desktopMode, - debug: debugMode, + launchMods: activeLaunchMods, additionalArgs: advancedLaunch ? additionalArgs : [], - skipSteam: skipSteamMode, }); return lastValueFrom(launch$).catch(() => {}); @@ -85,9 +136,11 @@ export function LaunchSlide({ version }: Props) { }, [version]); return ( -
-
- +
+
+
+ +

{version.name ? `${version.BSVersion} - ${version.name}` : version.BSVersion} {isOutdated && ( @@ -99,14 +152,23 @@ export function LaunchSlide({ version }: Props) { )}

-
- {!(version.oculus || version.metadata?.store === BsStore.OCULUS) && setMode(LaunchMods.OCULUS_MOD, !oculusMode)} active={oculusMode} text="pages.version-viewer.launch-mods.oculus" />} - setMode(LaunchMods.DESKTOP_MOD, !desktopMode)} active={desktopMode} text="pages.version-viewer.launch-mods.desktop" /> - setMode(LaunchMods.DEBUG_MOD, !debugMode)} active={debugMode} text="pages.version-viewer.launch-mods.debug" /> +
+ {pinnedLaunchMods.map(id => launchModItems.find(mod => mod.id === id)).map(launchMod => { + if(launchMod?.visible === false || !launchMod?.pinned) { return undefined; } + return ( + launchMod.onChange(!launchMod.active)} + /> + ); + })}
-
+
- +
-
- -
-
- {advancedLaunch && !(version.oculus || version.metadata?.store === BsStore.OCULUS) && setMode(LaunchMods.SKIPSTEAM_MOD, !skipSteamMode)} active={skipSteamMode} text="pages.version-viewer.launch-mods.skipsteam" />} -
-
+ +
diff --git a/src/renderer/config/default-configuration.config.ts b/src/renderer/config/default-configuration.config.ts index fedbfcdc..81c3b0fa 100644 --- a/src/renderer/config/default-configuration.config.ts +++ b/src/renderer/config/default-configuration.config.ts @@ -1,3 +1,4 @@ +import { LaunchMod, LaunchMods } from "shared/models/bs-launch/launch-option.interface"; import { BsvSearchOrder } from "shared/models/maps/beat-saver.model"; // NOTE: To refactor. Rename to LocalStorageConfigKeyValues since these are stored in the @@ -12,6 +13,7 @@ export interface DefaultConfigKeyValues { "default-shared-folders": string[]; "playlist-sort-order": BsvSearchOrder; "map-sort-order": BsvSearchOrder; + "pinned-launch-mods": LaunchMod[]; }; export type DefaultConfigKey = keyof DefaultConfigKeyValues; @@ -31,6 +33,7 @@ export const defaultConfiguration: { ], "playlist-sort-order": BsvSearchOrder.Relevance, "map-sort-order": BsvSearchOrder.Relevance, + "pinned-launch-mods": [LaunchMods.OCULUS, LaunchMods.DEBUG] }; export type ThemeConfig = "dark" | "light" | "os"; diff --git a/src/renderer/services/bs-launcher.service.ts b/src/renderer/services/bs-launcher.service.ts index 4361779d..3c78ff16 100644 --- a/src/renderer/services/bs-launcher.service.ts +++ b/src/renderer/services/bs-launcher.service.ts @@ -47,11 +47,8 @@ export class BSLauncherService { public getLaunchOptions(version: BSVersion): LaunchOption{ return { version, - oculus: this.config.get(LaunchMods.OCULUS_MOD), - desktop: this.config.get(LaunchMods.DESKTOP_MOD), - debug: this.config.get(LaunchMods.DEBUG_MOD), + launchMods: this.config.get("launch-mods") ?? [], additionalArgs: (this.config.get("additionnal-args") || "").split(";").map(arg => arg.trim()).filter(arg => arg.length > 0), - skipSteam: this.config.get(LaunchMods.SKIPSTEAM_MOD), } } @@ -124,10 +121,3 @@ export class BSLauncherService { return lastValueFrom(this.ipcService.sendV2("bs-launch.restore-steamvr")); } } - -export enum LaunchMods { - OCULUS_MOD = "LAUNCH_OCULUS_MOD", - DESKTOP_MOD = "LAUNCH_DESKTOP_MOD", - DEBUG_MOD = "LAUNCH_DEBUG_MOD", - SKIPSTEAM_MOD = "LAUNCH_SKIPSTEAM_MOD", -} diff --git a/src/shared/models/bs-launch/launch-option.interface.ts b/src/shared/models/bs-launch/launch-option.interface.ts index af972b23..21455b5a 100644 --- a/src/shared/models/bs-launch/launch-option.interface.ts +++ b/src/shared/models/bs-launch/launch-option.interface.ts @@ -1,11 +1,18 @@ import { BSVersion } from "shared/bs-version.interface"; +export const LaunchMods = { + OCULUS: "oculus", + FPFC: "fpfc", + DEBUG: "debug", + SKIP_STEAM: "skip_steam", + EDITOR: "editor", +} as const; + +export type LaunchMod = typeof LaunchMods[keyof typeof LaunchMods]; + export interface LaunchOption { version: BSVersion, - oculus?: boolean, - desktop?: boolean, - debug?: boolean, + launchMods?: LaunchMod[], additionalArgs?: string[], - admin?: boolean, - skipSteam?: boolean + admin?: boolean }