feat: instances v2 (#6431)

* feat: base of instances v2

* feat: use old profiles with compat layer

* prototype: instances v2

* fix: install_from using profile

* fix: skins migration fix

* fix: frontend still using profile path

* fix: add update proj multiselect guard

* fix: cargo fmt

* fix: content missing fields

* feat: break up app-lib/api/instance.rs

* fix: check_content_updates mismatch

* fix: updater modal cleanup w/new structure

* feat: better update all handling

* fix: remove preview_update_all

* fix: feedback on bulk update + lint

* fix: rem transitions

* fix: change to jsonb

* feat: app db backup after update

* fix: lint

* fix: sqlx prepare + use sqlx macros

* fix: lint

* fix: bugs

* feat: defuck the installing process up

* fix: bug of hell

* fix: shear

* fix: fmt

* fix: install progress spacing + change mc/content/overrides to bytes

* fix: lint

* fix: prepr

* fix: navtabs anim not working in app

* fix: worlds.vue improvements + browse page fixes

* feat: optimise queries + adapter fns

* fix: lint

* fix: lint

* feat: shared modrinth-content-management crate (#6469)

* feat: disable warnings setting

* feat: add instances shortcuts (#6329)

* Add modrinth://launch deep link to start a profile

Support external profile launching via modrinth://launch/{profile_path} for integrations such as Stream Deck.

* Change route to /launch/profile/{id} for future extensibility

* fix: ensure profile path is url decoded

* fix: URL-decode profile path from deep link

* fix: use urlencoding crate for URL decoding

* feat: implement app instance shortcuts

* feat: change windows shortcut creation to use windows api instead

* feat: implement creating a shortcut launching world/server

* format

* fmt

* fix multiline inline tables

* pnpm prepr

* feat: move create shortcut to last item

* refactor: split up shortcuts.rs for individual platforms

* refactor: turn profile launch url into url type

* use string literal and add safety comment

* pt2

* refactor: rename anything that's profile into instance

* update mac shortcut

---------

Co-authored-by: DJCheesusReal <134006619+DJCheesusReal@users.noreply.github.com>

---------

Co-authored-by: Truman Gao <106889354+tdgao@users.noreply.github.com>
Co-authored-by: DJCheesusReal <134006619+DJCheesusReal@users.noreply.github.com>
This commit is contained in:
Calum H.
2026-06-25 21:19:29 +00:00
committed by GitHub
co-authored by DJCheesusReal Truman Gao
parent ef4044534f
commit 734720e11e
353 changed files with 24745 additions and 9771 deletions
@@ -45,7 +45,7 @@ import {
useContentSelection,
} from './composables'
import { injectContentManager } from './providers/content-manager'
import type { ContentCardTableItem, ContentItem } from './types'
import type { BulkOperationStatus, ContentCardTableItem, ContentItem } from './types'
const { formatMessage } = useVIntl()
const debug = useDebugLogger('ContentPageLayout')
@@ -151,6 +151,7 @@ const messages = defineMessages({
})
const ctx = injectContentManager()
const skipNonEssentialWarnings = computed(() => ctx.skipNonEssentialWarnings?.value ?? false)
function getItemId(item: ContentItem) {
return ctx.getItemId?.(item) ?? item.file_path ?? item.file_name ?? item.id
@@ -247,6 +248,8 @@ if (ctx.isBulkOperating) {
const { isChanging, markChanging, unmarkChanging } = useChangingItems()
const bulkWaiting = ref(false)
const bulkStatusMessage = ref<string | null>(null)
const bulkItemCount = ref(0)
const refreshing = ref(false)
async function handleRefresh() {
@@ -387,7 +390,7 @@ async function promptDeleteItems(items: ContentItem[], event?: MouseEvent) {
}
function showDeletionConfirmation(event?: MouseEvent) {
if (event?.shiftKey && !ctx.isBusy.value) {
if ((event?.shiftKey || skipNonEssentialWarnings.value) && !ctx.isBusy.value) {
confirmDelete()
} else {
confirmDeletionModal.value?.show()
@@ -448,6 +451,8 @@ async function confirmDelete() {
if (ctx.bulkDeleteItems && itemsToDelete.length > 1) {
isBulkOperating.value = true
bulkOperation.value = 'delete'
bulkProgress.value = 0
bulkTotal.value = itemsToDelete.length
bulkWaiting.value = true
try {
await ctx.bulkDeleteItems(itemsToDelete)
@@ -456,6 +461,8 @@ async function confirmDelete() {
clearSelection()
isBulkOperating.value = false
bulkOperation.value = null
bulkProgress.value = 0
bulkTotal.value = 0
bulkWaiting.value = false
}
return
@@ -506,6 +513,8 @@ async function bulkEnable() {
if (ctx.bulkEnableItems) {
isBulkOperating.value = true
bulkOperation.value = 'enable'
bulkProgress.value = 0
bulkTotal.value = items.length
bulkWaiting.value = true
try {
await ctx.bulkEnableItems(items)
@@ -513,6 +522,8 @@ async function bulkEnable() {
clearSelection()
isBulkOperating.value = false
bulkOperation.value = null
bulkProgress.value = 0
bulkTotal.value = 0
bulkWaiting.value = false
}
return
@@ -527,6 +538,8 @@ async function bulkDisable() {
if (ctx.bulkDisableItems) {
isBulkOperating.value = true
bulkOperation.value = 'disable'
bulkProgress.value = 0
bulkTotal.value = items.length
bulkWaiting.value = true
try {
await ctx.bulkDisableItems(items)
@@ -534,6 +547,8 @@ async function bulkDisable() {
clearSelection()
isBulkOperating.value = false
bulkOperation.value = null
bulkProgress.value = 0
bulkTotal.value = 0
bulkWaiting.value = false
}
return
@@ -555,15 +570,19 @@ function handleSwitchVersionById(id: string) {
// Bulk updating
const confirmBulkUpdateModal = ref<InstanceType<typeof ConfirmBulkUpdateModal>>()
const pendingBulkUpdateItems = ref<ContentItem[]>([])
const pendingBulkUpdateAll = ref(false)
const hasBulkUpdateSupport = computed(() => !!(ctx.bulkUpdateItem || ctx.bulkUpdateItems))
const hasBulkUpdateSupport = computed(
() => !!(ctx.bulkUpdateAll || ctx.bulkUpdateItem || ctx.bulkUpdateItems),
)
function promptUpdateAll(event?: MouseEvent) {
if (!hasBulkUpdateSupport.value) return
const items = ctx.items.value.filter((item) => item.has_update)
if (items.length === 0) return
pendingBulkUpdateItems.value = items
if (event?.shiftKey && !ctx.isBusy.value) {
pendingBulkUpdateAll.value = true
if ((event?.shiftKey || skipNonEssentialWarnings.value) && !ctx.isBusy.value) {
confirmBulkUpdate()
} else {
confirmBulkUpdateModal.value?.show()
@@ -575,7 +594,8 @@ function promptUpdateSelected(event?: MouseEvent) {
const items = selectedItems.value.filter((item) => item.has_update)
if (items.length === 0) return
pendingBulkUpdateItems.value = items
if (event?.shiftKey && !ctx.isBusy.value) {
pendingBulkUpdateAll.value = false
if ((event?.shiftKey || skipNonEssentialWarnings.value) && !ctx.isBusy.value) {
confirmBulkUpdate()
} else {
confirmBulkUpdateModal.value?.show()
@@ -587,22 +607,61 @@ async function confirmBulkUpdate() {
const items = pendingBulkUpdateItems.value
if (items.length === 0 || !hasBulkUpdateSupport.value) return
if (ctx.bulkUpdateItems) {
isBulkOperating.value = true
bulkOperation.value = 'update'
bulkWaiting.value = true
try {
await ctx.bulkUpdateItems(items)
} finally {
clearSelection()
isBulkOperating.value = false
bulkOperation.value = null
bulkWaiting.value = false
const setBulkStatus = (status: BulkOperationStatus) => {
bulkStatusMessage.value = status.message ?? null
bulkProgress.value = status.progress ?? bulkProgress.value
bulkTotal.value = status.total ?? bulkTotal.value
bulkWaiting.value = status.waiting ?? false
}
try {
if (pendingBulkUpdateAll.value && ctx.bulkUpdateAll) {
isBulkOperating.value = true
bulkOperation.value = 'update'
bulkProgress.value = 0
bulkTotal.value = items.length
bulkItemCount.value = items.length
bulkStatusMessage.value = null
bulkWaiting.value = true
try {
await ctx.bulkUpdateAll(setBulkStatus)
} finally {
clearSelection()
isBulkOperating.value = false
bulkOperation.value = null
bulkProgress.value = 0
bulkTotal.value = 0
bulkItemCount.value = 0
bulkStatusMessage.value = null
bulkWaiting.value = false
}
} else if (ctx.bulkUpdateItems) {
isBulkOperating.value = true
bulkOperation.value = 'update'
bulkProgress.value = 0
bulkTotal.value = items.length
bulkItemCount.value = items.length
bulkStatusMessage.value = null
bulkWaiting.value = true
try {
await ctx.bulkUpdateItems(items)
} finally {
clearSelection()
isBulkOperating.value = false
bulkOperation.value = null
bulkProgress.value = 0
bulkTotal.value = 0
bulkItemCount.value = 0
bulkStatusMessage.value = null
bulkWaiting.value = false
}
} else if (ctx.bulkUpdateItem) {
await runBulk('update', items, ctx.bulkUpdateItem, { onComplete: clearSelection })
}
} else if (ctx.bulkUpdateItem) {
await runBulk('update', items, ctx.bulkUpdateItem, { onComplete: clearSelection })
} finally {
pendingBulkUpdateItems.value = []
pendingBulkUpdateAll.value = false
}
pendingBulkUpdateItems.value = []
}
const confirmUnlinkModal = ref<InstanceType<typeof ConfirmUnlinkModal>>()
@@ -883,6 +942,8 @@ const confirmUnlinkModal = ref<InstanceType<typeof ConfirmUnlinkModal>>()
:bulk-progress="bulkProgress"
:bulk-total="bulkTotal"
:bulk-waiting="bulkWaiting"
:bulk-status-message="bulkStatusMessage"
:bulk-item-count="bulkItemCount"
:aria-label="formatMessage(commonMessages.selectionActionsLabel)"
:get-item-id="getItemId"
@clear="clearSelection"