Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
40e3607
update add files copy and go to next step on just one file
tdgao Dec 31, 2025
312490b
rename and reorder stages
tdgao Dec 31, 2025
c4c2782
add metadata stage and update details stage
tdgao Dec 31, 2025
36a0e88
implement files inside metadata stage
tdgao Jan 1, 2026
59ab802
use regular prettier instead of prettier eslint
tdgao Jan 4, 2026
29bb86c
remove changelog stage config
tdgao Jan 4, 2026
35302a2
save button on details stage
tdgao Jan 5, 2026
4792e04
update edit buttons in versions table
tdgao Jan 5, 2026
f90fabc
add collapse environment selector
tdgao Jan 5, 2026
ff29b59
implement dependencies list in metadata step
tdgao Jan 5, 2026
4916005
move dependencies into provider
tdgao Jan 5, 2026
d35f490
add suggested dependencies to metadata stage
tdgao Jan 5, 2026
66d5ed1
pnpm prepr
tdgao Jan 5, 2026
7087514
fix unused var
tdgao Jan 5, 2026
88ef419
Revert "add collapse environment selector"
tdgao Jan 5, 2026
7f6b854
hide resource pack loader only when its the only loader
tdgao Jan 5, 2026
b84f032
fix no dependencies for modpack
tdgao Jan 5, 2026
19a8b20
add breadcrumbs with hide breadcrumb option
tdgao Jan 6, 2026
3ec4859
wider stages
tdgao Jan 6, 2026
7610956
add proper horizonal scroll breadcrumbs
tdgao Jan 6, 2026
316a100
fix titles
tdgao Jan 6, 2026
81dd02d
handle save version in version page
tdgao Jan 6, 2026
47cc131
Merge branch 'main' into truman/project-versions-v2
tdgao Jan 6, 2026
6514d0c
remove box shadow
tdgao Jan 6, 2026
4f4c1c6
add notification provider to storybook
tdgao Jan 6, 2026
a9ed630
add drop area for versions to drop file right into page
tdgao Jan 6, 2026
a3b062e
fix mobile versions table buttons overflowing
tdgao Jan 6, 2026
2336353
pnpm prepr
tdgao Jan 6, 2026
4db5a57
fix drop file opening modal in wrong stage
tdgao Jan 6, 2026
976b8c6
implement invalid file for dropping files
tdgao Jan 6, 2026
72e41bd
allow horizontal scroll on breadcrumbs
tdgao Jan 6, 2026
8a53fa0
update infer.js as best as possible
tdgao Jan 6, 2026
9bfce8b
add create version button uploading version state
tdgao Jan 6, 2026
aab15c0
add extractVersionFromFilename for resource pack and datapack
tdgao Jan 6, 2026
f3a8119
allow jars for datapack project
tdgao Jan 6, 2026
f500bb9
detect multiple loaders when possible
tdgao Jan 6, 2026
add15a2
iris means compatible with optifine too
tdgao Jan 6, 2026
dc31cc4
infer environment on loader change as well
tdgao Jan 6, 2026
92a0f40
add tooltip
tdgao Jan 6, 2026
d76fbe1
prevent navigate forward when cannot go to next step
tdgao Jan 7, 2026
63f9e3b
larger breadcrumb click targets
tdgao Jan 7, 2026
cfb6cba
hide loaders and mc versions stage until files added
tdgao Jan 7, 2026
8ea2aa4
fix max width in header
tdgao Jan 7, 2026
3ba9135
fix add files from metadata step jumping steps
tdgao Jan 7, 2026
63e689c
define width in NewModal instead
tdgao Jan 7, 2026
4a01b56
disable remove dependency in metadata stage
tdgao Jan 7, 2026
797e17a
switch metadata and details buttons positions
tdgao Jan 7, 2026
02916cf
fix remove button spacing
tdgao Jan 7, 2026
eb7c0a0
do not allow duplicate suggested dependencies
tdgao Jan 7, 2026
4771252
fix version detection for fabric minecraft version semvar
tdgao Jan 7, 2026
7f8405b
better verion number detection based on filename
tdgao Jan 7, 2026
5d0499f
show resource pack loader but uneditable
tdgao Jan 7, 2026
9f5761c
remove vanilla shader detection
tdgao Jan 7, 2026
50b9e3f
refactor: break up large infer.js into ts and modules
tdgao Jan 7, 2026
115ee58
remove duplicated types
tdgao Jan 7, 2026
8264ad2
add fill missing from file name step
tdgao Jan 7, 2026
b77223c
pnpm prepr
tdgao Jan 7, 2026
02f981c
fix neoforge loader parse failing and not adding neoforge loader
tdgao Jan 7, 2026
8ee09a9
add missing pack formats
tdgao Jan 7, 2026
91b8d57
handle new pack format
tdgao Jan 7, 2026
3a426cd
pnpm prepr
tdgao Jan 7, 2026
ae8f5ba
add another regex where it is version in anywhere in filename
tdgao Jan 7, 2026
972d77e
only show resource pack or data pack options for filetype on datapack…
tdgao Jan 7, 2026
8bc9f53
add redundant zip folder check
tdgao Jan 8, 2026
e1d6b6e
reject RP and DP if has redundant folder
tdgao Jan 8, 2026
1fb4bf6
fix hide stage in breadcrumb
tdgao Jan 8, 2026
be119c6
add snapshot group key in case no release version. brings out 26.1 sn…
tdgao Jan 8, 2026
b3331f3
pnpm prepr
tdgao Jan 8, 2026
4c65973
open in group if has something selected
tdgao Jan 8, 2026
2728d89
fix resource pack loader uneditable if accidentally selected on diffe…
tdgao Jan 8, 2026
64fcfe6
add new environment tags
tdgao Jan 9, 2026
02df537
add unknown and not applicable environment tags
tdgao Jan 9, 2026
62780d9
pnpm prepr
tdgao Jan 9, 2026
42a524f
Merge branch 'main' into truman/project-versions-v2
tdgao Jan 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@
"source.fixAll.eslint": "explicit",
"source.organizeImports": "always"
},
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[vue]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer"
Expand Down
2 changes: 2 additions & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"@nuxtjs/i18n": "^9.0.0",
"@types/dompurify": "^3.0.5",
"@types/iso-3166-2": "^1.0.4",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.1.0",
"@types/semver": "^7.7.1",
"autoprefixer": "^10.4.19",
"glob": "^10.2.7",
"nuxt": "^3.20.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
<template>
<MultiStageModal ref="modal" :stages="ctx.stageConfigs" :context="ctx" />
<MultiStageModal
ref="modal"
:stages="ctx.stageConfigs"
:context="ctx"
:breadcrumbs="!editingVersion"
@hide="() => (modalOpen = false)"
/>
<DropArea
v-if="!modalOpen"
:accept="acceptFileFromProjectType(projectV2.project_type)"
@change="handleDropArea"
/>
</template>

<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import {
DropArea,
injectModrinthClient,
injectNotificationManager,
injectProjectPageContext,
MultiStageModal,
} from '@modrinth/ui'
import { acceptFileFromProjectType } from '@modrinth/utils'
import type { ComponentExposed } from 'vue-component-type-helpers'

import {
createManageVersionContext,
provideManageVersionContext,
} from '~/providers/version/manage-version-modal'

const emit = defineEmits<{
(e: 'save'): void
}>()

const modal = useTemplateRef<ComponentExposed<typeof MultiStageModal>>('modal')
const modalOpen = ref(false)

const ctx = createManageVersionContext(modal)
const ctx = createManageVersionContext(modal, () => emit('save'))
provideManageVersionContext(ctx)

const { newDraftVersion } = ctx
const { newDraftVersion, editingVersion, handleNewFiles } = ctx

const { projectV2 } = injectProjectPageContext()
const { addNotification } = injectNotificationManager()
Expand Down Expand Up @@ -64,6 +82,15 @@ function openCreateVersionModal(
newDraftVersion(projectV2.value.id, version)
modal.value?.setStage(stageId ?? 0)
modal.value?.show()
modalOpen.value = true
}

async function handleDropArea(files: FileList) {
newDraftVersion(projectV2.value.id, null)
modal.value?.setStage(0)
await handleNewFiles(Array.from(files))
modal.value?.show()
modalOpen.value = true
}

defineExpose({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
class="flex items-center justify-between gap-2 rounded-xl bg-button-bg px-4 py-1 text-button-text"
class="flex h-11 items-center justify-between gap-2 rounded-xl bg-button-bg px-4 py-1 text-button-text"
>
<div class="grid max-w-[75%] grid-cols-[auto_1fr_auto] items-center gap-2">
<Avatar v-if="icon" :src="icon" alt="dependency-icon" size="20px" :no-shadow="true" />
Expand All @@ -9,7 +9,7 @@
{{ name || 'Unknown Project' }}
</span>

<TagItem class="shrink-0 border !border-solid border-surface-5">
<TagItem class="shrink-0 border !border-solid border-surface-5 capitalize">
{{ dependencyType }}
</TagItem>
</div>
Expand All @@ -22,9 +22,9 @@
{{ versionName }}
</span>

<div class="flex items-center justify-end gap-1">
<div v-if="!hideRemove" class="flex items-center justify-end gap-1">
<ButtonStyled size="standard" :circular="true">
<button aria-label="Remove file" class="!shadow-none" @click="emitRemove">
<button aria-label="Remove file" class="-mr-2 !shadow-none" @click="emitRemove">
<XIcon aria-hidden="true" />
</button>
</ButtonStyled>
Expand All @@ -42,12 +42,13 @@ const emit = defineEmits<{
(e: 'remove'): void
}>()

const { projectId, name, icon, dependencyType, versionName } = defineProps<{
const { projectId, name, icon, dependencyType, versionName, hideRemove } = defineProps<{
projectId: string
name?: string
icon?: string
dependencyType: Labrinth.Versions.v2.DependencyType
versionName?: string
hideRemove?: boolean
}>()

function emitRemove() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<template>
<div v-if="addedDependencies.length" class="5 flex flex-col gap-2">
<template v-for="(dependency, index) in addedDependencies">
<AddedDependencyRow
v-if="dependency"
:key="index"
:project-id="dependency.projectId"
:name="dependency.name"
:icon="dependency.icon"
:dependency-type="dependency.dependencyType"
:version-name="dependency.versionName"
:hide-remove="disableRemove"
@remove="() => removeDependency(index)"
/>
</template>
<span v-if="!addedDependencies.length"> No dependencies added. </span>
</div>
</template>

<script setup lang="ts">
import { injectManageVersionContext } from '~/providers/version/manage-version-modal'

import AddedDependencyRow from './AddedDependencyRow.vue'

const { disableRemove } = defineProps<{
disableRemove?: boolean
}>()

const { draftVersion, dependencyProjects, dependencyVersions, projectsFetchLoading } =
injectManageVersionContext()

const addedDependencies = computed(() =>
(draftVersion.value.dependencies || [])
.map((dep) => {
if (!dep.project_id) return null

const dependencyProject = dependencyProjects.value[dep.project_id]
const versionName = dependencyVersions.value[dep.version_id || '']?.name ?? ''

if (!dependencyProject && projectsFetchLoading.value) return null

return {
projectId: dep.project_id,
name: dependencyProject?.name,
icon: dependencyProject?.icon_url,
dependencyType: dep.dependency_type,
versionName,
}
})
.filter(Boolean),
)

const removeDependency = (index: number) => {
if (!draftVersion.value.dependencies) return
draftVersion.value.dependencies.splice(index, 1)
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,17 @@ function groupLoaders(loaders: Labrinth.Tags.v2.Loader[]) {
}

const groupedLoaders = computed(() => groupLoaders(loaders))

onMounted(() => {
if (selectedLoaders.value.length === 0) return

// Find the first group that contains any of the selected loaders
const groups = groupedLoaders.value
for (const [groupName, loadersInGroup] of Object.entries(groups)) {
if (loadersInGroup.some((loader) => selectedLoaders.value.includes(loader.name))) {
loaderGroup.value = groupName as GroupLabels
break
}
}
})
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import type { Labrinth } from '@modrinth/api-client'
import { SearchIcon } from '@modrinth/assets'
import { ButtonStyled, Chips } from '@modrinth/ui'
import { useMagicKeys } from '@vueuse/core'
import { computed, ref } from 'vue'
import { computed, nextTick, onMounted, ref } from 'vue'

type GameVersion = Labrinth.Tags.v2.GameVersion

Expand Down Expand Up @@ -147,16 +147,24 @@ function groupVersions(gameVersions: GameVersion[]) {
)

const getGroupKey = (v: string) => v.split('.').slice(0, 2).join('.')

const getSnapshotGroupKey = (v: string) => {
const cleanVersion = v.split('-')[0]
return cleanVersion.split('.').slice(0, 2).join('.')
}

const groups: Record<string, string[]> = {}

let currentGroupKey = getGroupKey(gameVersions.find((v) => v.major)?.version || '')
let currentGroupKey = getSnapshotGroupKey(gameVersions.find((v) => v.major)?.version || '')

gameVersions.forEach((gameVersion) => {
if (gameVersion.version_type === 'release') {
currentGroupKey = getGroupKey(gameVersion.version)
if (!groups[currentGroupKey]) groups[currentGroupKey] = []
groups[currentGroupKey].push(gameVersion.version)
} else {
if (!currentGroupKey) currentGroupKey = getSnapshotGroupKey(gameVersion.version)

const key = `${currentGroupKey} ${DEV_RELEASE_KEY}`
if (!groups[key]) groups[key] = []
groups[key].push(gameVersion.version)
Expand Down Expand Up @@ -205,4 +213,27 @@ function compareGroupKeys(a: string, b: string) {
function searchFilter(gameVersion: Labrinth.Tags.v2.GameVersion) {
return gameVersion.version.toLowerCase().includes(searchQuery.value.toLowerCase())
}

onMounted(async () => {
if (props.modelValue.length === 0) return

// Open non-release tab if any non-release versions are selected
const hasNonReleaseVersions = props.gameVersions.some(
(v) => props.modelValue.includes(v.version) && v.version_type !== 'release',
)

if (hasNonReleaseVersions) {
versionType.value = 'all'
}

await nextTick()
const firstSelectedVersion = allVersionsFlat.value.find((v) => props.modelValue.includes(v))
if (firstSelectedVersion) {
const buttons = Array.from(document.querySelectorAll('button'))
const element = buttons.find((btn) => btn.textContent?.trim() === firstSelectedVersion)
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'center' })
}
}
})
</script>
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
<template>
<div v-if="visibleDependencies.length" class="flex flex-col gap-4">
<span class="font-semibold text-contrast">Suggested dependencies</span>
<div class="flex flex-col gap-2">
<template v-for="(dependency, index) in visibleDependencies">
<SuggestedDependency
v-if="dependency"
:key="index"
:project-id="dependency.project_id"
:name="dependency.name"
:icon="dependency.icon"
:dependency-type="dependency.dependency_type"
:version-name="dependency.versionName"
@on-add-suggestion="
() =>
handleAddSuggestion({
dependency_type: dependency.dependency_type,
project_id: dependency.project_id,
version_id: dependency.version_id,
})
"
/>
</template>
</div>
<div v-if="visibleSuggestedDependencies.length" class="flex flex-col gap-2">
<template v-for="(dependency, index) in visibleSuggestedDependencies">
<SuggestedDependency
v-if="dependency"
:key="index"
:project-id="dependency.project_id"
:name="dependency.name"
:icon="dependency.icon"
:dependency-type="dependency.dependency_type"
:version-name="dependency.versionName"
@on-add-suggestion="
() =>
handleAddSuggestion({
dependency_type: dependency.dependency_type,
project_id: dependency.project_id,
version_id: dependency.version_id,
})
"
/>
</template>
</div>
</template>

Expand All @@ -32,28 +29,7 @@ import { injectManageVersionContext } from '~/providers/version/manage-version-m

import SuggestedDependency from './SuggestedDependency.vue'

export interface SuggestedDependency extends Labrinth.Versions.v3.Dependency {
icon?: string
name?: string
versionName?: string
}

const props = defineProps<{
suggestedDependencies: SuggestedDependency[]
}>()

const { draftVersion } = injectManageVersionContext()

const visibleDependencies = computed<SuggestedDependency[]>(() =>
props.suggestedDependencies
.filter(
(dep) =>
!draftVersion.value.dependencies?.some(
(d) => d.project_id === dep.project_id && d.version_id === dep.version_id,
),
)
.sort((a, b) => (a.name || '').localeCompare(b.name || '')),
)
const { visibleSuggestedDependencies } = injectManageVersionContext()

const emit = defineEmits<{
(e: 'onAddSuggestion', dependency: Labrinth.Versions.v3.Dependency): void
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
class="flex items-center justify-between gap-2 rounded-xl bg-button-bg px-4 py-1 text-button-text"
class="flex items-center justify-between gap-2 rounded-xl border-2 border-dashed border-surface-5 px-4 py-1 text-button-text"
>
<div class="grid max-w-[75%] grid-cols-[auto_1fr_auto] items-center gap-2">
<Avatar v-if="icon" :src="icon" alt="dependency-icon" size="20px" :no-shadow="true" />
Expand All @@ -9,7 +9,7 @@
{{ name || 'Unknown Project' }}
</span>

<TagItem class="shrink-0 border !border-solid border-surface-5">
<TagItem class="shrink-0 border !border-solid border-surface-5 capitalize">
{{ dependencyType }}
</TagItem>
</div>
Expand All @@ -23,7 +23,7 @@
</span>

<div class="flex items-center justify-end gap-1">
<ButtonStyled size="standard" :circular="true">
<ButtonStyled size="standard" :circular="true" type="transparent">
<button aria-label="Add dependency" class="!shadow-none" @click="emitAddSuggestion">
<PlusIcon aria-hidden="true" />
</button>
Expand Down
Loading