fix: make progress bars work again

Part-of: <https://dev.funkwhale.audio/funkwhale/funkwhale/-/merge_requests/2346>
This commit is contained in:
Kasper Seweryn 2023-03-01 01:17:16 +01:00 committed by Marge
parent 0782b53e09
commit 9cc58d16c8
13 changed files with 105 additions and 66 deletions

View File

@ -38,7 +38,6 @@ module.exports = {
'@intlify/vue-i18n/valid-message-syntax': 'error', '@intlify/vue-i18n/valid-message-syntax': 'error',
'@intlify/vue-i18n/no-i18n-t-path-prop': 'error', '@intlify/vue-i18n/no-i18n-t-path-prop': 'error',
'@intlify/vue-i18n/no-missing-keys': 'error', '@intlify/vue-i18n/no-missing-keys': 'error',
'@intlify/vue-i18n/no-dynamic-keys': 'error',
'@intlify/vue-i18n/no-unused-keys': ['error', { '@intlify/vue-i18n/no-unused-keys': ['error', {
extensions: ['.ts', '.vue'], extensions: ['.ts', '.vue'],
enableFix: true enableFix: true

View File

@ -58,7 +58,7 @@
}, },
"devDependencies": { "devDependencies": {
"@intlify/eslint-plugin-vue-i18n": "2.0.0", "@intlify/eslint-plugin-vue-i18n": "2.0.0",
"@intlify/unplugin-vue-i18n": "^0.8.1", "@intlify/unplugin-vue-i18n": "^0.8.2",
"@types/diff": "5.0.2", "@types/diff": "5.0.2",
"@types/dompurify": "2.4.0", "@types/dompurify": "2.4.0",
"@types/howler": "2.2.7", "@types/howler": "2.2.7",
@ -72,7 +72,7 @@
"@typescript-eslint/eslint-plugin": "5.48.2", "@typescript-eslint/eslint-plugin": "5.48.2",
"@vitejs/plugin-vue": "4.0.0", "@vitejs/plugin-vue": "4.0.0",
"@vitest/coverage-c8": "0.25.8", "@vitest/coverage-c8": "0.25.8",
"@vue/compiler-sfc": "3.2.45", "@vue/compiler-sfc": "3.2.47",
"@vue/eslint-config-standard": "8.0.1", "@vue/eslint-config-standard": "8.0.1",
"@vue/eslint-config-typescript": "11.0.2", "@vue/eslint-config-typescript": "11.0.2",
"@vue/test-utils": "2.2.7", "@vue/test-utils": "2.2.7",

View File

@ -79,15 +79,15 @@ export class HTMLSound implements Sound {
}) })
useEventListener(this.#audio, 'waiting', () => { useEventListener(this.#audio, 'waiting', () => {
console.log('>> AUDIO WAITING', this.__track?.title) console.log('>> AUDIO WAITING', this)
}) })
useEventListener(this.#audio, 'playing', () => { useEventListener(this.#audio, 'playing', () => {
console.log('>> AUDIO PLAYING', this.__track?.title) console.log('>> AUDIO PLAYING', this)
}) })
useEventListener(this.#audio, 'stalled', () => { useEventListener(this.#audio, 'stalled', () => {
console.log('>> AUDIO STALLED', this.__track?.title) console.log('>> AUDIO STALLED', this)
}) })
useEventListener(this.#audio, 'loadeddata', () => { useEventListener(this.#audio, 'loadeddata', () => {
@ -96,7 +96,7 @@ export class HTMLSound implements Sound {
}) })
useEventListener(this.#audio, 'error', (err) => { useEventListener(this.#audio, 'error', (err) => {
console.error('>> AUDIO ERRORED', err, this.__track?.title) console.error('>> AUDIO ERRORED', err, this)
this.isErrored.value = true this.isErrored.value = true
this.isLoaded.value = true this.isLoaded.value = true
}) })
@ -104,7 +104,7 @@ export class HTMLSound implements Sound {
async preload () { async preload () {
this.isErrored.value = false this.isErrored.value = false
console.log('CALLING PRELOAD ON', this.__track?.title) console.log('CALLING PRELOAD ON', this)
this.#audio.load() this.#audio.load()
} }

View File

@ -2,7 +2,7 @@
import type { QueueItemSource } from '~/types' import type { QueueItemSource } from '~/types'
import { whenever, watchDebounced, useCurrentElement, useScrollLock, useFullscreen, useIdle, refAutoReset, useStorage } from '@vueuse/core' import { whenever, watchDebounced, useCurrentElement, useScrollLock, useFullscreen, useIdle, refAutoReset, useStorage } from '@vueuse/core'
import { nextTick, ref, computed, watchEffect, watch, defineAsyncComponent } from 'vue' import { nextTick, ref, computed, watchEffect, defineAsyncComponent } from 'vue'
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap' import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
@ -27,7 +27,6 @@ const {
isPlaying, isPlaying,
currentTime, currentTime,
duration, duration,
progress,
bufferProgress, bufferProgress,
seekTo, seekTo,
loading: isLoadingAudio, loading: isLoadingAudio,
@ -108,13 +107,6 @@ const touchProgress = (event: MouseEvent) => {
seekTo(time) seekTo(time)
} }
const animated = ref(false)
watch(currentTrack, async track => {
animated.value = false
await nextTick()
animated.value = true
})
const play = async (index: number) => { const play = async (index: number) => {
isPlaying.value = true isPlaying.value = true
return playTrack(index) return playTrack(index)
@ -368,7 +360,7 @@ if (!isWebGLSupported) {
:style="{ 'transform': `translateX(${bufferProgress - 100}%)` }" :style="{ 'transform': `translateX(${bufferProgress - 100}%)` }"
/> />
<div <div
:class="['position bar', { animated }]" class="position bar"
:style="{ :style="{
animationDuration: duration + 's', animationDuration: duration + 's',
animationPlayState: isPlaying animationPlayState: isPlaying
@ -416,23 +408,21 @@ if (!isWebGLSupported) {
<h2 class="ui header"> <h2 class="ui header">
<div class="content"> <div class="content">
<button <button
v-t="'components.Queue.button.close'"
class="ui right floated basic button" class="ui right floated basic button"
@click="$store.commit('ui/queueFocused', null)" @click="$store.commit('ui/queueFocused', null)"
> />
{{ $t('components.Queue.button.close') }}
</button>
<button <button
v-t="'components.Queue.button.clear'"
class="ui right floated basic button danger" class="ui right floated basic button danger"
@click="clear" @click="clear"
> />
{{ $t('components.Queue.button.clear') }}
</button>
{{ labels.queue }} {{ labels.queue }}
<div class="sub header"> <div class="sub header">
<div> <div>
{{ $t('components.Queue.meta.queuePosition', {index: currentIndex +1, length: queue.length}) }} <span v-t="{ path: 'components.Queue.meta.queuePosition', args: { index: currentIndex + 1, length: queue.length } }" />
<span class="middle pipe symbol" /> <span class="middle pipe symbol" />
{{ $t('components.Queue.meta.end') }} <span v-t="'components.Queue.meta.end'" />
<span :title="labels.duration"> <span :title="labels.duration">
{{ endsIn }} {{ endsIn }}
</span> </span>

View File

@ -27,7 +27,6 @@ const {
seekTo, seekTo,
currentTime, currentTime,
duration, duration,
progress,
bufferProgress, bufferProgress,
loading: isLoadingAudio loading: isLoadingAudio
} = usePlayer() } = usePlayer()
@ -131,10 +130,9 @@ const hideArtist = () => {
> >
<h1 <h1
id="player-label" id="player-label"
v-t="'components.audio.Player.header.player'"
class="visually-hidden" class="visually-hidden"
> />
{{ $t('components.audio.Player.header.player') }}
</h1>
<div <div
class="ui inverted segment fixed-controls" class="ui inverted segment fixed-controls"
@click.prevent.stop="toggleMobilePlayer" @click.prevent.stop="toggleMobilePlayer"
@ -305,18 +303,14 @@ const hideArtist = () => {
@click.stop="toggleMobilePlayer" @click.stop="toggleMobilePlayer"
> >
<i class="stream icon" /> <i class="stream icon" />
<span> <span v-t="{ path: 'components.audio.Player.meta.position', args: { index: currentIndex + 1, length: queue.length } }" />
{{ $t('components.audio.Player.meta.position', { index: currentIndex + 1, length: queue.length }) }}
</span>
</button> </button>
<button <button
class="position circular control button desktop-and-below" class="position circular control button desktop-and-below"
@click.stop="switchTab" @click.stop="switchTab"
> >
<i class="stream icon" /> <i class="stream icon" />
<span> <span v-t="{ path: 'components.audio.Player.meta.position', args: { index: currentIndex + 1, length: queue.length } }" />
{{ $t('components.audio.Player.meta.position', { index: currentIndex + 1, length: queue.length }) }}
</span>
</button> </button>
<button <button

View File

@ -167,7 +167,13 @@ export const usePlayer = createGlobalState(() => {
}, 1000) }, 1000)
// Progress // Progress
const progress = ref(0) useRafFn(() => {
const sound = currentSound.value
document.documentElement.style.setProperty('--fw-track-progress', sound
? `${(sound.currentTime / sound.duration * 100).toFixed(Math.log(window.innerWidth))}%`
: '0'
)
})
// Loading // Loading
const loading = computed(() => { const loading = computed(() => {
@ -207,7 +213,6 @@ export const usePlayer = createGlobalState(() => {
seekBy, seekBy,
seekTo, seekTo,
bufferProgress, bufferProgress,
progress,
loading, loading,
errored errored
} }

View File

@ -1,6 +1,6 @@
import type { Track, Upload } from '~/types' import type { Track, Upload } from '~/types'
import { createGlobalState, useNow, useStorage, useTimeAgo, whenever } from '@vueuse/core' import { createGlobalState, useStorage, useTimeAgo, whenever } from '@vueuse/core'
import { computed, ref, shallowReactive, watchEffect } from 'vue' import { computed, ref, shallowReactive, watchEffect } from 'vue'
import { shuffle as shuffleArray, sum } from 'lodash-es' import { shuffle as shuffleArray, sum } from 'lodash-es'
import { useClamp } from '@vueuse/math' import { useClamp } from '@vueuse/math'

View File

@ -127,7 +127,6 @@ export const useTracks = createGlobalState(() => {
// Preload next track // Preload next track
const { start: preload, stop: abortPreload } = useTimeoutFn(async (track: QueueTrack) => { const { start: preload, stop: abortPreload } = useTimeoutFn(async (track: QueueTrack) => {
const sound = await createSound(track) const sound = await createSound(track)
sound.__track = track
await sound.preload() await sound.preload()
}, 100, { immediate: false }) }, 100, { immediate: false })

View File

@ -48,7 +48,7 @@
.ui.progress .bar { .ui.progress .bar {
transition: none; transition: none;
width: 100%; width: 100%;
transform: translateX(-100%); transform: translateX(calc(var(--fw-track-progress) - 100%));
transform-origin: top left; transform-origin: top left;
will-change: transform; will-change: transform;
position: absolute; position: absolute;

View File

@ -160,7 +160,7 @@
.ui.progress .bar { .ui.progress .bar {
transition: none; transition: none;
width: 100%; width: 100%;
transform: translateX(-100%); transform: translateX(calc(var(--fw-track-progress) - 100%));
transform-origin: top left; transform-origin: top left;
will-change: transform; will-change: transform;
} }
@ -171,18 +171,6 @@
.ui.progress:not(.indeterminate) .ui.progress:not(.indeterminate)
.bar.position:not(.buffer) { .bar.position:not(.buffer) {
background: var(--vibrant-color); background: var(--vibrant-color);
will-change: transform;
&.animated {
@keyframes progress {
0% { transform: translate3d(-100%, 0, 0) }
100% { transform: translate3d(0, 0, 0) }
}
animation-name: progress;
animation-timing-function: linear;
}
} }
.indicating.progress .bar { .indicating.progress .bar {

View File

@ -2,6 +2,11 @@
html { html {
scroll-behavior: smooth; scroll-behavior: smooth;
} }
:root {
--fw-track-progress: 0;
}
@media screen and (prefers-reduced-motion: reduce) { @media screen and (prefers-reduced-motion: reduce) {
html { html {
scroll-behavior: auto; scroll-behavior: auto;

View File

@ -1,10 +1,11 @@
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import VueI18n from '@intlify/unplugin-vue-i18n/vite'
import { VitePWA } from 'vite-plugin-pwa' import { VitePWA } from 'vite-plugin-pwa'
import { resolve } from 'path' import { resolve } from 'path'
import { defineConfig } from 'vite'
import VueI18n from '@intlify/unplugin-vue-i18n/vite'
import manifest from './pwa-manifest.json' import manifest from './pwa-manifest.json'
import Vue from '@vitejs/plugin-vue'
const port = +(process.env.VUE_PORT ?? 8080) const port = +(process.env.VUE_PORT ?? 8080)

View File

@ -1207,10 +1207,10 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@intlify/bundle-utils@^3.4.0": "@intlify/bundle-utils@^4.0.0":
version "3.4.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-3.4.0.tgz#72558611f4b223a6791f591363dc48a4bcacdf70" resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-4.0.0.tgz#29c1d602c7e4e33b516581496a7c6740ed7e2585"
integrity sha512-2UQkqiSAOSPEHMGWlybqWm4G2K0X+FyYho5AwXz6QklSX1EY5EDmOSxZmwscn2qmKBnp6OYsme5kUrnN9xrWzQ== integrity sha512-klXrYT9VXyKEXsD6UY3pShg0O5MPC07n0TZ5RrSs5ry6T1eZVolIFGJi9c3qcDrh1qjJxgikRnPBmD7qGDqbjw==
dependencies: dependencies:
"@intlify/message-compiler" next "@intlify/message-compiler" next
"@intlify/shared" next "@intlify/shared" next
@ -1299,12 +1299,12 @@
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.3.0-beta.16.tgz#74f254dbb7eac633b86d690a341349db29573896" resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.3.0-beta.16.tgz#74f254dbb7eac633b86d690a341349db29573896"
integrity sha512-kXbm4svALe3lX+EjdJxfnabOphqS4yQ1Ge/iIlR8tvUiYRCoNz3hig1M4336iY++Dfx5ytEQJPNjIcknNIuvig== integrity sha512-kXbm4svALe3lX+EjdJxfnabOphqS4yQ1Ge/iIlR8tvUiYRCoNz3hig1M4336iY++Dfx5ytEQJPNjIcknNIuvig==
"@intlify/unplugin-vue-i18n@^0.8.1": "@intlify/unplugin-vue-i18n@^0.8.2":
version "0.8.1" version "0.8.2"
resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-0.8.1.tgz#e472981da044d85b910521ee0f9e15382cba9a09" resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-0.8.2.tgz#4196cb5bee4243bb3a33af76ce9663f3e106809a"
integrity sha512-BhigujPmP6JL1FSxmpogCaL+REozncHCVkJuUnefz4GWBu3X+pRe5O7PeJn8/g+Iml2ieQJz4ISPMmEbuGQjqQ== integrity sha512-cRnzPqSEZQOmTD+p4pwc3RTS9HxreLqfID0keoqZDZweCy/CGRMLLTNd15S4TUf1vSBhPF03DItEFDr1F+8MDA==
dependencies: dependencies:
"@intlify/bundle-utils" "^3.4.0" "@intlify/bundle-utils" "^4.0.0"
"@intlify/shared" next "@intlify/shared" next
"@rollup/pluginutils" "^4.2.0" "@rollup/pluginutils" "^4.2.0"
"@vue/compiler-sfc" "^3.2.45" "@vue/compiler-sfc" "^3.2.45"
@ -2071,6 +2071,16 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
source-map "^0.6.1" source-map "^0.6.1"
"@vue/compiler-core@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.47.tgz#3e07c684d74897ac9aa5922c520741f3029267f8"
integrity sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/shared" "3.2.47"
estree-walker "^2.0.2"
source-map "^0.6.1"
"@vue/compiler-dom@3.2.45", "@vue/compiler-dom@^3.2.31", "@vue/compiler-dom@^3.2.45": "@vue/compiler-dom@3.2.45", "@vue/compiler-dom@^3.2.31", "@vue/compiler-dom@^3.2.45":
version "3.2.45" version "3.2.45"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz#c43cc15e50da62ecc16a42f2622d25dc5fd97dce" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz#c43cc15e50da62ecc16a42f2622d25dc5fd97dce"
@ -2079,6 +2089,14 @@
"@vue/compiler-core" "3.2.45" "@vue/compiler-core" "3.2.45"
"@vue/shared" "3.2.45" "@vue/shared" "3.2.45"
"@vue/compiler-dom@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz#a0b06caf7ef7056939e563dcaa9cbde30794f305"
integrity sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==
dependencies:
"@vue/compiler-core" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/compiler-sfc@3.2.45", "@vue/compiler-sfc@^3.2.45": "@vue/compiler-sfc@3.2.45", "@vue/compiler-sfc@^3.2.45":
version "3.2.45" version "3.2.45"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz#7f7989cc04ec9e7c55acd406827a2c4e96872c70" resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz#7f7989cc04ec9e7c55acd406827a2c4e96872c70"
@ -2095,6 +2113,22 @@
postcss "^8.1.10" postcss "^8.1.10"
source-map "^0.6.1" source-map "^0.6.1"
"@vue/compiler-sfc@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz#1bdc36f6cdc1643f72e2c397eb1a398f5004ad3d"
integrity sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.47"
"@vue/compiler-dom" "3.2.47"
"@vue/compiler-ssr" "3.2.47"
"@vue/reactivity-transform" "3.2.47"
"@vue/shared" "3.2.47"
estree-walker "^2.0.2"
magic-string "^0.25.7"
postcss "^8.1.10"
source-map "^0.6.1"
"@vue/compiler-ssr@3.2.45": "@vue/compiler-ssr@3.2.45":
version "3.2.45" version "3.2.45"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz#bd20604b6e64ea15344d5b6278c4141191c983b2" resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz#bd20604b6e64ea15344d5b6278c4141191c983b2"
@ -2103,6 +2137,14 @@
"@vue/compiler-dom" "3.2.45" "@vue/compiler-dom" "3.2.45"
"@vue/shared" "3.2.45" "@vue/shared" "3.2.45"
"@vue/compiler-ssr@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz#35872c01a273aac4d6070ab9d8da918ab13057ee"
integrity sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==
dependencies:
"@vue/compiler-dom" "3.2.47"
"@vue/shared" "3.2.47"
"@vue/devtools-api@^6.0.0-beta.11", "@vue/devtools-api@^6.2.1", "@vue/devtools-api@^6.4.5": "@vue/devtools-api@^6.0.0-beta.11", "@vue/devtools-api@^6.2.1", "@vue/devtools-api@^6.4.5":
version "6.5.0" version "6.5.0"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07" resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07"
@ -2140,6 +2182,17 @@
estree-walker "^2.0.2" estree-walker "^2.0.2"
magic-string "^0.25.7" magic-string "^0.25.7"
"@vue/reactivity-transform@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz#e45df4d06370f8abf29081a16afd25cffba6d84e"
integrity sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.47"
"@vue/shared" "3.2.47"
estree-walker "^2.0.2"
magic-string "^0.25.7"
"@vue/reactivity@3.2.45", "@vue/reactivity@^3.2.45": "@vue/reactivity@3.2.45", "@vue/reactivity@^3.2.45":
version "3.2.45" version "3.2.45"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.45.tgz#412a45b574de601be5a4a5d9a8cbd4dee4662ff0" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.45.tgz#412a45b574de601be5a4a5d9a8cbd4dee4662ff0"
@ -2177,6 +2230,11 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.45.tgz#a3fffa7489eafff38d984e23d0236e230c818bc2" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.45.tgz#a3fffa7489eafff38d984e23d0236e230c818bc2"
integrity sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg== integrity sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==
"@vue/shared@3.2.47":
version "3.2.47"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.47.tgz#e597ef75086c6e896ff5478a6bfc0a7aa4bbd14c"
integrity sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==
"@vue/test-utils@2.2.7": "@vue/test-utils@2.2.7":
version "2.2.7" version "2.2.7"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.2.7.tgz#0d93d635031a4cca2de70b825aef3fe20a41e702" resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.2.7.tgz#0d93d635031a4cca2de70b825aef3fe20a41e702"