fix: lint issues
This commit is contained in:
committed by
Alejandro González
parent
b3bc3a8731
commit
187e892c18
@@ -44,7 +44,13 @@
|
||||
</div>
|
||||
<div v-else class="logged-out account">
|
||||
<h4>Not signed in</h4>
|
||||
<Button v-tooltip="'Log in'" :disabled="loginDisabled" icon-only color="primary" @click="login()">
|
||||
<Button
|
||||
v-tooltip="'Log in'"
|
||||
:disabled="loginDisabled"
|
||||
icon-only
|
||||
color="primary"
|
||||
@click="login()"
|
||||
>
|
||||
<LogInIcon v-if="!loginDisabled" />
|
||||
<SpinnerIcon v-else class="animate-spin" />
|
||||
</Button>
|
||||
@@ -95,7 +101,7 @@ defineProps({
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const accounts = ref({})
|
||||
const loginDisabled = ref(false);
|
||||
const loginDisabled = ref(false)
|
||||
const defaultUser = ref()
|
||||
|
||||
async function refreshValues() {
|
||||
@@ -104,13 +110,13 @@ async function refreshValues() {
|
||||
}
|
||||
|
||||
function setLoginDisabled(value) {
|
||||
loginDisabled.value = value;
|
||||
loginDisabled.value = value
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
refreshValues,
|
||||
setLoginDisabled,
|
||||
loginDisabled
|
||||
loginDisabled,
|
||||
})
|
||||
await refreshValues()
|
||||
|
||||
@@ -129,7 +135,7 @@ async function setAccount(account) {
|
||||
}
|
||||
|
||||
async function login() {
|
||||
loginDisabled.value = true;
|
||||
loginDisabled.value = true
|
||||
const loggedIn = await login_flow().catch(handleSevereError)
|
||||
|
||||
if (loggedIn) {
|
||||
@@ -138,7 +144,7 @@ async function login() {
|
||||
}
|
||||
|
||||
trackEvent('AccountLogIn')
|
||||
loginDisabled.value = false;
|
||||
loginDisabled.value = false
|
||||
}
|
||||
|
||||
const logout = async (id) => {
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
:variant="variant"
|
||||
:texture-src="previewSkin"
|
||||
:cape-src="selectedCapeTexture"
|
||||
:scale="1.4" :fov="50"
|
||||
:scale="1.4"
|
||||
:fov="50"
|
||||
:initial-rotation="Math.PI / 8"
|
||||
class="h-full w-full"
|
||||
/>
|
||||
@@ -26,14 +27,12 @@
|
||||
<div class="flex flex-col gap-4 w-full min-h-[20rem]">
|
||||
<section>
|
||||
<h2 class="text-base font-semibold mb-2">Texture</h2>
|
||||
<Button @click="openUploadSkinModal">
|
||||
<UploadIcon /> Replace texture
|
||||
</Button>
|
||||
<Button @click="openUploadSkinModal"> <UploadIcon /> Replace texture </Button>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 class="text-base font-semibold mb-2">Arm style</h2>
|
||||
<RadioButtons v-model="variant" :items="['CLASSIC','SLIM']">
|
||||
<RadioButtons v-model="variant" :items="['CLASSIC', 'SLIM']">
|
||||
<template #default="{ item }">
|
||||
{{ item === 'CLASSIC' ? 'Wide' : 'Slim' }}
|
||||
</template>
|
||||
@@ -69,7 +68,7 @@
|
||||
tooltip="View more capes"
|
||||
@mouseup="openSelectCapeModal"
|
||||
>
|
||||
<template #icon><ChevronRightIcon/></template>
|
||||
<template #icon><ChevronRightIcon /></template>
|
||||
<span>More</span>
|
||||
</CapeLikeTextButton>
|
||||
</div>
|
||||
@@ -84,41 +83,49 @@
|
||||
{{ mode === 'new' ? 'Add skin' : 'Save skin' }}
|
||||
</Button>
|
||||
</ButtonStyled>
|
||||
<Button @click="hide"><XIcon/>Cancel</Button>
|
||||
<Button @click="hide"><XIcon />Cancel</Button>
|
||||
</div>
|
||||
</NewModal>
|
||||
|
||||
<SelectCapeModal ref="selectCapeModal" :capes="capes || []" @select="handleCapeSelected" @cancel="handleCapeCancel" />
|
||||
<SelectCapeModal
|
||||
ref="selectCapeModal"
|
||||
:capes="capes || []"
|
||||
@select="handleCapeSelected"
|
||||
@cancel="handleCapeCancel"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, useTemplateRef } from 'vue'
|
||||
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
|
||||
import {
|
||||
Card, FileInput, SkinPreviewRenderer,
|
||||
Button, RadioButtons, CapeButton, CapeLikeTextButton, ButtonStyled,
|
||||
NewModal
|
||||
SkinPreviewRenderer,
|
||||
Button,
|
||||
RadioButtons,
|
||||
CapeButton,
|
||||
CapeLikeTextButton,
|
||||
ButtonStyled,
|
||||
NewModal,
|
||||
} from '@modrinth/ui'
|
||||
import {
|
||||
add_and_equip_custom_skin,
|
||||
remove_custom_skin,
|
||||
unequip_skin,
|
||||
type Skin, type Cape, type SkinModel
|
||||
type Skin,
|
||||
type Cape,
|
||||
type SkinModel,
|
||||
} from '@/helpers/skins.ts'
|
||||
import { handleError } from '@/store/notifications'
|
||||
import {
|
||||
UploadIcon,
|
||||
CheckIcon, SaveIcon, XIcon, TrashIcon, ChevronRightIcon
|
||||
} from '@modrinth/assets'
|
||||
import { UploadIcon, CheckIcon, SaveIcon, XIcon, ChevronRightIcon } from '@modrinth/assets'
|
||||
|
||||
const modal = useTemplateRef('modal')
|
||||
const selectCapeModal = useTemplateRef('selectCapeModal')
|
||||
const mode = ref<'new'|'edit'>('new')
|
||||
const currentSkin = ref<Skin|null>(null)
|
||||
const mode = ref<'new' | 'edit'>('new')
|
||||
const currentSkin = ref<Skin | null>(null)
|
||||
const shouldRestoreModal = ref(false)
|
||||
|
||||
const fileUploadTextureBlob = ref<Uint8Array|null>(null)
|
||||
const fileName = ref<string|null>(null)
|
||||
const fileUploadTextureBlob = ref<Uint8Array | null>(null)
|
||||
const fileName = ref<string | null>(null)
|
||||
watch(fileUploadTextureBlob, () => {
|
||||
if (fileName.value === null && fileUploadTextureBlob.value) {
|
||||
fileName.value = 'New upload'
|
||||
@@ -126,7 +133,7 @@ watch(fileUploadTextureBlob, () => {
|
||||
})
|
||||
|
||||
const variant = ref<SkinModel>('CLASSIC')
|
||||
const selectedCape = ref<Cape|undefined>(undefined)
|
||||
const selectedCape = ref<Cape | undefined>(undefined)
|
||||
const props = defineProps<{ capes?: Cape[] }>()
|
||||
|
||||
const selectedCapeTexture = computed(() => selectedCape.value?.texture)
|
||||
@@ -134,11 +141,11 @@ const visibleCapeList = ref<Cape[]>([])
|
||||
|
||||
const sortedCapes = computed(() => {
|
||||
return [...(props.capes || [])].sort((a, b) => {
|
||||
const nameA = (a.name || '').toLowerCase();
|
||||
const nameB = (b.name || '').toLowerCase();
|
||||
return nameA.localeCompare(nameB);
|
||||
});
|
||||
});
|
||||
const nameA = (a.name || '').toLowerCase()
|
||||
const nameB = (b.name || '').toLowerCase()
|
||||
return nameA.localeCompare(nameB)
|
||||
})
|
||||
})
|
||||
|
||||
function initVisibleCapeList() {
|
||||
if (!props.capes || props.capes.length === 0) {
|
||||
@@ -163,10 +170,10 @@ function getSortedCapes(count: number): Cape[] {
|
||||
|
||||
function getSortedCapeExcluding(excludeId: string): Cape | undefined {
|
||||
if (!sortedCapes.value || sortedCapes.value.length <= 1) return undefined
|
||||
return sortedCapes.value.find(cape => cape.id !== excludeId)
|
||||
return sortedCapes.value.find((cape) => cape.id !== excludeId)
|
||||
}
|
||||
|
||||
const localPreviewUrl = ref<string|null>(null)
|
||||
const localPreviewUrl = ref<string | null>(null)
|
||||
watch(fileUploadTextureBlob, (blob, prev) => {
|
||||
if (prev && localPreviewUrl.value) URL.revokeObjectURL(localPreviewUrl.value)
|
||||
if (blob) localPreviewUrl.value = URL.createObjectURL(new Blob([blob]))
|
||||
@@ -179,24 +186,25 @@ const previewSkin = computed(() => {
|
||||
})
|
||||
|
||||
const hasEdits = computed(() => {
|
||||
if (mode.value !== 'edit') return true;
|
||||
if (fileUploadTextureBlob.value) return true;
|
||||
if (!currentSkin.value) return false;
|
||||
if (variant.value !== currentSkin.value.variant) return true;
|
||||
if ((selectedCape.value?.id || null) !== (currentSkin.value.cape_id || null)) return true;
|
||||
return false;
|
||||
});
|
||||
if (mode.value !== 'edit') return true
|
||||
if (fileUploadTextureBlob.value) return true
|
||||
if (!currentSkin.value) return false
|
||||
if (variant.value !== currentSkin.value.variant) return true
|
||||
if ((selectedCape.value?.id || null) !== (currentSkin.value.cape_id || null)) return true
|
||||
return false
|
||||
})
|
||||
|
||||
const disableSave = computed(() =>
|
||||
(mode.value === 'new' && !fileUploadTextureBlob.value) ||
|
||||
(mode.value === 'edit' && !hasEdits.value)
|
||||
);
|
||||
const disableSave = computed(
|
||||
() =>
|
||||
(mode.value === 'new' && !fileUploadTextureBlob.value) ||
|
||||
(mode.value === 'edit' && !hasEdits.value),
|
||||
)
|
||||
|
||||
const saveTooltip = computed(() => {
|
||||
if (mode.value === 'new' && !fileUploadTextureBlob.value) return 'Upload a skin first!';
|
||||
if (mode.value === 'edit' && !hasEdits.value) return 'Make an edit to the skin first!';
|
||||
return undefined;
|
||||
});
|
||||
if (mode.value === 'new' && !fileUploadTextureBlob.value) return 'Upload a skin first!'
|
||||
if (mode.value === 'edit' && !hasEdits.value) return 'Make an edit to the skin first!'
|
||||
return undefined
|
||||
})
|
||||
|
||||
function resetState() {
|
||||
mode.value = 'new'
|
||||
@@ -218,7 +226,7 @@ function show(e: MouseEvent, skin?: Skin) {
|
||||
currentSkin.value = skin ?? null
|
||||
if (skin) {
|
||||
variant.value = skin.variant
|
||||
selectedCape.value = props.capes?.find(c=>c.id===skin.cape_id)
|
||||
selectedCape.value = props.capes?.find((c) => c.id === skin.cape_id)
|
||||
} else {
|
||||
variant.value = 'CLASSIC'
|
||||
selectedCape.value = undefined
|
||||
@@ -243,7 +251,7 @@ function showNew(e: MouseEvent, skinTexture: Uint8Array, filename: string) {
|
||||
function restoreWithNewTexture(skinTexture: Uint8Array, filename: string) {
|
||||
fileUploadTextureBlob.value = skinTexture
|
||||
fileName.value = filename
|
||||
|
||||
|
||||
if (shouldRestoreModal.value) {
|
||||
setTimeout(() => {
|
||||
modal.value?.show()
|
||||
@@ -257,12 +265,12 @@ function hide() {
|
||||
setTimeout(() => resetState(), 250)
|
||||
}
|
||||
|
||||
function selectCape(cape: Cape|undefined) {
|
||||
function selectCape(cape: Cape | undefined) {
|
||||
if (cape && selectedCape.value?.id !== cape.id) {
|
||||
const isInVisibleList = visibleCapeList.value.some(c => c.id === cape.id)
|
||||
const isInVisibleList = visibleCapeList.value.some((c) => c.id === cape.id)
|
||||
if (!isInVisibleList && visibleCapeList.value.length > 0) {
|
||||
visibleCapeList.value.splice(0, 1, cape)
|
||||
|
||||
|
||||
if (visibleCapeList.value.length > 1 && visibleCapeList.value[1].id === cape.id) {
|
||||
const otherCape = getSortedCapeExcluding(cape.id)
|
||||
if (otherCape) {
|
||||
@@ -306,7 +314,7 @@ function openSelectCapeModal(e: MouseEvent) {
|
||||
currentSkin.value?.texture_key,
|
||||
selectedCape.value,
|
||||
previewSkin.value,
|
||||
variant.value
|
||||
variant.value,
|
||||
)
|
||||
}, 0)
|
||||
}
|
||||
@@ -329,7 +337,7 @@ async function save() {
|
||||
blob = new Uint8Array(buf)
|
||||
}
|
||||
|
||||
await unequip_skin();
|
||||
await unequip_skin()
|
||||
|
||||
if (mode.value === 'new') {
|
||||
await add_and_equip_custom_skin(blob, variant.value, selectedCape.value)
|
||||
@@ -346,9 +354,13 @@ async function save() {
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.capes, () => {
|
||||
initVisibleCapeList()
|
||||
}, { immediate: true })
|
||||
watch(
|
||||
() => props.capes,
|
||||
() => {
|
||||
initVisibleCapeList()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'saved'): void
|
||||
@@ -361,6 +373,6 @@ defineExpose({
|
||||
showNew,
|
||||
restoreWithNewTexture,
|
||||
hide,
|
||||
shouldRestoreModal
|
||||
shouldRestoreModal,
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
CapeButton,
|
||||
CapeLikeTextButton,
|
||||
SkinPreviewRenderer,
|
||||
NewModal
|
||||
NewModal,
|
||||
} from '@modrinth/ui'
|
||||
import { CheckIcon, XIcon } from '@modrinth/assets'
|
||||
|
||||
@@ -24,11 +24,11 @@ const props = defineProps<{
|
||||
|
||||
const sortedCapes = computed(() => {
|
||||
return [...props.capes].sort((a, b) => {
|
||||
const nameA = (a.name || '').toLowerCase();
|
||||
const nameB = (b.name || '').toLowerCase();
|
||||
return nameA.localeCompare(nameB);
|
||||
});
|
||||
});
|
||||
const nameA = (a.name || '').toLowerCase()
|
||||
const nameB = (b.name || '').toLowerCase()
|
||||
return nameA.localeCompare(nameB)
|
||||
})
|
||||
})
|
||||
|
||||
const currentSkinId = ref<string | undefined>()
|
||||
const currentSkinTexture = ref<string | undefined>()
|
||||
@@ -93,7 +93,7 @@ defineExpose({
|
||||
:variant="currentSkinVariant"
|
||||
:scale="1.4"
|
||||
:fov="50"
|
||||
:initial-rotation="Math.PI + (Math.PI / 8)"
|
||||
:initial-rotation="Math.PI + Math.PI / 8"
|
||||
class="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<NewModal ref="modal" @on-hide="hide(true)">
|
||||
<template #title>
|
||||
<span class="text-lg font-extrabold text-contrast">
|
||||
Upload skin texture
|
||||
</span>
|
||||
<span class="text-lg font-extrabold text-contrast"> Upload skin texture </span>
|
||||
</template>
|
||||
<div class="relative">
|
||||
<div
|
||||
@@ -12,10 +10,22 @@
|
||||
@drop.prevent="handleFileOperation"
|
||||
@dragover.prevent
|
||||
>
|
||||
<p class="mx-auto mb-0 text-primary text-xl text-center flex items-center gap-2"><UploadIcon /> Select skin texture file</p>
|
||||
<p class="mx-auto mt-0 text-secondary text-sm text-center">Drag and drop or click here to browse</p>
|
||||
<p class="mx-auto mt-0 text-secondary text-xs text-center">Only 64x64 PNG files are accepted</p>
|
||||
<input ref="fileInput" type="file" accept="image/png" class="hidden" @change="handleFileOperation" />
|
||||
<p class="mx-auto mb-0 text-primary text-xl text-center flex items-center gap-2">
|
||||
<UploadIcon /> Select skin texture file
|
||||
</p>
|
||||
<p class="mx-auto mt-0 text-secondary text-sm text-center">
|
||||
Drag and drop or click here to browse
|
||||
</p>
|
||||
<p class="mx-auto mt-0 text-secondary text-xs text-center">
|
||||
Only 64x64 PNG files are accepted
|
||||
</p>
|
||||
<input
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
accept="image/png"
|
||||
class="hidden"
|
||||
@change="handleFileOperation"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
@@ -24,7 +34,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { UploadIcon } from '@modrinth/assets'
|
||||
import { useNotifications } from "@/store/state"
|
||||
import { useNotifications } from '@/store/state'
|
||||
import { NewModal } from '@modrinth/ui'
|
||||
|
||||
const notifications = useNotifications()
|
||||
@@ -71,30 +81,30 @@ async function validateImageDimensions(file: File): Promise<boolean> {
|
||||
|
||||
async function handleFileOperation(e: Event | DragEvent) {
|
||||
// Get files from either drag event or file input
|
||||
const files = (e as DragEvent).dataTransfer?.files || (e.target as HTMLInputElement).files;
|
||||
const files = (e as DragEvent).dataTransfer?.files || (e.target as HTMLInputElement).files
|
||||
if (!files || files.length === 0) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const file = files[0];
|
||||
const file = files[0]
|
||||
if (file.type !== 'image/png') {
|
||||
notifications.addNotification({
|
||||
title: 'Invalid file type.',
|
||||
text: 'Only PNG files are accepted.',
|
||||
type: 'error',
|
||||
});
|
||||
return;
|
||||
})
|
||||
return
|
||||
}
|
||||
const isValidDimensions = await validateImageDimensions(file);
|
||||
const isValidDimensions = await validateImageDimensions(file)
|
||||
if (!isValidDimensions) {
|
||||
notifications.addNotification({
|
||||
title: 'Invalid dimensions.',
|
||||
text: 'Only 64x64 PNG files are accepted.',
|
||||
type: 'error',
|
||||
});
|
||||
return;
|
||||
})
|
||||
return
|
||||
}
|
||||
emit('uploaded', file);
|
||||
hide();
|
||||
emit('uploaded', file)
|
||||
hide()
|
||||
}
|
||||
|
||||
defineExpose({ show, hide })
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { UpdatedIcon, PlusIcon, ExcitedRinthbot, LogInIcon, SpinnerIcon, EditIcon, TrashIcon } from '@modrinth/assets'
|
||||
import {
|
||||
UpdatedIcon,
|
||||
PlusIcon,
|
||||
ExcitedRinthbot,
|
||||
LogInIcon,
|
||||
SpinnerIcon,
|
||||
EditIcon,
|
||||
TrashIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
SkinPreviewRenderer,
|
||||
SkinButton,
|
||||
SkinLikeTextButton,
|
||||
Button,
|
||||
ConfirmModal
|
||||
ConfirmModal,
|
||||
} from '@modrinth/ui'
|
||||
import type { Ref } from 'vue';
|
||||
import type { Ref } from 'vue'
|
||||
import { ref, computed, useTemplateRef, watch, onMounted, onUnmounted, inject } from 'vue'
|
||||
import EditSkinModal from '@/components/ui/skin/EditSkinModal.vue'
|
||||
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
|
||||
@@ -28,9 +36,9 @@ import type { Cape, Skin } from '@/helpers/skins.ts'
|
||||
import { get_default_user, users, login as login_flow } from '@/helpers/auth'
|
||||
import type { RenderResult } from '@/helpers/rendering/batchSkinRenderer.ts'
|
||||
import { generateSkinPreviews, map } from '@/helpers/rendering/batchSkinRenderer.ts'
|
||||
import { handleSevereError } from "@/store/error";
|
||||
import { trackEvent } from "@/helpers/analytics";
|
||||
import type AccountsCard from "@/components/ui/AccountsCard.vue";
|
||||
import { handleSevereError } from '@/store/error'
|
||||
import { trackEvent } from '@/helpers/analytics'
|
||||
import type AccountsCard from '@/components/ui/AccountsCard.vue'
|
||||
|
||||
const editSkinModal = useTemplateRef('editSkinModal')
|
||||
const selectCapeModal = useTemplateRef('selectCapeModal')
|
||||
@@ -64,7 +72,9 @@ const currentCape = computed(() => {
|
||||
const skinTexture = computed(() => selectedSkin.value?.texture ?? '')
|
||||
const capeTexture = computed(() => currentCape.value?.texture)
|
||||
const skinVariant = computed(() => selectedSkin.value?.variant)
|
||||
const skinNametag = computed(() => settings.value.hide_nametag_skins_page ? undefined : username.value)
|
||||
const skinNametag = computed(() =>
|
||||
settings.value.hide_nametag_skins_page ? undefined : username.value,
|
||||
)
|
||||
|
||||
let userCheckInterval: number | null = null
|
||||
|
||||
@@ -141,7 +151,7 @@ function getBakedSkinTextures(skin: Skin): RenderResult | undefined {
|
||||
}
|
||||
|
||||
async function login() {
|
||||
accountsCard.value.setLoginDisabled(true);
|
||||
accountsCard.value.setLoginDisabled(true)
|
||||
const loggedIn = await login_flow().catch(handleSevereError)
|
||||
|
||||
if (loggedIn && accountsCard) {
|
||||
@@ -149,7 +159,7 @@ async function login() {
|
||||
}
|
||||
|
||||
trackEvent('AccountLogIn')
|
||||
accountsCard.value.setLoginDisabled(false);
|
||||
accountsCard.value.setLoginDisabled(false)
|
||||
}
|
||||
|
||||
function openUploadSkinModal(e: MouseEvent) {
|
||||
@@ -158,9 +168,9 @@ function openUploadSkinModal(e: MouseEvent) {
|
||||
|
||||
function onSkinFileUploaded(file: File) {
|
||||
const fakeEvent = new MouseEvent('click')
|
||||
file.arrayBuffer().then(buf => {
|
||||
file.arrayBuffer().then((buf) => {
|
||||
const skinTexture = new Uint8Array(buf)
|
||||
|
||||
|
||||
if (editSkinModal.value && editSkinModal.value.shouldRestoreModal) {
|
||||
editSkinModal.value.restoreWithNewTexture(skinTexture, file.name)
|
||||
} else {
|
||||
@@ -181,16 +191,16 @@ function onUploadCanceled() {
|
||||
|
||||
watch(
|
||||
() => selectedSkin.value?.cape_id,
|
||||
() => {}
|
||||
() => {},
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
userCheckInterval = window.setInterval(checkUserChanges, 250);
|
||||
userCheckInterval = window.setInterval(checkUserChanges, 250)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (userCheckInterval !== null) {
|
||||
window.clearInterval(userCheckInterval);
|
||||
window.clearInterval(userCheckInterval)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -198,9 +208,9 @@ async function checkUserChanges() {
|
||||
try {
|
||||
const defaultId = await get_default_user()
|
||||
if (defaultId !== currentUserId.value) {
|
||||
await loadCurrentUser();
|
||||
await loadCapes();
|
||||
await loadSkins();
|
||||
await loadCurrentUser()
|
||||
await loadCapes()
|
||||
await loadSkins()
|
||||
}
|
||||
} catch (error) {
|
||||
if (currentUser.value) {
|
||||
@@ -221,7 +231,11 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
@open-upload-modal="openUploadSkinModal"
|
||||
/>
|
||||
<SelectCapeModal ref="selectCapeModal" :capes="capes" @select="handleCapeSelected" />
|
||||
<UploadSkinModal ref="uploadSkinModal" @uploaded="onSkinFileUploaded" @canceled="onUploadCanceled" />
|
||||
<UploadSkinModal
|
||||
ref="uploadSkinModal"
|
||||
@uploaded="onSkinFileUploaded"
|
||||
@canceled="onUploadCanceled"
|
||||
/>
|
||||
<ConfirmModal
|
||||
ref="deleteSkinModal"
|
||||
title="Are you sure you want to delete this skin?"
|
||||
@@ -237,20 +251,21 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
<ButtonStyled :disabled="!!selectedSkin?.cape_id">
|
||||
<button
|
||||
v-tooltip="
|
||||
selectedSkin?.cape_id
|
||||
? 'The equipped skin is overriding the default cape.'
|
||||
: undefined
|
||||
"
|
||||
selectedSkin?.cape_id
|
||||
? 'The equipped skin is overriding the default cape.'
|
||||
: undefined
|
||||
"
|
||||
:disabled="!!selectedSkin?.cape_id"
|
||||
@click="
|
||||
(e: MouseEvent) => selectCapeModal?.show(
|
||||
e,
|
||||
selectedSkin?.texture_key,
|
||||
currentCape,
|
||||
skinTexture,
|
||||
skinVariant
|
||||
)
|
||||
"
|
||||
(e: MouseEvent) =>
|
||||
selectCapeModal?.show(
|
||||
e,
|
||||
selectedSkin?.texture_key,
|
||||
currentCape,
|
||||
skinTexture,
|
||||
skinVariant,
|
||||
)
|
||||
"
|
||||
>
|
||||
<UpdatedIcon />
|
||||
Change cape
|
||||
@@ -275,15 +290,11 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
<section class="flex flex-col gap-2 mt-1">
|
||||
<h2 class="text-lg font-bold m-0 text-primary">Saved skins</h2>
|
||||
<div class="skin-card-grid">
|
||||
<SkinLikeTextButton
|
||||
class="skin-card"
|
||||
tooltip="Add a skin"
|
||||
@click="openUploadSkinModal"
|
||||
>
|
||||
<template #icon>
|
||||
<PlusIcon class="w-5 h-5 stroke-2" />
|
||||
</template>
|
||||
<span>Add a skin</span>
|
||||
<SkinLikeTextButton class="skin-card" tooltip="Add a skin" @click="openUploadSkinModal">
|
||||
<template #icon>
|
||||
<PlusIcon class="w-5 h-5 stroke-2" />
|
||||
</template>
|
||||
<span>Add a skin</span>
|
||||
</SkinLikeTextButton>
|
||||
|
||||
<SkinButton
|
||||
@@ -296,24 +307,24 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
@select="changeSkin(skin)"
|
||||
>
|
||||
<template #overlay-buttons>
|
||||
<Button
|
||||
color="green"
|
||||
aria-label="Edit skin"
|
||||
@click.stop="(e) => editSkinModal?.show(e, skin)"
|
||||
>
|
||||
<EditIcon class="w-5 h-5" /> Edit
|
||||
</Button>
|
||||
<Button
|
||||
v-show="!skin.is_equipped"
|
||||
v-tooltip="'Delete skin'"
|
||||
aria-label="Delete skin"
|
||||
color="red"
|
||||
class="!rounded-[100%]"
|
||||
icon-only
|
||||
@click.stop="() => confirmDeleteSkin(skin)"
|
||||
>
|
||||
<TrashIcon class="w-5 h-5" />
|
||||
</Button>
|
||||
<Button
|
||||
color="green"
|
||||
aria-label="Edit skin"
|
||||
@click.stop="(e) => editSkinModal?.show(e, skin)"
|
||||
>
|
||||
<EditIcon class="w-5 h-5" /> Edit
|
||||
</Button>
|
||||
<Button
|
||||
v-show="!skin.is_equipped"
|
||||
v-tooltip="'Delete skin'"
|
||||
aria-label="Delete skin"
|
||||
color="red"
|
||||
class="!rounded-[100%]"
|
||||
icon-only
|
||||
@click.stop="() => confirmDeleteSkin(skin)"
|
||||
>
|
||||
<TrashIcon class="w-5 h-5" />
|
||||
</Button>
|
||||
</template>
|
||||
</SkinButton>
|
||||
</div>
|
||||
@@ -338,14 +349,32 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
</div>
|
||||
|
||||
<div v-else class="flex items-center justify-center min-h-[50vh] pt-[25%]">
|
||||
<div class="bg-bg-raised rounded-lg p-7 flex flex-col gap-5 shadow-md relative max-w-xl w-full mx-auto">
|
||||
<img :src="ExcitedRinthbot" alt="Excited Modrinth Bot" class="absolute -top-28 right-8 md:right-20 h-28 w-auto" />
|
||||
<div class="absolute top-0 left-0 w-full h-[1px] opacity-40 bg-gradient-to-r from-transparent via-green-500 to-transparent" style="background: linear-gradient(to right, transparent 2rem, var(--color-green) calc(100% - 13rem), var(--color-green) calc(100% - 5rem), transparent calc(100% - 2rem))"></div>
|
||||
<div
|
||||
class="bg-bg-raised rounded-lg p-7 flex flex-col gap-5 shadow-md relative max-w-xl w-full mx-auto"
|
||||
>
|
||||
<img
|
||||
:src="ExcitedRinthbot"
|
||||
alt="Excited Modrinth Bot"
|
||||
class="absolute -top-28 right-8 md:right-20 h-28 w-auto"
|
||||
/>
|
||||
<div
|
||||
class="absolute top-0 left-0 w-full h-[1px] opacity-40 bg-gradient-to-r from-transparent via-green-500 to-transparent"
|
||||
style="
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
transparent 2rem,
|
||||
var(--color-green) calc(100% - 13rem),
|
||||
var(--color-green) calc(100% - 5rem),
|
||||
transparent calc(100% - 2rem)
|
||||
);
|
||||
"
|
||||
></div>
|
||||
|
||||
<div class="flex flex-col gap-5">
|
||||
<h1 class="text-3xl font-extrabold m-0">Please sign-in</h1>
|
||||
<p class="text-lg m-0">
|
||||
Please sign into your Minecraft account to use the skin management features of the Modrinth app.
|
||||
Please sign into your Minecraft account to use the skin management features of the
|
||||
Modrinth app.
|
||||
</p>
|
||||
<ButtonStyled v-show="accountsCard" color="brand" :disabled="accountsCard.loginDisabled">
|
||||
<Button :disabled="accountsCard.loginDisabled" @click="login">
|
||||
|
||||
@@ -66,13 +66,13 @@ function onScroll({ target: { scrollTop, offsetHeight, scrollHeight } }) {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@property --_top-fade-height {
|
||||
syntax: "<length-percentage>";
|
||||
syntax: '<length-percentage>';
|
||||
inherits: false;
|
||||
initial-value: 0%;
|
||||
}
|
||||
|
||||
@property --_bottom-fade-height {
|
||||
syntax: "<length-percentage>";
|
||||
syntax: '<length-percentage>';
|
||||
inherits: false;
|
||||
initial-value: 0%;
|
||||
}
|
||||
@@ -88,15 +88,17 @@ function onScroll({ target: { scrollTop, offsetHeight, scrollHeight } }) {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: --_top-fade-height 0.05s linear, --_bottom-fade-height 0.05s linear;
|
||||
transition:
|
||||
--_top-fade-height 0.05s linear,
|
||||
--_bottom-fade-height 0.05s linear;
|
||||
|
||||
--_fade-height: 3rem;
|
||||
|
||||
mask-image: linear-gradient(
|
||||
transparent,
|
||||
rgb(0 0 0 / 100%) var(--_top-fade-height, 0%),
|
||||
rgb(0 0 0 / 100%) calc(100% - var(--_bottom-fade-height, 0%)),
|
||||
transparent 100%
|
||||
transparent,
|
||||
rgb(0 0 0 / 100%) var(--_top-fade-height, 0%),
|
||||
rgb(0 0 0 / 100%) calc(100% - var(--_bottom-fade-height, 0%)),
|
||||
transparent 100%
|
||||
);
|
||||
|
||||
&.top-fade {
|
||||
|
||||
@@ -100,11 +100,11 @@ export { default as AddPaymentMethodModal } from './billing/AddPaymentMethodModa
|
||||
export { default as ModrinthServersPurchaseModal } from './billing/ModrinthServersPurchaseModal.vue'
|
||||
|
||||
// Skins
|
||||
export { default as SkinPreviewRenderer } from "./skin/SkinPreviewRenderer.vue"
|
||||
export { default as CapeButton } from "./skin/CapeButton.vue"
|
||||
export { default as CapeLikeTextButton } from "./skin/CapeLikeTextButton.vue"
|
||||
export { default as SkinButton } from "./skin/SkinButton.vue"
|
||||
export { default as SkinLikeTextButton } from "./skin/SkinLikeTextButton.vue"
|
||||
export { default as SkinPreviewRenderer } from './skin/SkinPreviewRenderer.vue'
|
||||
export { default as CapeButton } from './skin/CapeButton.vue'
|
||||
export { default as CapeLikeTextButton } from './skin/CapeLikeTextButton.vue'
|
||||
export { default as SkinButton } from './skin/SkinButton.vue'
|
||||
export { default as SkinLikeTextButton } from './skin/SkinLikeTextButton.vue'
|
||||
|
||||
// Version
|
||||
export { default as VersionChannelIndicator } from './version/VersionChannelIndicator.vue'
|
||||
|
||||
@@ -19,13 +19,18 @@ const props = withDefaults(
|
||||
},
|
||||
)
|
||||
|
||||
console.log(props);
|
||||
console.log(props)
|
||||
|
||||
const highlighted = computed(() => props.selected ?? props.isEquipped)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button v-tooltip="name" class="block border-0 m-0 p-0 bg-transparent group cursor-pointer" :aria-label="name" @click="emit('select')">
|
||||
<button
|
||||
v-tooltip="name"
|
||||
class="block border-0 m-0 p-0 bg-transparent group cursor-pointer"
|
||||
:aria-label="name"
|
||||
@click="emit('select')"
|
||||
>
|
||||
<span
|
||||
:class="
|
||||
highlighted
|
||||
|
||||
@@ -26,7 +26,7 @@ withDefaults(
|
||||
'block rounded-lg group-active:scale-95 transition-all border-2 relative',
|
||||
highlighted
|
||||
? 'border-brand highlighted-glow'
|
||||
: 'border-transparent opacity-75 group-hover:opacity-100'
|
||||
: 'border-transparent opacity-75 group-hover:opacity-100',
|
||||
]"
|
||||
>
|
||||
<span class="block p-[3px] rounded-lg bg-button-bg">
|
||||
|
||||
@@ -6,20 +6,23 @@ const emit = defineEmits<{
|
||||
(e: 'edit', event: MouseEvent): void
|
||||
}>()
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
forwardImageSrc?: string
|
||||
backwardImageSrc?: string
|
||||
selected: boolean
|
||||
tooltip?: string
|
||||
}>(), {
|
||||
forwardImageSrc: undefined,
|
||||
backwardImageSrc: undefined,
|
||||
tooltip: undefined,
|
||||
})
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
forwardImageSrc?: string
|
||||
backwardImageSrc?: string
|
||||
selected: boolean
|
||||
tooltip?: string
|
||||
}>(),
|
||||
{
|
||||
forwardImageSrc: undefined,
|
||||
backwardImageSrc: undefined,
|
||||
tooltip: undefined,
|
||||
},
|
||||
)
|
||||
|
||||
const imagesLoaded = ref({
|
||||
forward: Boolean(props.forwardImageSrc),
|
||||
backward: Boolean(props.backwardImageSrc)
|
||||
backward: Boolean(props.backwardImageSrc),
|
||||
})
|
||||
|
||||
function onImageLoad(type: 'forward' | 'backward') {
|
||||
@@ -31,9 +34,7 @@ function onImageLoad(type: 'forward' | 'backward') {
|
||||
<div
|
||||
v-tooltip="tooltip ?? undefined"
|
||||
class="group flex relative overflow-hidden rounded-xl border-solid border-2 transition-colors duration-200"
|
||||
:class="[
|
||||
selected ? 'border-brand' : 'border-transparent hover:border-white/50'
|
||||
]"
|
||||
:class="[selected ? 'border-brand' : 'border-transparent hover:border-white/50']"
|
||||
>
|
||||
<button
|
||||
class="skin-btn-bg absolute inset-0 cursor-pointer p-0 border-none group-hover:brightness-125"
|
||||
@@ -41,13 +42,19 @@ function onImageLoad(type: 'forward' | 'backward') {
|
||||
@click="emit('select')"
|
||||
></button>
|
||||
|
||||
<div v-if="!(imagesLoaded.forward && imagesLoaded.backward)" class="skeleton-loader w-full h-full">
|
||||
<div
|
||||
v-if="!(imagesLoaded.forward && imagesLoaded.backward)"
|
||||
class="skeleton-loader w-full h-full"
|
||||
>
|
||||
<div class="skeleton absolute inset-0 aspect-[5/7]"></div>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-show="imagesLoaded.forward && imagesLoaded.backward"
|
||||
:class="['skin-button__image-parent pointer-events-none w-full h-full grid [transform-style:preserve-3d] transition-transform duration-500 group-hover:[transform:rotateY(180deg)] place-items-stretch', selected ? 'with-shadow' : '']"
|
||||
:class="[
|
||||
'skin-button__image-parent pointer-events-none w-full h-full grid [transform-style:preserve-3d] transition-transform duration-500 group-hover:[transform:rotateY(180deg)] place-items-stretch',
|
||||
selected ? 'with-shadow' : '',
|
||||
]"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
@@ -68,7 +75,7 @@ function onImageLoad(type: 'forward' | 'backward') {
|
||||
<span
|
||||
v-if="$slots['overlay-buttons']"
|
||||
class="absolute inset-0 flex items-end justify-start p-1 gap-1 translate-y-4 scale-75 opacity-0 transition-all group-hover:opacity-100 group-hover:scale-100 group-hover:translate-y-0 group-hover:translate-x-0"
|
||||
style="pointer-events: none;"
|
||||
style="pointer-events: none"
|
||||
>
|
||||
<slot name="overlay-buttons" />
|
||||
</span>
|
||||
@@ -104,7 +111,13 @@ function onImageLoad(type: 'forward' | 'backward') {
|
||||
background: linear-gradient(180deg, #3a3d47 0%, #33363d 100%);
|
||||
}
|
||||
.skin-btn-bg.selected {
|
||||
background: linear-gradient(157.61deg, var(--color-brand) -76.68%, rgba(27, 217, 106, 0.534) -38.61%, rgba(12, 89, 44, 0.6) 100.4%), #27292F;
|
||||
background: linear-gradient(
|
||||
157.61deg,
|
||||
var(--color-brand) -76.68%,
|
||||
rgba(27, 217, 106, 0.534) -38.61%,
|
||||
rgba(12, 89, 44, 0.6) 100.4%
|
||||
),
|
||||
#27292f;
|
||||
}
|
||||
|
||||
.skin-btn-bg.selected:hover,
|
||||
|
||||
@@ -14,7 +14,9 @@ const pressed = ref(false)
|
||||
@mouseup="pressed = false"
|
||||
@mouseleave="pressed = false"
|
||||
></button>
|
||||
<div class="relative w-full h-full flex flex-col items-center justify-center pointer-events-none">
|
||||
<div
|
||||
class="relative w-full h-full flex flex-col items-center justify-center pointer-events-none"
|
||||
>
|
||||
<div class="mb-2">
|
||||
<slot name="icon"></slot>
|
||||
</div>
|
||||
@@ -28,7 +30,9 @@ const pressed = ref(false)
|
||||
<style scoped lang="scss">
|
||||
.skin-like-text-bg {
|
||||
background: linear-gradient(180deg, #3a3d47 0%, #33363d 100%);
|
||||
transition: filter 200ms ease-in-out, box-shadow 200ms ease-in-out;
|
||||
transition:
|
||||
filter 200ms ease-in-out,
|
||||
box-shadow 200ms ease-in-out;
|
||||
}
|
||||
.skin-like-text-bg:hover {
|
||||
filter: brightness(var(--hover-brightness));
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<template>
|
||||
<div class="relative w-full h-full cursor-grab">
|
||||
<div class="absolute bottom-[18%] left-0 right-0 flex justify-center items-center mb-2 pointer-events-none z-10">
|
||||
<div
|
||||
class="absolute bottom-[18%] left-0 right-0 flex justify-center items-center mb-2 pointer-events-none z-10"
|
||||
>
|
||||
<span class="text-primary text-xs px-2 py-1 rounded-full backdrop-blur-sm">
|
||||
Drag to rotate
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="nametag" class="absolute top-[13%] left-1/2 transform -translate-x-1/2 px-3 py-1 rounded-md text-[200%] pointer-events-none z-10 font-minecraft text-primary nametag-bg">
|
||||
<div
|
||||
v-if="nametag"
|
||||
class="absolute top-[13%] left-1/2 transform -translate-x-1/2 px-3 py-1 rounded-md text-[200%] pointer-events-none z-10 font-minecraft text-primary nametag-bg"
|
||||
>
|
||||
{{ nametag }}
|
||||
</div>
|
||||
|
||||
@@ -13,18 +18,22 @@
|
||||
shadows
|
||||
alpha
|
||||
:antialias="antialias"
|
||||
:renderer-options="{
|
||||
outputColorSpace: THREE.SRGBColorSpace,
|
||||
toneMapping: THREE.NoToneMapping,
|
||||
}"
|
||||
@pointerdown="onPointerDown"
|
||||
@pointermove="onPointerMove"
|
||||
@pointerup="onPointerUp"
|
||||
@pointerleave="onPointerUp"
|
||||
:rendererOptions="{
|
||||
outputColorSpace: THREE.SRGBColorSpace,
|
||||
toneMapping: THREE.NoToneMapping
|
||||
}"
|
||||
>
|
||||
<Suspense>
|
||||
<Group>
|
||||
<Group :rotation="[0, modelRotation, 0]" :position="[0, -0.05 * scale, 1.95]" :scale="[0.8 * scale, 0.8 * scale, 0.8 * scale]">
|
||||
<Group
|
||||
:rotation="[0, modelRotation, 0]"
|
||||
:position="[0, -0.05 * scale, 1.95]"
|
||||
:scale="[0.8 * scale, 0.8 * scale, 0.8 * scale]"
|
||||
>
|
||||
<primitive v-if="scene" :object="scene" />
|
||||
</Group>
|
||||
|
||||
@@ -42,7 +51,11 @@
|
||||
/>
|
||||
</TresMesh>
|
||||
|
||||
<TresMesh :position="[0, -0.1 * scale, 2]" :rotation="[-Math.PI / 2, 0, 0]" :scale="[0.75 * scale, 0.75 * scale, 0.75 * scale]">
|
||||
<TresMesh
|
||||
:position="[0, -0.1 * scale, 2]"
|
||||
:rotation="[-Math.PI / 2, 0, 0]"
|
||||
:scale="[0.75 * scale, 0.75 * scale, 0.75 * scale]"
|
||||
>
|
||||
<TresPlaneGeometry :args="[2, 2]" />
|
||||
<TresMeshBasicMaterial
|
||||
:map="radialTexture"
|
||||
@@ -55,7 +68,7 @@
|
||||
</Suspense>
|
||||
|
||||
<TresPerspectiveCamera
|
||||
:makeDefault="true"
|
||||
:make-default="true"
|
||||
:fov="fov"
|
||||
:position="[0, 1.5, -3.25]"
|
||||
:look-at="target"
|
||||
@@ -83,7 +96,7 @@ const props = withDefaults(
|
||||
nametag?: string
|
||||
antialias?: boolean
|
||||
scale?: number
|
||||
fov?: number,
|
||||
fov?: number
|
||||
initialRotation?: number
|
||||
}>(),
|
||||
{
|
||||
@@ -94,11 +107,11 @@ const props = withDefaults(
|
||||
capeModelSrc: '',
|
||||
capeSrc: undefined,
|
||||
initialRotation: 15.75,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const selectedModelSrc = computed(() =>
|
||||
props.variant === 'SLIM' ? props.slimModelSrc : props.wideModelSrc
|
||||
props.variant === 'SLIM' ? props.slimModelSrc : props.wideModelSrc,
|
||||
)
|
||||
|
||||
const scene = shallowRef<THREE.Object3D | null>(null)
|
||||
@@ -135,7 +148,7 @@ async function loadModel(src: string) {
|
||||
}
|
||||
|
||||
bodyNode.value = null
|
||||
loadedScene.traverse(node => {
|
||||
loadedScene.traverse((node) => {
|
||||
if (node.name === 'Body') {
|
||||
bodyNode.value = node
|
||||
}
|
||||
@@ -218,11 +231,11 @@ function attachCapeToBody() {
|
||||
function applyTextureToScene(root: THREE.Object3D | null, tex: THREE.Texture | null) {
|
||||
if (!root || !tex) return
|
||||
|
||||
root.traverse(child => {
|
||||
root.traverse((child) => {
|
||||
if ((child as THREE.Mesh).isMesh) {
|
||||
const mesh = child as THREE.Mesh
|
||||
|
||||
if (mesh.name === "Cape") return
|
||||
if (mesh.name === 'Cape') return
|
||||
|
||||
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]
|
||||
|
||||
@@ -242,7 +255,7 @@ function applyTextureToScene(root: THREE.Object3D | null, tex: THREE.Texture | n
|
||||
function applyCapeTexture(root: THREE.Object3D | null, tex: THREE.Texture | null) {
|
||||
if (!root) return
|
||||
|
||||
root.traverse(child => {
|
||||
root.traverse((child) => {
|
||||
if ((child as THREE.Mesh).isMesh) {
|
||||
const mesh = child as THREE.Mesh
|
||||
|
||||
@@ -282,7 +295,7 @@ const isDragging = ref(false)
|
||||
const previousX = ref(0)
|
||||
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
(event.currentTarget as HTMLElement).setPointerCapture(event.pointerId)
|
||||
;(event.currentTarget as HTMLElement).setPointerCapture(event.pointerId)
|
||||
isDragging.value = true
|
||||
previousX.value = event.clientX
|
||||
}
|
||||
@@ -295,8 +308,8 @@ const onPointerMove = (event: PointerEvent) => {
|
||||
}
|
||||
|
||||
const onPointerUp = (event: PointerEvent) => {
|
||||
isDragging.value = false;
|
||||
(event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId)
|
||||
isDragging.value = false
|
||||
;(event.currentTarget as HTMLElement).releasePointerCapture(event.pointerId)
|
||||
}
|
||||
|
||||
const radialTexture = createRadialTexture(512)
|
||||
@@ -316,17 +329,26 @@ function createRadialTexture(size: number): THREE.CanvasTexture {
|
||||
return new THREE.CanvasTexture(canvas)
|
||||
}
|
||||
|
||||
watch(selectedModelSrc, src => loadModel(src))
|
||||
watch(() => props.capeModelSrc, src => src && loadCape(src))
|
||||
watch(() => props.textureSrc, async newSrc => {
|
||||
texture.value = await loadAndApplyTexture(newSrc)
|
||||
if (scene.value && texture.value) {
|
||||
applyTextureToScene(scene.value, texture.value)
|
||||
}
|
||||
})
|
||||
watch(() => props.capeSrc, async newCapeSrc => {
|
||||
await loadAndApplyCapeTexture(newCapeSrc)
|
||||
})
|
||||
watch(selectedModelSrc, (src) => loadModel(src))
|
||||
watch(
|
||||
() => props.capeModelSrc,
|
||||
(src) => src && loadCape(src),
|
||||
)
|
||||
watch(
|
||||
() => props.textureSrc,
|
||||
async (newSrc) => {
|
||||
texture.value = await loadAndApplyTexture(newSrc)
|
||||
if (scene.value && texture.value) {
|
||||
applyTextureToScene(scene.value, texture.value)
|
||||
}
|
||||
},
|
||||
)
|
||||
watch(
|
||||
() => props.capeSrc,
|
||||
async (newCapeSrc) => {
|
||||
await loadAndApplyCapeTexture(newCapeSrc)
|
||||
},
|
||||
)
|
||||
|
||||
onBeforeMount(async () => {
|
||||
texture.value = await loadAndApplyTexture(props.textureSrc)
|
||||
@@ -345,7 +367,10 @@ onBeforeMount(async () => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nametag-bg {
|
||||
background: linear-gradient(308.68deg, rgba(0, 0, 0, 0) -52.46%, rgba(100, 100, 100, 0.1) 94.75%), rgba(0, 0, 0, 0.2);
|
||||
box-shadow: inset -0.5px -0.5px 0px rgba(0, 0, 0, 0.25), inset 0.5px 0.5px 0px rgba(255, 255, 255, 0.05);
|
||||
background: linear-gradient(308.68deg, rgba(0, 0, 0, 0) -52.46%, rgba(100, 100, 100, 0.1) 94.75%),
|
||||
rgba(0, 0, 0, 0.2);
|
||||
box-shadow:
|
||||
inset -0.5px -0.5px 0px rgba(0, 0, 0, 0.25),
|
||||
inset 0.5px 0.5px 0px rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user