Skip to content

Finish skins frontend #3657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 61 commits into
base: skins-frontend
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
7b20dbb
feat: start on SkinPreviewRenderer
IMB11 May 14, 2025
8f581bb
feat: setting for nametag
IMB11 May 14, 2025
32e3402
feat: hide nametag setting (sql)
IMB11 May 14, 2025
ad0ee34
fix: positioning of meshes
IMB11 May 14, 2025
0ec37aa
fix: lighting
IMB11 May 14, 2025
7fb8fc0
fix: allow dragging off-bounds
IMB11 May 14, 2025
c4bf874
fix: better color mapping
IMB11 May 14, 2025
b8f4152
feat: hide nametag setting (impl)
IMB11 May 14, 2025
67fda4c
feat: Start on edit modal + cape button cleanup + renderer fixes
IMB11 May 15, 2025
37c7576
feat: Finish new skin modal
IMB11 May 15, 2025
756ec40
feat: finish cape modal
IMB11 May 15, 2025
0988ca2
feat: skin rendering on load
IMB11 May 15, 2025
84f0e6c
fix: logic for Skins.vue
IMB11 May 15, 2025
f29912f
fix: types
IMB11 May 15, 2025
650def7
fix: types (for modal + renderer)
IMB11 May 15, 2025
9a625fe
feat: Editing?
IMB11 May 15, 2025
280b642
fix: renderer not updating variant
IMB11 May 15, 2025
2b9ad38
fix: mojang username not modrinth username
IMB11 May 15, 2025
9d36241
feat: batched skin rendering - remove vzge references (apart from cap…
IMB11 May 15, 2025
19062c7
feat: fix sizing on SkinButton and SkinLikeButton, also implement bus…
IMB11 May 16, 2025
973138f
feat: capes in preview renderer & baked renders
IMB11 May 16, 2025
0667c8d
fix: lint fixes
IMB11 May 16, 2025
0583d68
refactor: Start on cleanup and polish
IMB11 May 17, 2025
cb804f7
fix: hide error notification when logged out
IMB11 May 18, 2025
c9b5a24
revert: .gltf formatting
IMB11 May 18, 2025
9e51a2f
chore(app-frontend): fix typos
AlexTMjugador May 18, 2025
44bafea
fix(app-lib): delay account skin data deletion to next reboot
AlexTMjugador May 18, 2025
9295556
fix: login button & provide/inject AccountsCard
IMB11 May 21, 2025
9a4d669
polish: skin buttons
IMB11 May 21, 2025
e5ba1be
fix: imports
IMB11 May 22, 2025
8d58ec0
polish: use figma values
IMB11 May 22, 2025
6e5ad05
polish: tweak underneath shadow
IMB11 May 22, 2025
7cc120e
polish: cursor grab
IMB11 May 22, 2025
f587338
polish: remove green bg from CapeLikeTextButton when selected.
IMB11 May 22, 2025
1da6b30
polish: modal tweaks
IMB11 May 22, 2025
07200f1
polish: grid tweaks + start on upload skin modal
IMB11 May 22, 2025
30097a2
polish: drag and drop file flow
IMB11 May 22, 2025
6d90219
polish: button positioning in SkinButton
IMB11 May 22, 2025
e0063cb
fix: lint issues
IMB11 May 22, 2025
2acb58f
polish: deduplicate model+cape stuff and fix layout
IMB11 May 24, 2025
59c8101
fix: lint issues
IMB11 May 24, 2025
f9ab581
fix: camel case requirement for make-default
IMB11 May 24, 2025
627fd09
polish: use indexed db to persist skin previews
IMB11 May 24, 2025
32c935f
fix: lint issues
IMB11 May 24, 2025
e35015c
polish: add skin icon sizing
IMB11 May 24, 2025
5fb6835
polish: theme fixes
IMB11 May 24, 2025
5b50265
feat: animation system for skin preview renderer
IMB11 May 25, 2025
7ec2c9e
feat(app/minecraft_skins): save current custom external skin when equ…
AlexTMjugador May 25, 2025
c463f4b
fix: cape button & dynamic nametag sizing
IMB11 May 25, 2025
6696c5e
Merge remote-tracking branch 'fork/skins-frontend' into skins-frontend
IMB11 May 25, 2025
5468c26
feat(theseus): add `normalize_skin_texture` Tauri command
AlexTMjugador May 28, 2025
14b7499
chore: Rust build fixes
AlexTMjugador May 28, 2025
e324abf
feat: start impl of skin normalization on frontend
IMB11 May 29, 2025
31f6c80
feat(theseus): change parameter type of `normalize_skin_texture` Taur…
AlexTMjugador May 29, 2025
27e1040
fix: normalization
IMB11 May 29, 2025
37e9c9c
fix(theseus): make new `normalize_skin_texture` command usable
AlexTMjugador May 29, 2025
7375d2c
feat: finish normalization impl
IMB11 May 29, 2025
c0e85ad
fix: vueuse issue
IMB11 Jun 2, 2025
4f73479
fix: use optimistic approach when changing skins/capes.
IMB11 Jun 3, 2025
9dbd99e
fix: nametag cleanup + scroll fix
IMB11 Jun 3, 2025
a1c5eaf
fix: edit modal computedAsync not fast enough for skin preview renderer
IMB11 Jun 4, 2025
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
6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ async-walkdir = "2.1.0"
async_zip = "0.0.17"
base64 = "0.22.1"
bitflags = "2.9.0"
bytemuck = "1.23.0"
bytes = "1.10.1"
censor = "0.3.0"
chrono = "0.4.41"
Expand Down Expand Up @@ -86,6 +87,7 @@ notify = { version = "8.0.0", default-features = false }
notify-debouncer-mini = { version = "0.6.0", default-features = false }
p256 = "0.13.2"
paste = "1.0.15"
png = "0.17.16"
prometheus = "0.14.0"
quartz_nbt = "0.2.9"
quick-xml = "0.37.5"
Expand All @@ -94,6 +96,7 @@ rand_chacha = "=0.3.1" # Locked on 0.3 until we can update rand to 0.9
redis = "=0.29.5" # Locked on 0.29 until deadpool-redis updates to 0.30
regex = "1.11.1"
reqwest = { version = "0.12.15", default-features = false }
rgb = "0.8.50"
rust-s3 = { version = "0.35.1", default-features = false, features = [
"fail-on-err",
"tags",
Expand Down
1 change: 1 addition & 0 deletions apps/app-frontend/.prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
**/dist
*.gltf
11 changes: 7 additions & 4 deletions apps/app-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,26 @@
"intl:extract": "formatjs extract \"{,src/components,src/composables,src/helpers,src/pages,src/store}/**/*.{vue,ts,tsx,js,jsx,mts,cts,mjs,cjs}\" --ignore '**/*.d.ts' --ignore 'node_modules' --out-file src/locales/en-US/index.json --format crowdin --preserve-whitespace"
},
"dependencies": {
"@geometrically/minecraft-motd-parser": "^1.1.4",
"@modrinth/assets": "workspace:*",
"@modrinth/ui": "workspace:*",
"@modrinth/utils": "workspace:*",
"@sentry/vue": "^8.27.0",
"@geometrically/minecraft-motd-parser": "^1.1.4",
"@tauri-apps/api": "^2.5.0",
"@tauri-apps/plugin-dialog": "^2.2.1",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-opener": "^2.2.6",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-updater": "^2.7.1",
"@tauri-apps/plugin-window-state": "^2.2.2",
"@types/three": "^0.172.0",
"@vintl/vintl": "^4.4.1",
"@vueuse/core": "^11.1.0",
"dayjs": "^1.11.10",
"floating-vue": "^5.2.2",
"ofetch": "^1.3.4",
"pinia": "^2.1.7",
"posthog-js": "^1.158.2",
"three": "^0.172.0",
"vite-svg-loader": "^5.1.0",
"vue": "^3.5.13",
"vue-multiselect": "3.0.0",
Expand All @@ -39,6 +42,7 @@
"@eslint/compat": "^1.1.1",
"@formatjs/cli": "^6.2.12",
"@nuxt/eslint-config": "^0.5.6",
"@taijased/vue-render-tracker": "^1.0.7",
"@vitejs/plugin-vue": "^5.0.4",
"autoprefixer": "^10.4.19",
"eslint": "^9.9.1",
Expand All @@ -51,8 +55,7 @@
"tsconfig": "workspace:*",
"typescript": "^5.5.4",
"vite": "^5.4.6",
"vue-tsc": "^2.1.6",
"@taijased/vue-render-tracker": "^1.0.7"
"vue-tsc": "^2.1.6"
},
"packageManager": "pnpm@9.4.0",
"web-types": "../../web-types.json"
Expand Down
13 changes: 12 additions & 1 deletion apps/app-frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup>
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { computed, onMounted, onUnmounted, ref, watch, provide } from 'vue'
import { RouterView, useRoute, useRouter } from 'vue-router'
import {
ArrowBigUpDashIcon,
Expand Down Expand Up @@ -69,6 +69,8 @@ import { hide_ads_window, init_ads_window } from '@/helpers/ads.js'
import FriendsList from '@/components/ui/friends/FriendsList.vue'
import { openUrl } from '@tauri-apps/plugin-opener'
import QuickInstanceSwitcher from '@/components/ui/QuickInstanceSwitcher.vue'
import { get_available_capes, get_available_skins } from './helpers/skins'
import { generateSkinPreviews } from './helpers/rendering/batch-skin-renderer'

const formatRelativeTime = useRelativeTime()

Expand Down Expand Up @@ -198,6 +200,14 @@ async function setupApp() {
get_opening_command().then(handleCommand)
checkUpdates()
fetchCredentials()

try {
const skins = (await get_available_skins()) ?? []
const capes = (await get_available_capes()) ?? []
generateSkinPreviews(skins, capes)
} catch (error) {
console.warn('Failed to generate skin previews in app setup.', error)
}
}

const stateFailed = ref(false)
Expand Down Expand Up @@ -305,6 +315,7 @@ onMounted(() => {
})

const accounts = ref(null)
provide('accountsCard', accounts)

command_listener(handleCommand)
async function handleCommand(e) {
Expand Down
1 change: 1 addition & 0 deletions apps/app-frontend/src/assets/models/cape.gltf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"asset":{"version":"2.0","generator":"Blockbench 4.12.4 glTF exporter"},"scenes":[{"nodes":[1],"name":"blockbench_export"}],"scene":0,"nodes":[{"rotation":[0,0,0.19509032201612825,0.9807852804032304],"translation":[0.15625,1,0],"name":"Cape","mesh":0},{"children":[0]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963}],"buffers":[{"byteLength":840,"uri":"data:application/octet-stream;base64,AAAAPQAAAAAAAKA+AAAAPQAAAAAAAKC+AAAAPQAAgL8AAKA+AAAAPQAAgL8AAKC+AAAAvQAAAAAAAKC+AAAAvQAAAAAAAKA+AAAAvQAAgL8AAKC+AAAAvQAAgL8AAKA+AAAAvQAAAAAAAKC+AAAAPQAAAAAAAKC+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAvQAAgL8AAKA+AAAAPQAAgL8AAKA+AAAAvQAAgL8AAKC+AAAAPQAAgL8AAKC+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAvQAAgL8AAKA+AAAAPQAAgL8AAKA+AAAAPQAAAAAAAKC+AAAAvQAAAAAAAKC+AAAAPQAAgL8AAKC+AAAAvQAAgL8AAKC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPAAAgD0AADA+AACAPQAAgDwAAAg/AAAwPgAACD8AAEA+AACAPQAAsD4AAIA9AABAPgAACD8AALA+AAAIPwAAgDwAAAA9AACAPAAAgD0AADA+AAAAPQAAMD4AAIA9AAAwPgAAAD0AAKg+AAAAPQAAMD4AAAAAAACoPgAAAAAAAEA+AACAPQAAMD4AAIA9AABAPgAACD8AADA+AAAIPwAAAAAAAIA9AACAPAAAgD0AAAAAAAAIPwAAgDwAAAg/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUA"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[0.03125,0,0.3125],"min":[-0.03125,-1,-0.3125],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.34375,0.53125],"min":[0,0],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"cape.png"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png","uri":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAABCRJREFUaEPtlktoE1EUhm/sIzFpYsBgHyn2oRZt0C5ESxWFYutKNyKIWBBR6kpBd3Ur2J0FRdCiuKmIIG4qLnzgQqTUXSlVqdoXTU0lljRpYtOHI/8dznBnOm1mphMwJWeTzJ0795zznf/cex2MMXam2S/ht38siJ8V1lgd5mOBARerc3rZnmK37rwvCyk2nE6wezMRh+4EncH9B1ql+fkU64rPsaBz5bqh4T7Daxn1Kc5zVNeEePJIci0Aq71bzenY6JChwAnA0OBHQ/OtJLnWNwqAy61R1tO3k891ueRKoDKwtqbv7MGbgCnfRgHQoqG9hyX4hc/yiho+/HNqVPFtdj2jwTpQgd/RKT7/0ZUku/o4qAJw50KYXbzr4e+3BioYzc3kwGzAUCLWFw2+UBiCb3bNTDHiPQeAP3CGNmg/6VcSBpDu3hhvDQpOCwABwrQKsRIsqYAChy/EQAXwlPiZ3a3igNPkXIyTjr8o5L7PPdzGf59c+sV/faeWeIIIAHPJ8M3kc7l1K09LKghWAIgqoPZ7djPFTlxb4D6yAgBOQfntrUVWVeRk44tplXJorOVGkVIJTBCTpw9ECFYBIEkyMfmsAXh3u1qi5MlxbbGX/x1ZSCjBAAwgfPr6h49R5UVavk0FXC2wju5p07s6iqEF0PtqSlEf+bKzDRwdgaDU7AkoySJ5Oo/D6ZRq/H0yyuJ/lxkSheG/FgCNm7kL0BoEAG32sqtY1YIHd2/mGzTMVgD3y2slqjgW9xY6ma9AThAARIMiBtOpjADwTWc0bEkB+FZsSTxTW0KBsGPXx0yvrUpEeHAAQIM7wBJLcu8DwHaP/H8i6VSND6SiSjBlRW4WWUypVIBbIgzjVgB0tpdKqLReS0KVPTMTfH0ra2cEgAmAAEd+l1z52LybqwBQYAQAyZPh6gtDW4jjuC4fHx8wXSm0JDbeI95SRYHiFZlUaWVtPQiKAugl5E8ASAUYiy8vc0C474uGasPE5PHc4g0wK/f4obom6UNimol7kTZwQLANwOuqBokqDEeQf4lfvvnNxZJcBTBAGZplWQcQ3tcgwY9oWlXinRW4ugoAgNAWWe6ocn1QvgyRTUb4RZFVljlY/3hSBYCqb6cCZo8ekuATVRZPI/gQW8FWAI1VHganVgDQUajdA6y2gAgAcZFBjTBSpG0AcAqc3VVmCMDTbxGWZvIRCaMNkJ7pFMCzVQD4liCQ8kRFUlvaBkCvL+wYw2ZmNUgCgLajFhRPJlv3ADuS1VtjPQCk823S574ffN/x1dQqy8dHR5SN2Spcbaymz2mjwNYDAD4IQn3TDpVLQIAqNjwAANRrAdoI/3sAOF7Xe1khCNQGVH1bL0JGJb1R52VtD8gVYHkAuVKpbMWZV0C2yObKunkF5EqlshVnXgHZIpsr6/4DlbxcPydnT74AAAAASUVORK5CYII="}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]}]}
1 change: 1 addition & 0 deletions apps/app-frontend/src/assets/models/classic_player.gltf

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/app-frontend/src/assets/models/slim_player.gltf

Large diffs are not rendered by default.

Binary file added apps/app-frontend/src/assets/skins/herobrine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/app-frontend/src/assets/skins/steve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 20 additions & 3 deletions apps/app-frontend/src/components/ui/AccountsCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,15 @@
</div>
<div v-else class="logged-out account">
<h4>Not signed in</h4>
<Button v-tooltip="'Log in'" icon-only color="primary" @click="login()">
<LogInIcon />
<Button
v-tooltip="'Log in'"
:disabled="loginDisabled"
icon-only
color="primary"
@click="login()"
>
<LogInIcon v-if="!loginDisabled" />
<SpinnerIcon v-else class="animate-spin" />
</Button>
</div>
<div v-if="displayAccounts.length > 0" class="account-group">
Expand All @@ -68,7 +75,7 @@
</template>

<script setup>
import { DropdownIcon, PlusIcon, TrashIcon, LogInIcon } from '@modrinth/assets'
import { DropdownIcon, PlusIcon, TrashIcon, LogInIcon, SpinnerIcon } from '@modrinth/assets'
import { Avatar, Button, Card } from '@modrinth/ui'
import { ref, computed, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
import {
Expand All @@ -94,14 +101,22 @@ defineProps({
const emit = defineEmits(['change'])
const accounts = ref({})
const loginDisabled = ref(false)
const defaultUser = ref()
async function refreshValues() {
defaultUser.value = await get_default_user().catch(handleError)
accounts.value = await users().catch(handleError)
}
function setLoginDisabled(value) {
loginDisabled.value = value
}
defineExpose({
refreshValues,
setLoginDisabled,
loginDisabled,
})
await refreshValues()
Expand All @@ -120,6 +135,7 @@ async function setAccount(account) {
}
async function login() {
loginDisabled.value = true
const loggedIn = await login_flow().catch(handleSevereError)
if (loggedIn) {
Expand All @@ -128,6 +144,7 @@ async function login() {
}
trackEvent('AccountLogIn')
loginDisabled.value = false
}
const logout = async (id) => {
Expand Down
4 changes: 2 additions & 2 deletions apps/app-frontend/src/components/ui/ErrorModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,8 @@ async function copyToClipboard(text) {
<template v-else-if="metadata.notEnoughSpace">
<h3>Not enough space</h3>
<p>
It looks like there is not enough space on the disk containing the dirctory you
selected Please free up some space and try again or cancel the directory change.
It looks like there is not enough space on the disk containing the directory you
selected. Please free up some space and try again or cancel the directory change.
</p>
</template>
<template v-else>
Expand Down
2 changes: 1 addition & 1 deletion apps/app-frontend/src/components/ui/modal/ModalWrapper.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, useTemplateRef } from 'vue'
import { useTemplateRef } from 'vue'
import { NewModal as Modal } from '@modrinth/ui'
import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
import { useTheming } from '@/store/theme.ts'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ watch(
/>
</div>

<div class="mt-4 flex items-center justify-between">
<div>
<h2 class="m-0 text-lg font-extrabold text-contrast">Hide Nametag</h2>
<p class="m-0 mt-1">
Allows you to disable the nametag that appears above the main preview on the skin management
page.
</p>
</div>
<Toggle id="hide-nametag-skins-page" v-model="settings.hide_nametag_skins_page" />
</div>

<div v-if="os !== 'MacOS'" class="mt-4 flex items-center justify-between gap-4">
<div>
<h2 class="m-0 text-lg font-extrabold text-contrast">Native Decorations</h2>
Expand Down
Loading
Loading