diff --git a/Cargo.lock b/Cargo.lock index 2ee6be34dc..d275cd47d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9024,6 +9024,7 @@ dependencies = [ "tracing", "tracing-error", "tracing-subscriber", + "ts-rs", "url", "uuid 1.12.0", "whoami", @@ -9062,6 +9063,7 @@ dependencies = [ "tokio", "tracing", "tracing-error", + "ts-rs", "url", "uuid 1.12.0", "window-shadows", @@ -9605,6 +9607,31 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ts-rs" +version = "10.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e640d9b0964e9d39df633548591090ab92f7a4567bc31d3891af23471a3365c6" +dependencies = [ + "chrono", + "lazy_static", + "thiserror 2.0.7", + "ts-rs-macros", + "uuid 1.12.0", +] + +[[package]] +name = "ts-rs-macros" +version = "10.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9d8656589772eeec2cf7a8264d9cda40fb28b9bc53118ceb9e8c07f8f38730" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "termcolor", +] + [[package]] name = "tungstenite" version = "0.23.0" diff --git a/apps/app-frontend/.prettierignore b/apps/app-frontend/.prettierignore index 581edad3d9..8dbe7d0fb3 100644 --- a/apps/app-frontend/.prettierignore +++ b/apps/app-frontend/.prettierignore @@ -1 +1,2 @@ **/dist +src/helpers/*-types.d.ts \ No newline at end of file diff --git a/apps/app-frontend/eslint.config.mjs b/apps/app-frontend/eslint.config.mjs index 05f559424b..3b987a709e 100644 --- a/apps/app-frontend/eslint.config.mjs +++ b/apps/app-frontend/eslint.config.mjs @@ -2,21 +2,26 @@ import { createConfigForNuxt } from '@nuxt/eslint-config/flat' import { fixupPluginRules } from '@eslint/compat' import turboPlugin from 'eslint-plugin-turbo' -export default createConfigForNuxt().append([ - { - name: 'turbo', - plugins: { - turbo: fixupPluginRules(turboPlugin), +export default createConfigForNuxt().append( + [ + { + name: 'turbo', + plugins: { + turbo: fixupPluginRules(turboPlugin), + }, + rules: { + 'turbo/no-undeclared-env-vars': 'error', + }, }, - rules: { - 'turbo/no-undeclared-env-vars': 'error', + { + name: 'modrinth', + rules: { + 'vue/html-self-closing': 'off', + 'vue/multi-word-component-names': 'off', + }, }, - }, + ], { - name: 'modrinth', - rules: { - 'vue/html-self-closing': 'off', - 'vue/multi-word-component-names': 'off', - }, + ignores: ['src/helpers/*-types.d.ts'], }, -]) +) diff --git a/apps/app-frontend/src/components/GridDisplay.vue b/apps/app-frontend/src/components/GridDisplay.vue index 6e5dac5d87..ccf1dabd63 100644 --- a/apps/app-frontend/src/components/GridDisplay.vue +++ b/apps/app-frontend/src/components/GridDisplay.vue @@ -16,7 +16,7 @@ import { Button, DropdownSelect } from '@modrinth/ui' import { formatCategoryHeader } from '@modrinth/utils' import ContextMenu from '@/components/ui/ContextMenu.vue' import dayjs from 'dayjs' -import { duplicate, remove } from '@/helpers/profile.js' +import { duplicate, remove } from '@/helpers/profile' import { handleError } from '@/store/notifications.js' import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue' diff --git a/apps/app-frontend/src/components/RowDisplay.vue b/apps/app-frontend/src/components/RowDisplay.vue index 080301de3c..75c6423ac5 100644 --- a/apps/app-frontend/src/components/RowDisplay.vue +++ b/apps/app-frontend/src/components/RowDisplay.vue @@ -19,7 +19,7 @@ import ContextMenu from '@/components/ui/ContextMenu.vue' import ProjectCard from '@/components/ui/ProjectCard.vue' import { get_by_profile_path } from '@/helpers/process.js' import { handleError } from '@/store/notifications.js' -import { duplicate, kill, remove, run } from '@/helpers/profile.js' +import { duplicate, kill, remove, run } from '@/helpers/profile' import { useRouter } from 'vue-router' import { showProfileInFolder } from '@/helpers/utils.js' import { trackEvent } from '@/helpers/analytics' diff --git a/apps/app-frontend/src/components/ui/AddContentButton.vue b/apps/app-frontend/src/components/ui/AddContentButton.vue index 414634c92d..f4196edc5c 100644 --- a/apps/app-frontend/src/components/ui/AddContentButton.vue +++ b/apps/app-frontend/src/components/ui/AddContentButton.vue @@ -2,7 +2,7 @@ import { DropdownIcon, PlusIcon, FolderOpenIcon } from '@modrinth/assets' import { ButtonStyled, OverflowMenu } from '@modrinth/ui' import { open } from '@tauri-apps/plugin-dialog' -import { add_project_from_path } from '@/helpers/profile.js' +import { add_project_from_path } from '@/helpers/profile' import { handleError } from '@/store/notifications.js' import { useRouter } from 'vue-router' diff --git a/apps/app-frontend/src/components/ui/ErrorModal.vue b/apps/app-frontend/src/components/ui/ErrorModal.vue index c3beffa9fb..2448547bb0 100644 --- a/apps/app-frontend/src/components/ui/ErrorModal.vue +++ b/apps/app-frontend/src/components/ui/ErrorModal.vue @@ -15,7 +15,7 @@ import { login as login_flow, set_default_user } from '@/helpers/auth.js' import { handleError } from '@/store/notifications.js' import { handleSevereError } from '@/store/error.js' import { cancel_directory_change } from '@/helpers/settings.js' -import { install } from '@/helpers/profile.js' +import { install } from '@/helpers/profile' import { trackEvent } from '@/helpers/analytics' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' diff --git a/apps/app-frontend/src/components/ui/ExportModal.vue b/apps/app-frontend/src/components/ui/ExportModal.vue index 0b96afb113..17ea569176 100644 --- a/apps/app-frontend/src/components/ui/ExportModal.vue +++ b/apps/app-frontend/src/components/ui/ExportModal.vue @@ -3,7 +3,7 @@ import { XIcon, PlusIcon } from '@modrinth/assets' import { Button, Checkbox } from '@modrinth/ui' import { PackageIcon, VersionIcon } from '@/assets/icons' import { ref } from 'vue' -import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js' +import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile' import { open } from '@tauri-apps/plugin-dialog' import { handleError } from '@/store/notifications.js' import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue' diff --git a/apps/app-frontend/src/components/ui/RunningAppBar.vue b/apps/app-frontend/src/components/ui/RunningAppBar.vue index 12e3b0dd81..e9d1104441 100644 --- a/apps/app-frontend/src/components/ui/RunningAppBar.vue +++ b/apps/app-frontend/src/components/ui/RunningAppBar.vue @@ -106,7 +106,7 @@ import { useRouter } from 'vue-router' import { progress_bars_list } from '@/helpers/state.js' import ProgressBar from '@/components/ui/ProgressBar.vue' import { handleError } from '@/store/notifications.js' -import { get_many } from '@/helpers/profile.js' +import { get_many } from '@/helpers/profile' import { trackEvent } from '@/helpers/analytics' const router = useRouter() diff --git a/apps/app-frontend/src/components/ui/instance_settings/GeneralSettings.vue b/apps/app-frontend/src/components/ui/instance_settings/GeneralSettings.vue index 6d035dce6d..ec01412bf7 100644 --- a/apps/app-frontend/src/components/ui/instance_settings/GeneralSettings.vue +++ b/apps/app-frontend/src/components/ui/instance_settings/GeneralSettings.vue @@ -10,7 +10,8 @@ import { open } from '@tauri-apps/plugin-dialog' import { defineMessages, useVIntl } from '@vintl/vintl' import { useRouter } from 'vue-router' import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue' -import type { InstanceSettingsTabProps, GameInstance } from '../../../helpers/types' +import type { InstanceSettingsTabProps } from '../../../helpers/types' +import type { Profile } from '@/helpers/lib-types' const { formatMessage } = useVIntl() const router = useRouter() @@ -35,7 +36,7 @@ async function duplicateProfile() { }) } -const allInstances = ref((await list()) as GameInstance[]) +const allInstances = ref((await list()) as Profile[]) const availableGroups = computed(() => [ ...new Set([...allInstances.value.flatMap((instance) => instance.groups), ...groups.value]), ]) diff --git a/apps/app-frontend/src/helpers/api-types.d.ts b/apps/app-frontend/src/helpers/api-types.d.ts new file mode 100644 index 0000000000..0486f07ced --- /dev/null +++ b/apps/app-frontend/src/helpers/api-types.d.ts @@ -0,0 +1,8 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Hooks } from "./lib-types.d"; +import type { LinkedData } from "./lib-types.d"; +import type { MemorySettings } from "./lib-types.d"; +import type { ModLoader } from "./lib-types.d"; +import type { WindowSize } from "./lib-types.d"; + +export type EditProfile = { name: string | null, game_version: string | null, loader: ModLoader | null, loader_version: string | null | null, groups: Array | null, linked_data: LinkedData | null | null, java_path: string | null | null, extra_launch_args: Array | null | null, custom_env_vars: Array<[string, string]> | null | null, memory: MemorySettings | null | null, force_fullscreen: boolean | null | null, game_resolution: WindowSize | null | null, hooks: Hooks | null, }; diff --git a/apps/app-frontend/src/helpers/lib-types.d.ts b/apps/app-frontend/src/helpers/lib-types.d.ts new file mode 100644 index 0000000000..2ddaad087a --- /dev/null +++ b/apps/app-frontend/src/helpers/lib-types.d.ts @@ -0,0 +1,36 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CacheBehaviour = "stale_while_revalidate_skip_offline" | "stale_while_revalidate" | "must_revalidate" | "bypass"; + +export type FileMetadata = { project_id: string, version_id: string, }; + +/** + * Game initialization hooks + */ +export type Hooks = { pre_launch: string | null, wrapper: string | null, post_exit: string | null, }; + +export type JavaVersion = { major_version: number, version: string, architecture: string, path: string, }; + +export type LinkedData = { project_id: string, version_id: string, locked: boolean, }; + +/** + * Minecraft memory settings + */ +export type MemorySettings = { maximum: number, }; + +export type ModLoader = "vanilla" | "forge" | "fabric" | "quilt" | "neoforge"; + +export type ProcessMetadata = { uuid: string, profile_path: string, start_time: string, }; + +export type Profile = { path: string, install_stage: ProfileInstallStage, name: string, icon_path: string | null, game_version: string, loader: ModLoader, loader_version: string | null, groups: Array, linked_data: LinkedData | null, created: string, modified: string, last_played: string | null, submitted_time_played: bigint, recent_time_played: bigint, java_path: string | null, extra_launch_args: Array | null, custom_env_vars: Array<[string, string]> | null, memory: MemorySettings | null, force_fullscreen: boolean | null, game_resolution: WindowSize | null, hooks: Hooks, }; + +export type ProfileFile = { hash: string, file_name: string, size: bigint, metadata: FileMetadata | null, update_version_id: string | null, project_type: ProjectType, }; + +export type ProfileInstallStage = "installed" | "minecraft_installing" | "pack_installed" | "pack_installing" | "not_installed"; + +export type ProjectType = "mod" | "datapack" | "resourcepack" | "shaderpack"; + +/** + * Game window size + */ +export type WindowSize = [number, number]; diff --git a/apps/app-frontend/src/helpers/profile.js b/apps/app-frontend/src/helpers/profile.js deleted file mode 100644 index ed9741bdbc..0000000000 --- a/apps/app-frontend/src/helpers/profile.js +++ /dev/null @@ -1,204 +0,0 @@ -/** - * All theseus API calls return serialized values (both return values and errors); - * So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized, - * and deserialized into a usable JS object. - */ -import { invoke } from '@tauri-apps/api/core' -import { install_to_existing_profile } from '@/helpers/pack.js' -import { handleError } from '@/store/notifications.js' - -/// Add instance -/* - name: String, // the name of the profile, and relative path to create - game_version: String, // the game version of the profile - modloader: ModLoader, // the modloader to use - - ModLoader is an enum, with the following variants: Vanilla, Forge, Fabric, Quilt - loader_version: String, // the modloader version to use, set to "latest", "stable", or the ID of your chosen loader - icon: Path, // the icon for the profile - - icon is a path to an image file, which will be copied into the profile directory -*/ - -export async function create(name, gameVersion, modloader, loaderVersion, iconPath, skipInstall) { - //Trim string name to avoid "Unable to find directory" - name = name.trim() - return await invoke('plugin:profile-create|profile_create', { - name, - gameVersion, - modloader, - loaderVersion, - iconPath, - skipInstall, - }) -} - -// duplicate a profile -export async function duplicate(path) { - return await invoke('plugin:profile-create|profile_duplicate', { path }) -} - -// Remove a profile -export async function remove(path) { - return await invoke('plugin:profile|profile_remove', { path }) -} - -// Get a profile by path -// Returns a Profile -export async function get(path) { - return await invoke('plugin:profile|profile_get', { path }) -} - -export async function get_many(paths) { - return await invoke('plugin:profile|profile_get_many', { paths }) -} - -// Get a profile's projects -// Returns a map of a path to profile file -export async function get_projects(path, cacheBehaviour) { - return await invoke('plugin:profile|profile_get_projects', { path, cacheBehaviour }) -} - -// Get a profile's full fs path -// Returns a path -export async function get_full_path(path) { - return await invoke('plugin:profile|profile_get_full_path', { path }) -} - -// Get's a mod's full fs path -// Returns a path -export async function get_mod_full_path(path, projectPath) { - return await invoke('plugin:profile|profile_get_mod_full_path', { path, projectPath }) -} - -// Get optimal java version from profile -// Returns a java version -export async function get_optimal_jre_key(path) { - return await invoke('plugin:profile|profile_get_optimal_jre_key', { path }) -} - -// Get a copy of the profile set -// Returns hashmap of path -> Profile -export async function list() { - return await invoke('plugin:profile|profile_list') -} - -export async function check_installed(path, projectId) { - return await invoke('plugin:profile|profile_check_installed', { path, projectId }) -} - -// Installs/Repairs a profile -export async function install(path, force) { - return await invoke('plugin:profile|profile_install', { path, force }) -} - -// Updates all of a profile's projects -export async function update_all(path) { - return await invoke('plugin:profile|profile_update_all', { path }) -} - -// Updates a specified project -export async function update_project(path, projectPath) { - return await invoke('plugin:profile|profile_update_project', { path, projectPath }) -} - -// Add a project to a profile from a version -// Returns a path to the new project file -export async function add_project_from_version(path, versionId) { - return await invoke('plugin:profile|profile_add_project_from_version', { path, versionId }) -} - -// Add a project to a profile from a path + project_type -// Returns a path to the new project file -export async function add_project_from_path(path, projectPath, projectType) { - return await invoke('plugin:profile|profile_add_project_from_path', { - path, - projectPath, - projectType, - }) -} - -// Toggle disabling a project -export async function toggle_disable_project(path, projectPath) { - return await invoke('plugin:profile|profile_toggle_disable_project', { path, projectPath }) -} - -// Remove a project -export async function remove_project(path, projectPath) { - return await invoke('plugin:profile|profile_remove_project', { path, projectPath }) -} - -// Update a managed Modrinth profile to a specific version -export async function update_managed_modrinth_version(path, versionId) { - return await invoke('plugin:profile|profile_update_managed_modrinth_version', { path, versionId }) -} - -// Repair a managed Modrinth profile -export async function update_repair_modrinth(path) { - return await invoke('plugin:profile|profile_repair_managed_modrinth', { path }) -} - -// Export a profile to .mrpack -/// included_overrides is an array of paths to override folders to include (ie: 'mods', 'resource_packs') -// Version id is optional (ie: 1.1.5) -export async function export_profile_mrpack( - path, - exportLocation, - includedOverrides, - versionId, - description, - name, -) { - return await invoke('plugin:profile|profile_export_mrpack', { - path, - exportLocation, - includedOverrides, - versionId, - description, - name, - }) -} - -// Given a folder path, populate an array of all the subfolders -// Intended to be used for finding potential override folders -// profile -// -- mods -// -- resourcepacks -// -- file1 -// => [mods, resourcepacks] -// allows selection for 'included_overrides' in export_profile_mrpack -export async function get_pack_export_candidates(profilePath) { - return await invoke('plugin:profile|profile_get_pack_export_candidates', { profilePath }) -} - -// Run Minecraft using a pathed profile -// Returns PID of child -export async function run(path) { - return await invoke('plugin:profile|profile_run', { path }) -} - -export async function kill(path) { - return await invoke('plugin:profile|profile_kill', { path }) -} - -// Edits a profile -export async function edit(path, editProfile) { - return await invoke('plugin:profile|profile_edit', { path, editProfile }) -} - -// Edits a profile's icon -export async function edit_icon(path, iconPath) { - return await invoke('plugin:profile|profile_edit_icon', { path, iconPath }) -} - -export async function finish_install(instance) { - if (instance.install_stage !== 'pack_installed') { - let linkedData = instance.linked_data - await install_to_existing_profile( - linkedData.project_id, - linkedData.version_id, - instance.name, - instance.path, - ).catch(handleError) - } else { - await install(instance.path, false).catch(handleError) - } -} diff --git a/apps/app-frontend/src/helpers/profile.ts b/apps/app-frontend/src/helpers/profile.ts new file mode 100644 index 0000000000..c855970c8d --- /dev/null +++ b/apps/app-frontend/src/helpers/profile.ts @@ -0,0 +1,250 @@ +/** + * All theseus API calls return serialized values (both return values and errors); + * So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized, + * and deserialized into a usable JS object. + */ +import { invoke } from '@tauri-apps/api/core' +import { install_to_existing_profile } from '@/helpers/pack.js' +import { handleError } from '@/store/notifications.js' +import type { CacheBehaviour, JavaVersion, ModLoader, ProcessMetadata, Profile } from './lib-types' +import type { EditProfile } from './api-types' + +/// Add instance +/* + name: String, // the name of the profile, and relative path to create + game_version: String, // the game version of the profile + modloader: ModLoader, // the modloader to use + - ModLoader is an enum, with the following variants: Vanilla, Forge, Fabric, Quilt + loader_version: String, // the modloader version to use, set to "latest", "stable", or the ID of your chosen loader + icon: Path, // the icon for the profile + - icon is a path to an image file, which will be copied into the profile directory +*/ + +/** + * + * @param name the name of the profile, and relative path to create + * @param gameVersion the game version of the profile + * @param modLoader the modloader to use + * @param loaderVersion the modlader version to use, set to "latest", "stable", or the ID of your chosen loader + * @param iconPath the icon for the profile + * @param skipInstall + * @returns + */ + +export async function create( + name: string, + gameVersion: string, + modLoader: ModLoader, + loaderVersion?: string, + iconPath?: string, + skipInstall?: boolean, +) { + //Trim string name to avoid "Unable to find directory" + name = name.trim() + return await invoke('plugin:profile-create|profile_create', { + name, + gameVersion, + modLoader, + loaderVersion, + iconPath, + skipInstall, + }) +} + +// duplicate a profile +export async function duplicate(path: string) { + return await invoke('plugin:profile-create|profile_duplicate', { path }) +} + +// Remove a profile +export async function remove(path: string): Promise { + await invoke('plugin:profile|profile_remove', { path }) +} + +// Get a profile by path +// Returns a Profile +export async function get(path: string) { + const res = await invoke('plugin:profile|profile_get', { path }) + console.log('PROFILE GET', res) + return res +} + +export async function get_many(paths: string[]) { + return await invoke('plugin:profile|profile_get_many', { paths }) +} + +// Get a profile's projects +// Returns a map of a path to profile file +export async function get_projects(path: string, cacheBehaviour?: CacheBehaviour) { + return await invoke('plugin:profile|profile_get_projects', { path, cacheBehaviour }) +} + +// Get a profile's full fs path +// Returns a path +export async function get_full_path(path: string) { + return await invoke('plugin:profile|profile_get_full_path', { path }) +} + +// Get's a mod's full fs path +// Returns a path +export async function get_mod_full_path(path: string, projectPath: string) { + return await invoke('plugin:profile|profile_get_mod_full_path', { path, projectPath }) +} + +// Get optimal java version from profile +// Returns a java version +export async function get_optimal_jre_key(path: string) { + return await invoke('plugin:profile|profile_get_optimal_jre_key', { + path, + }) +} + +// Get a copy of the profile set +// Returns hashmap of path -> Profile +export async function list(): Promise { + return await invoke('plugin:profile|profile_list') +} + +export async function check_installed(path: string, projectId: string) { + return await invoke('plugin:profile|profile_check_installed', { path, projectId }) +} + +// Installs/Repairs a profile +export async function install(path: string, force: boolean): Promise { + await invoke('plugin:profile|profile_install', { path, force }) +} + +// Updates all of a profile's projects +export async function update_all(path: string) { + return await invoke>('plugin:profile|profile_update_all', { path }) +} + +// Updates a specified project +export async function update_project(path: string, projectPath: string) { + return await invoke('plugin:profile|profile_update_project', { path, projectPath }) +} + +// Add a project to a profile from a version +// Returns a path to the new project file +export async function add_project_from_version(path: string, versionId: string) { + return await invoke('plugin:profile|profile_add_project_from_version', { + path, + versionId, + }) +} + +type ProjectType = 'mod' | 'datapack' | 'resourcepack' | 'shaderpack' + +// Add a project to a profile from a path + project_type +// Returns a path to the new project file +export async function add_project_from_path( + path: string, + projectPath: string, + projectType?: ProjectType, +) { + return await invoke('plugin:profile|profile_add_project_from_path', { + path, + projectPath, + projectType, + }) +} + +// Toggle disabling a project +export async function toggle_disable_project(path: string, projectPath: string) { + return await invoke('plugin:profile|profile_toggle_disable_project', { + path, + projectPath, + }) +} + +// Remove a project +export async function remove_project(path: string, projectPath: string): Promise { + return await invoke('plugin:profile|profile_remove_project', { path, projectPath }) +} + +// Update a managed Modrinth profile to a specific version +export async function update_managed_modrinth_version( + path: string, + versionId: string, +): Promise { + return await invoke('plugin:profile|profile_update_managed_modrinth_version', { path, versionId }) +} + +// Repair a managed Modrinth profile +export async function update_repair_modrinth(path: string): Promise { + return await invoke('plugin:profile|profile_repair_managed_modrinth', { path }) +} + +// Export a profile to .mrpack +/// included_overrides is an array of paths to override folders to include (ie: 'mods', 'resource_packs') +// Version id is optional (ie: 1.1.5) +export async function export_profile_mrpack( + path: string, + exportLocation: string, + includedOverrides: string[], + versionId?: string, + description?: string, + name?: string, +): Promise { + return await invoke('plugin:profile|profile_export_mrpack', { + path, + exportLocation, + includedOverrides, + versionId, + description, + name, + }) +} + +// Given a folder path, populate an array of all the subfolders +// Intended to be used for finding potential override folders +// profile +// -- mods +// -- resourcepacks +// -- file1 +// => [mods, resourcepacks] +// allows selection for 'included_overrides' in export_profile_mrpack +export async function get_pack_export_candidates(profilePath: string) { + return await invoke('plugin:profile|profile_get_pack_export_candidates', { + profilePath, + }) +} + +// Run Minecraft using a pathed profile +// Returns PID of child +export async function run(path: string) { + return await invoke('plugin:profile|profile_run', { path }) +} + +export async function kill(path: string): Promise { + return await invoke('plugin:profile|profile_kill', { path }) +} + +// Edits a profile +export async function edit(path: string, editProfile: Partial): Promise { + return await invoke('plugin:profile|profile_edit', { path, editProfile }) +} + +// Edits a profile's icon +export async function edit_icon(path: string, iconPath?: string): Promise { + return await invoke('plugin:profile|profile_edit_icon', { path, iconPath }) +} + +export async function finish_install(instance: Profile): Promise { + if (instance.install_stage !== 'pack_installed') { + const linkedData = instance.linked_data + if (!linkedData) { + handleError('No linked data found for instance') + return + } + + await install_to_existing_profile( + linkedData.project_id, + linkedData.version_id, + instance.name, + instance.path, + ).catch(handleError) + } else { + await install(instance.path, false).catch(handleError) + } +} diff --git a/apps/app-frontend/src/helpers/types.d.ts b/apps/app-frontend/src/helpers/types.d.ts index 1007744d0f..ad65ebc1bf 100644 --- a/apps/app-frontend/src/helpers/types.d.ts +++ b/apps/app-frontend/src/helpers/types.d.ts @@ -1,36 +1,5 @@ import type { ModrinthId } from '@modrinth/utils' - -type GameInstance = { - path: string - install_stage: InstallStage - - name: string - icon_path?: string - - game_version: string - loader: InstanceLoader - loader_version?: string - - groups: string[] - - linked_data?: LinkedData - - created: Date - modified: Date - last_played?: Date - - submitted_time_played: number - recent_time_played: number - - java_path?: string - extra_launch_args?: string[] - custom_env_vars?: [string, string][] - - memory?: MemorySettings - force_fullscreen?: boolean - game_resolution?: [number, number] - hooks: Hooks -} +import type { Profile } from './lib-types' type InstallStage = | 'installed' @@ -110,6 +79,6 @@ type AppSettings = { } export type InstanceSettingsTabProps = { - instance: GameInstance + instance: Profile offline?: boolean } diff --git a/apps/app-frontend/src/pages/Browse.vue b/apps/app-frontend/src/pages/Browse.vue index a57b2fddd2..45270a2a96 100644 --- a/apps/app-frontend/src/pages/Browse.vue +++ b/apps/app-frontend/src/pages/Browse.vue @@ -19,7 +19,7 @@ import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags' import type { LocationQuery } from 'vue-router' import { useRoute, useRouter } from 'vue-router' import SearchCard from '@/components/ui/SearchCard.vue' -import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js' +import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile' import { get_search_results } from '@/helpers/cache.js' import NavTabs from '@/components/ui/NavTabs.vue' import type Instance from '@/components/ui/Instance.vue' diff --git a/apps/app-frontend/src/pages/Index.vue b/apps/app-frontend/src/pages/Index.vue index 9d9064eb5c..91ee4edc71 100644 --- a/apps/app-frontend/src/pages/Index.vue +++ b/apps/app-frontend/src/pages/Index.vue @@ -2,7 +2,7 @@ import { ref, onUnmounted, computed } from 'vue' import { useRoute } from 'vue-router' import RowDisplay from '@/components/RowDisplay.vue' -import { list } from '@/helpers/profile.js' +import { list } from '@/helpers/profile' import { profile_listener } from '@/helpers/events' import { useBreadcrumbs } from '@/store/breadcrumbs' import { handleError } from '@/store/notifications.js' diff --git a/apps/app-frontend/src/pages/instance/Mods.vue b/apps/app-frontend/src/pages/instance/Mods.vue index e49f2d9c7c..a2a644a568 100644 --- a/apps/app-frontend/src/pages/instance/Mods.vue +++ b/apps/app-frontend/src/pages/instance/Mods.vue @@ -285,7 +285,7 @@ import { toggle_disable_project, update_all, update_project, -} from '@/helpers/profile.js' +} from '@/helpers/profile' import { handleError } from '@/store/notifications.js' import { trackEvent } from '@/helpers/analytics' import { highlightModInProfile } from '@/helpers/utils.js' diff --git a/apps/app-frontend/src/pages/library/Custom.vue b/apps/app-frontend/src/pages/library/Custom.vue index 619b411130..2c2f93d4a8 100644 --- a/apps/app-frontend/src/pages/library/Custom.vue +++ b/apps/app-frontend/src/pages/library/Custom.vue @@ -1,12 +1,10 @@ -