diff --git a/.gitignore b/.gitignore index e8f682b..91199b9 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,5 @@ sw.* # Vim swap files *.swp + +/.venv/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3a77f1d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "editor.tabSize": 2, + "[vue]": { + "editor.defaultFormatter": "octref.vetur", + "editor.formatOnSave": true + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true + } +} diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..d02f57c --- /dev/null +++ b/Pipfile @@ -0,0 +1,12 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +gtts = ">=2.2.3" + +[dev-packages] + +[requires] +python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..ff42739 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,84 @@ +{ + "_meta": { + "hash": { + "sha256": "ff889e495c55c0c92b329fe9caf8fad69d314e15db1c62b97c7abbc709a211ab" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872", + "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569" + ], + "version": "==2021.10.8" + }, + "charset-normalizer": { + "hashes": [ + "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0", + "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b" + ], + "markers": "python_version >= '3'", + "version": "==2.0.7" + }, + "click": { + "hashes": [ + "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", + "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" + ], + "markers": "python_version >= '3.6'", + "version": "==8.0.3" + }, + "gtts": { + "hashes": [ + "sha256:8376d9bde85ac8cda95ea92dcd12bf31a3efe9e810edcb04437a4151cbec30d6", + "sha256:88cfaa112471867009a0450a696e5bc69cf1671fa0fa683fe17d0650023c863c" + ], + "index": "pypi", + "version": "==2.2.3" + }, + "idna": { + "hashes": [ + "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", + "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" + ], + "markers": "python_version >= '3'", + "version": "==3.3" + }, + "requests": { + "hashes": [ + "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", + "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==2.26.0" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "urllib3": { + "hashes": [ + "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", + "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.26.7" + } + }, + "develop": {} +} diff --git a/assets/api.ts b/assets/api.ts new file mode 100644 index 0000000..d54cd1a --- /dev/null +++ b/assets/api.ts @@ -0,0 +1,114 @@ +import _axios, { AxiosInstance } from 'axios' + +class Api { + axios: AxiosInstance + _isAuthorized = true + _user = '' + + constructor() { + this.axios = _axios.create() + } + + async setWanikaniApiKey(apiKey?: string) { + const setApiKey = () => { + if (apiKey) { + this.axios.defaults.headers.common.Authorization = `Bearer ${apiKey}` + } else { + delete this.axios.defaults.headers.common.Authorization + } + } + + setApiKey() + const r = await this.user_getSettings() + + if (r) return r + apiKey = '' + setApiKey() + + return null + } + + async isAuthorized(): Promise { + if (this._isAuthorized && !this._user) { + const r = await this.user_getSettings() + if (r) { + this._user = r.identifier + this._isAuthorized = true + } + } + + return this._isAuthorized + } + + async user_getSettings() { + const r = await this.axios + .get('/api/user', { + params: { + select: 'level,levelMin,identifier', + }, + }) + .then( + (r) => + r.data as { + level: number + levelMin?: number + identifier: string + } + ) + .then((r) => ({ + ...r, + levelMin: r.levelMin || 1, + })) + .catch(() => null) + + if (!r) { + return + } + + return r + } + + async user_updateSettings(u: { levelMin: number }) { + return await this.axios.patch('/api/user', u) + } + + async quiz_getMany(params: { + entries: string[] + select: string[] + type?: string + direction?: string + }) { + return await this.axios.post('/api/quiz/getMany', params).then( + (r) => + r.data as { + result: { + id: string + entry: string + }[] + } + ) + } + + async quiz_create(params: { entries: string[]; type: string }) { + return await this.axios.put('/api/quiz', params).then( + (r) => + r.data as { + result: { + ids: string[] + }[] + } + ) + } + + async quiz_deleteMany(params: { entries: string[]; type: string }) { + return await this.axios.post('/api/quiz/deleteMany', params) + } + + async extra_delete(params: { id: string }) { + return await this.axios.delete('/extra/delete', { + params, + }) + } +} + +export const api = new Api() diff --git a/assets/buefy.png b/assets/buefy.png deleted file mode 100644 index 0526173..0000000 Binary files a/assets/buefy.png and /dev/null differ diff --git a/assets/speak.ts b/assets/speak.ts new file mode 100644 index 0000000..df6b9ca --- /dev/null +++ b/assets/speak.ts @@ -0,0 +1,82 @@ +const allVoices: Record = {} + +// eslint-disable-next-line array-callback-return +speechSynthesis.getVoices().map((v) => { + allVoices[v.lang] = v.lang +}) + +speechSynthesis.onvoiceschanged = () => { + // eslint-disable-next-line array-callback-return + speechSynthesis.getVoices().map((v) => { + allVoices[v.lang] = v.lang + }) +} + +export async function speak( + s: string, + opts: { + lang?: string + forceOffline?: boolean + } = {} +) { + if (!opts.forceOffline) { + const audio = new Audio( + `/api/tts?q=${encodeURIComponent(s)}&lang=${encodeURIComponent( + opts.lang || 'ja' + )}` + ) + await audio.play().catch(() => + speak(s, { + ...opts, + forceOffline: true, + }) + ) + return + } + + let lang = 'zh' + + const voices = Object.keys(allVoices) + const stage1 = () => voices.filter((v) => v === lang)[0] + const stage2 = () => { + const m1 = lang.substr(0, 2) + const m2 = lang.substr(3, 2) + const r1 = new RegExp(`^${m1}[-_]${m2}`, 'i') + return voices.filter((v) => r1.test(v))[0] + } + const stage3 = () => { + const m1 = lang.substr(0, 2).toLocaleLowerCase() + return voices.filter((v) => v.toLocaleLowerCase().startsWith(m1))[0] + } + + lang = stage1() || stage2() || stage3() || '' + + if (lang) { + const utterance = new SpeechSynthesisUtterance(s) + utterance.lang = lang + speechSynthesis.speak(utterance) + + return new Promise((resolve) => { + utterance.onend = () => { + resolve() + } + }) + } +} + +window.addEventListener('keydown', (ev) => { + if ( + ev.target instanceof HTMLElement && + ['INPUT', 'TEXTAREA'].includes(ev.target.tagName.toLocaleUpperCase()) + ) { + return + } + + if (ev.key === 's') { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const s = window.getSelection()!.toString() + if (s) { + speak(s) + } + } +}) diff --git a/assets/util.ts b/assets/util.ts new file mode 100644 index 0000000..bc6797a --- /dev/null +++ b/assets/util.ts @@ -0,0 +1,76 @@ +export const doClick = async (el?: Element | null, timeout?: number) => { + if (!el) return + + if (el.classList?.add) { + el.classList.add('active') + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { click } = el as any + if (typeof click === 'function') { + const r = click() + if (r instanceof Promise) { + await r + } else if (timeout) { + await new Promise((resolve) => setTimeout(resolve, timeout)) + } else { + return + } + } + + if (el.classList?.remove) { + el.classList.remove('active') + } +} + +export const doMapKeypress = ( + evt: KeyboardEvent, + map: Record void) | string | HTMLElement> +) => { + let action = map[evt.key] + while (typeof action === 'string') { + action = map[action] + } + if (typeof action === 'function') { + action() + } else if (action instanceof HTMLElement) { + doClick(action) + } +} + +/** + * Shuffles array in place. ES6 version + * @param {Array} a items An array containing the items. + */ +export function shuffle(a: T[]) { + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)) + ;[a[i], a[j]] = [a[j], a[i]] + } + return a +} + +export function sample(arr: T[], size: number): T[] { + const allN = Array(arr.length) + .fill(null) + .map((_, i) => i) + const outN: number[] = [] + + while (allN.length && outN.length < size) { + outN.push(...allN.splice(Math.floor(Math.random() * allN.length), 1)) + } + + return outN.map((n) => arr[n]) +} + +export function capitalize(s: string): string { + if (s.includes(' ')) { + return s + .split(' ') + .map((s0) => capitalize(s0)) + .join(' ') + } + + if (!s) return s + return s[0].toLocaleUpperCase() + s.substr(1) +} diff --git a/components/Card.vue b/components/Card.vue deleted file mode 100644 index 9c379b1..0000000 --- a/components/Card.vue +++ /dev/null @@ -1,42 +0,0 @@ - - - diff --git a/components/ContextMenu.vue b/components/ContextMenu.vue new file mode 100644 index 0000000..e4a08f6 --- /dev/null +++ b/components/ContextMenu.vue @@ -0,0 +1,282 @@ + + + + + diff --git a/components/NuxtLogo.vue b/components/NuxtLogo.vue deleted file mode 100644 index 970eba0..0000000 --- a/components/NuxtLogo.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/Tutorial.vue b/components/Tutorial.vue deleted file mode 100644 index 6440b8f..0000000 --- a/components/Tutorial.vue +++ /dev/null @@ -1,46 +0,0 @@ - - diff --git a/package.json b/package.json index f39bf82..52e0f50 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "jaquiz", + "name": "@jaquiz/www", "version": "1.0.0", "private": true, "scripts": { @@ -10,14 +10,20 @@ }, "dependencies": { "@nuxtjs/pwa": "^3.3.5", + "axios": "^0.23.0", "core-js": "^3.15.1", "nuxt": "^2.15.7", - "nuxt-buefy": "^0.4.8" + "nuxt-buefy": "^0.4.8", + "nuxt-property-decorator": "^2.9.1", + "nuxt-typed-vuex": "^0.2.0", + "vue-context": "^6.0.0" }, "devDependencies": { "@nuxt/types": "^2.15.7", "@nuxt/typescript-build": "^2.1.0", "eslint-config-prettier": "^8.3.0", - "prettier": "^2.3.2" + "prettier": "^2.3.2", + "sass": "^1.43.3", + "sass-loader": "~10.1.1" } -} \ No newline at end of file +} diff --git a/store/README.md b/store/README.md deleted file mode 100644 index 1972d27..0000000 --- a/store/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# STORE - -**This directory is not required, you can delete it if you don't want to use it.** - -This directory contains your Vuex Store files. -Vuex Store option is implemented in the Nuxt.js framework. - -Creating a file in this directory automatically activates the option in the framework. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). diff --git a/store/index.ts b/store/index.ts new file mode 100644 index 0000000..922499f --- /dev/null +++ b/store/index.ts @@ -0,0 +1,129 @@ +import { actionTree, getAccessorType, mutationTree } from 'typed-vuex' +import { api } from '~/assets/api' + +interface ISettings { + level: number | null + levelMin: number | null + sentenceMin: number | null + sentenceMax: number | null +} + +export const state = () => ({ + settings: { + level: null, + levelMin: null, + sentenceMin: null, + sentenceMax: null, + } as ISettings, + isApp: false, + tabs: [] as { + title: string + component: string + permanent?: boolean + query: Record + }[], + activeTab: 0, + identifier: '', +}) + +export const mutations = mutationTree(state, { + SET_SETTINGS(state, settings: ISettings) { + state.settings = settings + }, + SET_IS_APP(state, isApp: boolean) { + state.isApp = isApp + }, + SET_TAB_TITLE(state, { i, title }: { i: number; title: string }) { + state.tabs[i].title = title + state.tabs = JSON.parse(JSON.stringify(state.tabs)) + }, + ADD_TAB( + state, + { + component, + query = {}, + first, + }: { + component: string + query?: Record + first?: boolean + } + ) { + if (first) { + state.tabs = [ + { + title: component, + component, + permanent: true, + query, + }, + ] + } else { + state.tabs = [ + ...state.tabs, + { + title: component, + component, + query, + }, + ] + } + + state.activeTab = state.tabs.length - 1 + }, + DELETE_TAB(state, { i }: { i: number }) { + state.tabs.splice(i, 1) + + if (state.activeTab >= state.tabs.length) { + state.activeTab = state.tabs.length - 1 + } + + state.tabs = JSON.parse(JSON.stringify(state.tabs)) + }, + SET_TAB_COMPONENT(state, { i, component }: { i: number; component: string }) { + state.tabs[i].component = component + + state.tabs = JSON.parse(JSON.stringify(state.tabs)) + state.activeTab = i + }, + SET_TAB_CURRENT(state, { i }: { i: number }) { + state.activeTab = i + }, + SET_IDENTIFIER(state, name: string) { + state.identifier = name + }, +}) + +export const actions = actionTree( + { state, mutations }, + { + async updateSettings({ commit }) { + const r = await api.user_getSettings() + commit('SET_IS_APP', !!r) + commit('SET_IDENTIFIER', r?.identifier || '') + + if (r) { + if (!r.levelMin) { + r.levelMin = 1 + + await api.user_updateSettings({ + levelMin: r.levelMin, + }) + } + } + + commit('SET_SETTINGS', { + level: r?.level || null, + levelMin: r?.levelMin || null, + sentenceMin: null, + sentenceMax: null, + }) + }, + } +) + +export const accessorType = getAccessorType({ + state, + mutations, + actions, +}) diff --git a/tsconfig.json b/tsconfig.json index 4b17280..86bb182 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,7 +25,8 @@ }, "types": [ "@nuxt/types", - "@types/node" + "@types/node", + "buefy/types" ] }, "exclude": [ diff --git a/vue-inject.d.ts b/vue-inject.d.ts new file mode 100644 index 0000000..504f8e0 --- /dev/null +++ b/vue-inject.d.ts @@ -0,0 +1,8 @@ +import { accessorType } from '~/store' + +declare module 'vue/types/vue' { + interface Vue { + $accessor: typeof accessorType + $axios: never + } +} diff --git a/yarn.lock b/yarn.lock index ce489a1..afd0946 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2116,6 +2116,13 @@ autoprefixer@^9.6.1: postcss "^7.0.32" postcss-value-parser "^4.1.0" +axios@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.23.0.tgz#b0fa5d0948a8d1d75e3d5635238b6c4625b05149" + integrity sha512-NmvAE4i0YAv5cKq8zlDoPd1VLKAqX5oLuZKs8xkJa4qi6RGn0uhCYFjWtHHC9EM/MwOwYWOs53W+V0aqEXq1sg== + dependencies: + follow-redirects "^1.14.4" + babel-loader@^8.2.2: version "8.2.3" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" @@ -2576,6 +2583,21 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1, chokidar@^3.4.2, chokidar@^3.5.1, chokidar@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -2595,21 +2617,6 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.4.1, chokidar@^3.4.2, chokidar@^3.5.1, chokidar@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -3825,6 +3832,11 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +follow-redirects@^1.14.4: + version "1.14.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" + integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -4750,7 +4762,7 @@ jiti@^1.9.2: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.12.9.tgz#2ce45b265cfc8dc91ebd70a5204807cf915291bc" integrity sha512-TdcJywkQtcwLxogc4rSMAi479G2eDPzfW0fLySks7TPhgZZ4s/tM6stnzayIh3gS/db3zExWJyUx4cNWrwAmoQ== -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -4963,6 +4975,13 @@ lodash@^4.15.0, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17. resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +loose-envify@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + lower-case@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" @@ -5490,6 +5509,24 @@ nuxt-buefy@^0.4.8: dependencies: buefy "^0.9.10" +nuxt-property-decorator@^2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/nuxt-property-decorator/-/nuxt-property-decorator-2.9.1.tgz#60fd87b64d85519b091374c7ea4c0ce1979f6afa" + integrity sha512-dE2GrrGKZMhv0dHAr+Lmj+JOQfjIouINgF58QNRDFNOZXMJrXxKO5zGqvCRwmx3hxqqwht7TXHdz9w1AnvL2IA== + dependencies: + vue-class-component "^7.2.6" + vue-property-decorator "^9.0.0" + vuex-class "^0.3.2" + vuex-module-decorators "^1.0.1" + +nuxt-typed-vuex@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nuxt-typed-vuex/-/nuxt-typed-vuex-0.2.0.tgz#989ff05bf30aa10bc82456f03cf5645bc9621e3a" + integrity sha512-c+6LRuwv4aox0aarAccd5zdd3tWOBDxbc32VRLeVpAt//ZG8Lcutt4Xc067iyRaquOashkJvQ8//Ko18EaySTw== + dependencies: + typed-vuex "0.2.0" + upath "^2.0.1" + nuxt@^2.15.7: version "2.15.8" resolved "https://registry.yarnpkg.com/nuxt/-/nuxt-2.15.8.tgz#946cba46bdaaf0e3918aa27fd9ea0fed8ed303b0" @@ -6999,7 +7036,7 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-loader@10.1.1: +sass-loader@10.1.1, sass-loader@~10.1.1: version "10.1.1" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d" integrity sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw== @@ -7010,6 +7047,13 @@ sass-loader@10.1.1: schema-utils "^3.0.0" semver "^7.3.2" +sass@^1.43.3: + version "1.43.3" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.3.tgz#aa16a69131b84f0cd23189a242571e8905f1ce43" + integrity sha512-BJnLngqWpMeS65UvlYYEuCb3/fLxDxhHtOB/gWPxs6NKrslTxGt3ZxwIvOe/0Jm4tWwM/+tIpE3wj4dLEhPDeQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -7768,6 +7812,11 @@ type-fest@^0.8.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +typed-vuex@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/typed-vuex/-/typed-vuex-0.2.0.tgz#1c1b60150926a764c0a5a2d5eb6620ef8e9096aa" + integrity sha512-0JZJBcQiXJq4TnQh0W3sdZzTnwUiHO0AygttInsXO8BZ4eRTbNRmldKDIzsgA7WRlVi+pJ8oAKDlDDpkx0Ilpg== + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -8004,11 +8053,30 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +vue-class-component@^7.2.6: + version "7.2.6" + resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-7.2.6.tgz#8471e037b8e4762f5a464686e19e5afc708502e4" + integrity sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w== + +vue-clickaway@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/vue-clickaway/-/vue-clickaway-2.2.2.tgz#cecf6839575e8b2afc5d3edb3efb616d293dbb44" + integrity sha512-25SpjXKetL06GLYoLoC8pqAV6Cur9cQ//2g35GRFBV4FgoljbZZjTINR8g2NuVXXDMLSUXaKx5dutgO4PaDE7A== + dependencies: + loose-envify "^1.2.0" + vue-client-only@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/vue-client-only/-/vue-client-only-2.1.0.tgz#1a67a47b8ecacfa86d75830173fffee3bf8a4ee3" integrity sha512-vKl1skEKn8EK9f8P2ZzhRnuaRHLHrlt1sbRmazlvsx6EiC3A8oWF8YCBrMJzoN+W3OnElwIGbVjsx6/xelY1AA== +vue-context@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/vue-context/-/vue-context-6.0.0.tgz#97a1a423fc86fb7a54f46d1567670aba281a5970" + integrity sha512-x8gO6xgj0MtTCWcYbDjO/7VJ/gT+nV+unqICvsN0hwqqBzft31eYyExYqrSvfDCRI7ixxObu5tbl7BQAKge7eg== + dependencies: + vue-clickaway "^2.2.2" + vue-hot-reload-api@^2.3.0: version "2.3.4" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" @@ -8037,6 +8105,11 @@ vue-no-ssr@^1.1.1: resolved "https://registry.yarnpkg.com/vue-no-ssr/-/vue-no-ssr-1.1.1.tgz#875f3be6fb0ae41568a837f3ac1a80eaa137b998" integrity sha512-ZMjqRpWabMPqPc7gIrG0Nw6vRf1+itwf0Itft7LbMXs2g3Zs/NFmevjZGN1x7K3Q95GmIjWbQZTVerxiBxI+0g== +vue-property-decorator@^9.0.0: + version "9.1.2" + resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-9.1.2.tgz#266a2eac61ba6527e2e68a6933cfb98fddab5457" + integrity sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ== + vue-router@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.2.tgz#5f55e3f251970e36c3e8d88a7cd2d67a350ade5c" @@ -8082,6 +8155,16 @@ vue@^2.6.12: resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235" integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ== +vuex-class@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/vuex-class/-/vuex-class-0.3.2.tgz#c7e96a076c1682137d4d23a8dcfdc63f220e17a8" + integrity sha512-m0w7/FMsNcwJgunJeM+wcNaHzK2KX1K1rw2WUQf7Q16ndXHo7pflRyOV/E8795JO/7fstyjH3EgqBI4h4n4qXQ== + +vuex-module-decorators@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vuex-module-decorators/-/vuex-module-decorators-1.0.1.tgz#d34dafb5428a3636f1c26d3d014c15fc9659ccd0" + integrity sha512-FLWZsXV5XAtl/bcKUyQFpnSBtpc3wK/7zSdy9oKbyp71mZd4ut5y2zSd219wWW9OG7WUOlVwac4rXFFDVnq7ug== + vuex@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71"