Master export error chapters (#515)

Co-authored-by: peakhgrhill <peakhgrhill@porton.me>
Co-authored-by: eriksyang <eriksyang@gmail.com>
Co-authored-by: 邓亮 <787615673@qq.com>
Co-authored-by: Anthony H <anthonyhuanggr@gmail.com>
Co-authored-by: jasonw <111627243+Jasonw372@users.noreply.github.com>
Co-authored-by: EriksYang <35193151+EriksYang@users.noreply.github.com>
Co-authored-by: KaiyiWing <Zhang.kaiyi42@gmail.com>
This commit is contained in:
hemoo
2023-08-05 18:11:23 +08:00
committed by GitHub
parent a002c573d6
commit 08e0d27952
9 changed files with 265 additions and 143 deletions

View File

@@ -37,7 +37,8 @@
"swr": "^2.0.4",
"typescript": "^4.0.3",
"use-immer": "^0.9.0",
"use-sound": "^4.0.1"
"use-sound": "^4.0.1",
"xlsx": "^0.18.5"
},
"scripts": {
"dev": "vite",

View File

@@ -7,12 +7,12 @@ import styles from './index.module.css'
import Tooltip from '@/components/Tooltip'
import { currentChapterAtom, currentDictInfoAtom, infoPanelStateAtom, randomConfigAtom, wordDictationConfigAtom } from '@/store'
import type { InfoPanelType } from '@/typings'
import type { WordWithIndex } from '@/typings'
import { recordOpenInfoPanelAction } from '@/utils'
import { Transition } from '@headlessui/react'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { useCallback, useContext, useEffect, useMemo } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import IexportWords from '~icons/icon-park-outline/excel'
import IconCoffee from '~icons/mdi/coffee'
import IconXiaoHongShu from '~icons/my-icons/xiaohongshu'
import IconGithub from '~icons/simple-icons/github'
@@ -34,11 +34,39 @@ const ResultScreen = () => {
dispatch({ type: TypingStateActionType.TICK_TIMER, addTime: 0 })
}, [dispatch])
const wrongWords = useMemo(() => {
const wordList = state.chapterData.wrongWordIndexes.map((index) => state.chapterData.words.find((word) => word.index === index))
const exportWords = useCallback(() => {
const { words, userInputLogs } = state.chapterData
const exportData = userInputLogs.map((log) => {
const word = words[log.index]
const wordName = word.name
return {
...word,
correctCount: log.correctCount,
wrongCount: log.wrongCount,
wrongLetters: Object.entries(log.LetterMistakes)
.map(([key, mistakes]) => `${wordName[Number(key)]}:${mistakes.length}`)
.join(';'),
}
})
return wordList.filter((word) => word !== undefined) as WordWithIndex[]
}, [state.chapterData.wrongWordIndexes, state.chapterData.words])
import('xlsx')
.then(({ utils, writeFileXLSX }) => {
const ws = utils.json_to_sheet(exportData)
const wb = utils.book_new()
utils.book_append_sheet(wb, ws, 'Data')
writeFileXLSX(wb, `${currentDictInfo.name}${currentChapter + 1}章.xlsx`)
})
.catch(() => {
console.log('写入 xlsx 模块导入失败')
})
}, [currentChapter, currentDictInfo.name, state.chapterData])
const wrongWords = useMemo(() => {
return state.chapterData.userInputLogs
.filter((log) => log.wrongCount > 0)
.map((log) => state.chapterData.words[log.index])
.filter((word) => word !== undefined)
}, [state.chapterData.userInputLogs, state.chapterData.words])
const isLastChapter = useMemo(() => {
return currentChapter >= currentDictInfo.chapterCount - 1
@@ -46,9 +74,9 @@ const ResultScreen = () => {
const correctRate = useMemo(() => {
const chapterLength = state.chapterData.words.length
const correctCount = chapterLength - state.chapterData.wrongWordIndexes.length
const correctCount = chapterLength - wrongWords.length
return Math.floor((correctCount / chapterLength) * 100)
}, [state.chapterData.words.length, state.chapterData.wrongWordIndexes])
}, [state.chapterData.words.length, wrongWords.length])
const mistakeLevel = useMemo(() => {
if (correctRate >= 85) {
@@ -172,12 +200,12 @@ const ResultScreen = () => {
))}
</div>
<div className="align-center flex w-full flex-row justify-start rounded-b-xl bg-indigo-200 px-4 dark:bg-indigo-400">
<ConclusionBar mistakeLevel={mistakeLevel} mistakeCount={state.chapterData.wrongWordIndexes.length} />
<ConclusionBar mistakeLevel={mistakeLevel} mistakeCount={wrongWords.length} />
</div>
</div>
<div className="ml-2 flex flex-col items-center justify-end gap-3.5 text-xl">
<ShareButton />
<IexportWords fontSize={18} className="cursor-pointer text-gray-500" onClick={exportWords}></IexportWords>
<IconXiaoHongShu
fontSize={15}
className="cursor-pointer text-gray-500 hover:text-red-500 focus:outline-none"

View File

@@ -2,9 +2,10 @@ import type { WordUpdateAction } from '../InputHandler'
import InputHandler from '../InputHandler'
import WordSound from '../WordSound'
import Letter from './Letter'
import type { LetterState } from './Letter'
import Notation from './Notation'
import style from './index.module.css'
import { initialWordState } from './type'
import type { WordState } from './type'
import { EXPLICIT_SPACE } from '@/constants'
import useKeySounds from '@/hooks/useKeySounds'
import { TypingContext, TypingStateActionType } from '@/pages/Typing/store'
@@ -19,49 +20,10 @@ import {
import type { Word } from '@/typings'
import { getUtcStringForMixpanel, useMixPanelWordLogUploader } from '@/utils'
import { useSaveWordRecord } from '@/utils/db'
import type { LetterMistakes } from '@/utils/db/record'
import { useAtomValue } from 'jotai'
import { useCallback, useContext, useEffect, useState } from 'react'
import { useImmer } from 'use-immer'
type WordState = {
displayWord: string
inputWord: string
letterStates: LetterState[]
isFinished: boolean
// 是否出现输入错误
hasWrong: boolean
// 记录是否已经出现过输入错误
hasMadeInputWrong: boolean
// 用户输入错误的次数
wrongCount: number
startTime: string
endTime: string
inputCount: number
correctCount: number
letterTimeArray: number[]
letterMistake: LetterMistakes
// 用于随机隐藏字母功能
randomLetterVisible: boolean[]
}
const initialWordState: WordState = {
displayWord: '',
inputWord: '',
letterStates: [],
isFinished: false,
hasWrong: false,
hasMadeInputWrong: false,
wrongCount: 0,
startTime: '',
endTime: '',
inputCount: 0,
correctCount: 0,
letterTimeArray: [],
letterMistake: {},
randomLetterVisible: [],
}
const vowelLetters = ['A', 'E', 'I', 'O', 'U']
export default function WordComponent({ word, onFinish }: { word: Word; onFinish: () => void }) {
@@ -161,7 +123,6 @@ export default function WordComponent({ word, onFinish }: { word: Word; onFinish
const inputChar = wordState.inputWord[inputLength - 1]
const correctChar = wordState.displayWord[inputLength - 1]
let isEqual = false
if (inputChar != undefined && correctChar != undefined) {
isEqual = isIgnoreCase ? inputChar.toLowerCase() === correctChar.toLowerCase() : inputChar === correctChar
@@ -189,26 +150,26 @@ export default function WordComponent({ word, onFinish }: { word: Word; onFinish
playKeySound()
}
dispatch({ type: TypingStateActionType.INCREASE_CORRECT_COUNT })
dispatch({ type: TypingStateActionType.REPORT_CORRECT_WORD })
} else {
// 出错时
playBeepSound()
setWordState((state) => {
state.letterStates[inputLength - 1] = 'wrong'
state.hasWrong = true
state.hasMadeInputWrong = true
state.wrongCount += 1
state.letterTimeArray = []
if (state.letterMistake[inputLength - 1]) {
state.letterMistake[inputLength - 1].push(inputChar)
} else {
state.letterMistake[inputLength - 1] = [inputChar]
}
})
dispatch({ type: TypingStateActionType.INCREASE_WRONG_COUNT })
dispatch({ type: TypingStateActionType.REPORT_WRONG_WORD })
const currentState = JSON.parse(JSON.stringify(state))
dispatch({ type: TypingStateActionType.REPORT_WRONG_WORD, payload: { letterMistake: currentState.letterMistake } })
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wordState.inputWord])
@@ -231,10 +192,6 @@ export default function WordComponent({ word, onFinish }: { word: Word; onFinish
useEffect(() => {
if (wordState.isFinished) {
if (!wordState.hasMadeInputWrong) {
dispatch({ type: TypingStateActionType.REPORT_CORRECT_WORD })
}
dispatch({ type: TypingStateActionType.SET_IS_SAVING_RECORD, payload: true })
wordLogUploader({

View File

@@ -0,0 +1,40 @@
import type { LetterState } from './Letter'
import type { LetterMistakes } from '@/utils/db/record'
export type WordState = {
displayWord: string
inputWord: string
letterStates: LetterState[]
isFinished: boolean
// 是否出现输入错误
hasWrong: boolean
// 记录是否已经出现过输入错误
hasMadeInputWrong: boolean
// 用户输入错误的次数
wrongCount: number
startTime: string
endTime: string
inputCount: number
correctCount: number
letterTimeArray: number[]
letterMistake: LetterMistakes
// 用于随机隐藏字母功能
randomLetterVisible: boolean[]
}
export const initialWordState: WordState = {
displayWord: '',
inputWord: '',
letterStates: [],
isFinished: false,
hasWrong: false,
hasMadeInputWrong: false,
wrongCount: 0,
startTime: '',
endTime: '',
inputCount: 0,
correctCount: 0,
letterTimeArray: [],
letterMistake: {},
randomLetterVisible: [],
}

View File

@@ -1,38 +1,10 @@
import type { TypingState, UserInputLog } from './type'
import type { WordWithIndex } from '@/typings'
import type { LetterMistakes } from '@/utils/db/record'
import { mergeLetterMistake } from '@/utils/db/utils'
import shuffle from '@/utils/shuffle'
import { createContext } from 'react'
export type ChapterData = {
words: WordWithIndex[]
index: number
// 用户输入的单词数
wordCount: number
correctCount: number
wrongCount: number
wrongWordIndexes: number[]
// 一次打对未犯错的单词索引
correctWordIndexes: number[]
// 本章节用户输入的单词的 record id 列表
wordRecordIds: number[]
}
export type TimerData = {
time: number
accuracy: number
wpm: number
}
export type TypingState = {
chapterData: ChapterData
timerData: TimerData
isTyping: boolean
isFinished: boolean
isShowSkip: boolean
isTransVisible: boolean
isLoopSingleWord: boolean
// 是否正在保存数据
isSavingRecord: boolean
}
export const initialState: TypingState = {
chapterData: {
words: [],
@@ -40,9 +12,8 @@ export const initialState: TypingState = {
wordCount: 0,
correctCount: 0,
wrongCount: 0,
wrongWordIndexes: [],
correctWordIndexes: [],
wordRecordIds: [],
userInputLogs: [],
},
timerData: {
time: 0,
@@ -57,6 +28,13 @@ export const initialState: TypingState = {
isSavingRecord: false,
}
export const initialUserInputLog: UserInputLog = {
index: 0,
correctCount: 0,
wrongCount: 0,
LetterMistakes: {},
}
export enum TypingStateActionType {
SETUP_CHAPTER = 'SETUP_CHAPTER',
SET_IS_SKIP = 'SET_IS_SKIP',
@@ -67,8 +45,7 @@ export enum TypingStateActionType {
NEXT_WORD = 'NEXT_WORD',
LOOP_CURRENT_WORD = 'LOOP_CURRENT_WORD',
FINISH_CHAPTER = 'FINISH_CHAPTER',
INCREASE_CORRECT_COUNT = 'INCREASE_CORRECT_COUNT',
INCREASE_WRONG_COUNT = 'INCREASE_WRONG_COUNT',
INCREASE_WRONG_WORD = 'INCREASE_WRONG_WORD',
SKIP_WORD = 'SKIP_WORD',
SKIP_2_WORD_INDEX = 'SKIP_2_WORD_INDEX',
REPEAT_CHAPTER = 'REPEAT_CHAPTER',
@@ -87,13 +64,11 @@ export type TypingStateAction =
| { type: TypingStateActionType.SET_IS_SKIP; payload: boolean }
| { type: TypingStateActionType.SET_IS_TYPING; payload: boolean }
| { type: TypingStateActionType.TOGGLE_IS_TYPING }
| { type: TypingStateActionType.REPORT_WRONG_WORD }
| { type: TypingStateActionType.REPORT_WRONG_WORD; payload: { letterMistake: LetterMistakes } }
| { type: TypingStateActionType.REPORT_CORRECT_WORD }
| { type: TypingStateActionType.NEXT_WORD }
| { type: TypingStateActionType.LOOP_CURRENT_WORD }
| { type: TypingStateActionType.FINISH_CHAPTER }
| { type: TypingStateActionType.INCREASE_CORRECT_COUNT }
| { type: TypingStateActionType.INCREASE_WRONG_COUNT }
| { type: TypingStateActionType.SKIP_WORD }
| { type: TypingStateActionType.SKIP_2_WORD_INDEX; newIndex: number }
| { type: TypingStateActionType.REPEAT_CHAPTER; shouldShuffle: boolean }
@@ -111,6 +86,7 @@ export const typingReducer = (state: TypingState, action: TypingStateAction) =>
switch (action.type) {
case TypingStateActionType.SETUP_CHAPTER:
state.chapterData.words = action.payload.shouldShuffle ? shuffle(action.payload.words) : action.payload.words
state.chapterData.userInputLogs = state.chapterData.words.map((_, index) => ({ ...structuredClone(initialUserInputLog), index }))
break
case TypingStateActionType.SET_IS_SKIP:
state.isShowSkip = action.payload
@@ -122,25 +98,20 @@ export const typingReducer = (state: TypingState, action: TypingStateAction) =>
case TypingStateActionType.TOGGLE_IS_TYPING:
state.isTyping = !state.isTyping
break
case TypingStateActionType.REPORT_WRONG_WORD: {
const wordIndex = state.chapterData.words[state.chapterData.index].index
case TypingStateActionType.REPORT_CORRECT_WORD: {
state.chapterData.correctCount += 1
const prevIndex = state.chapterData.wrongWordIndexes.indexOf(wordIndex)
if (prevIndex === -1) {
state.chapterData.wrongWordIndexes.push(wordIndex)
}
const wordLog = state.chapterData.userInputLogs[state.chapterData.index]
wordLog.correctCount += 1
break
}
case TypingStateActionType.REPORT_CORRECT_WORD: {
const wordIndex = state.chapterData.words[state.chapterData.index].index
case TypingStateActionType.REPORT_WRONG_WORD: {
state.chapterData.wrongCount += 1
const prevWrongIndex = state.chapterData.wrongWordIndexes.indexOf(wordIndex)
const prevCorrectIndex = state.chapterData.correctWordIndexes.indexOf(wordIndex)
// 如果之前没有被记录过 出现错误或者正确
if (prevCorrectIndex === -1 && prevWrongIndex === -1) {
state.chapterData.correctWordIndexes.push(wordIndex)
}
const letterMistake = action.payload.letterMistake
const wordLog = state.chapterData.userInputLogs[state.chapterData.index]
wordLog.wrongCount += 1
wordLog.LetterMistakes = mergeLetterMistake(wordLog.LetterMistakes, letterMistake)
break
}
case TypingStateActionType.NEXT_WORD:
@@ -158,12 +129,6 @@ export const typingReducer = (state: TypingState, action: TypingStateAction) =>
state.isFinished = true
state.isShowSkip = false
break
case TypingStateActionType.INCREASE_CORRECT_COUNT:
state.chapterData.correctCount += 1
break
case TypingStateActionType.INCREASE_WRONG_COUNT:
state.chapterData.wrongCount += 1
break
case TypingStateActionType.SKIP_WORD: {
const newIndex = state.chapterData.index + 1
if (newIndex >= state.chapterData.words.length) {
@@ -186,14 +151,15 @@ export const typingReducer = (state: TypingState, action: TypingStateAction) =>
}
case TypingStateActionType.REPEAT_CHAPTER: {
const newState = structuredClone(initialState)
newState.chapterData.userInputLogs = state.chapterData.words.map((_, index) => ({ ...structuredClone(initialUserInputLog), index }))
newState.isTyping = true
newState.chapterData.words = action.shouldShuffle ? shuffle(state.chapterData.words) : state.chapterData.words
newState.isTransVisible = state.isTransVisible
return newState
}
case TypingStateActionType.NEXT_CHAPTER: {
const newState = structuredClone(initialState)
newState.chapterData.userInputLogs = state.chapterData.words.map((_, index) => ({ ...structuredClone(initialUserInputLog), index }))
newState.isTyping = true
newState.isTransVisible = state.isTransVisible
return newState

View File

@@ -0,0 +1,54 @@
import type { WordWithIndex } from '@/typings'
import type { LetterMistakes } from '@/utils/db/record'
export type ChapterData = {
// warning: 因为有章节内随机的存在,所有记录 index 的场景都应该使用 WordWithIndex.index
words: WordWithIndex[]
// chapter index
index: number
// 输入的单词数
wordCount: number
// 输入正确的单词数
correctCount: number
// 输入错误的单词数
wrongCount: number
// 每个单词的输入记录
userInputLogs: UserInputLog[]
// 本章节用户输入的单词的 record id 列表
wordRecordIds: number[]
}
export type UserInputLog = {
// the index in ChapterData.words, not the index in WordWithIndex
index: number
correctCount: number
wrongCount: number
LetterMistakes: LetterMistakes
}
export type TimerData = {
time: number
accuracy: number
wpm: number
}
export type WrongWordData = {
name: string
wrongCount: number
wrongLetters: Array<{
letter: string
count: number
}>
}
export type TypingState = {
chapterData: ChapterData
timerData: TimerData
isTyping: boolean
isFinished: boolean
isShowSkip: boolean
isTransVisible: boolean
isLoopSingleWord: boolean
// 是否正在保存数据
isSavingRecord: boolean
}

View File

@@ -1,7 +1,7 @@
import type { IChapterRecord, IWordRecord, LetterMistakes } from './record'
import { ChapterRecord, WordRecord } from './record'
import type { TypingState } from '@/pages/Typing/store'
import { TypingContext, TypingStateActionType } from '@/pages/Typing/store'
import type { TypingState } from '@/pages/Typing/store/type'
import { currentChapterAtom, currentDictIdAtom } from '@/store'
import type { Table } from 'dexie'
import Dexie from 'dexie'
@@ -33,9 +33,10 @@ export function useSaveChapterRecord() {
const saveChapterRecord = useCallback(
(typingState: TypingState) => {
const {
chapterData: { correctCount, wrongCount, wordCount, correctWordIndexes, words, wordRecordIds },
chapterData: { correctCount, wrongCount, userInputLogs, wordCount, words, wordRecordIds },
timerData: { time },
} = typingState
const correctWordIndexes = userInputLogs.filter((log) => log.correctCount > 0 && log.wrongCount === 0).map((log) => log.index)
const chapterRecord = new ChapterRecord(
dictID,

17
src/utils/db/utils.ts Normal file
View File

@@ -0,0 +1,17 @@
import type { LetterMistakes } from './record'
export function mergeLetterMistake(letterMistake1: LetterMistakes, letterMistake2: LetterMistakes): LetterMistakes {
const result: LetterMistakes = {}
for (const mistakes of [letterMistake1, letterMistake2]) {
for (const key in mistakes) {
if (result[key]) {
result[key].push(...mistakes[key])
} else {
result[key] = [...mistakes[key]]
}
}
}
return result
}

100
yarn.lock
View File

@@ -1125,17 +1125,17 @@
"@esbuild/android-arm64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd"
integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==
"@esbuild/android-arm@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d"
integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==
"@esbuild/android-x64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1"
integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==
"@esbuild/darwin-arm64@0.17.19":
@@ -1145,92 +1145,92 @@
"@esbuild/darwin-x64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb"
integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==
"@esbuild/freebsd-arm64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2"
integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==
"@esbuild/freebsd-x64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4"
integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==
"@esbuild/linux-arm64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb"
integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==
"@esbuild/linux-arm@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a"
integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==
"@esbuild/linux-ia32@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a"
integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==
"@esbuild/linux-loong64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72"
integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==
"@esbuild/linux-mips64el@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289"
integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==
"@esbuild/linux-ppc64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7"
integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==
"@esbuild/linux-riscv64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09"
integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==
"@esbuild/linux-s390x@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829"
integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==
"@esbuild/linux-x64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4"
integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==
"@esbuild/netbsd-x64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462"
integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==
"@esbuild/openbsd-x64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691"
integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==
"@esbuild/sunos-x64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273"
integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==
"@esbuild/win32-arm64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f"
integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==
"@esbuild/win32-ia32@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03"
integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==
"@esbuild/win32-x64@0.17.19":
version "0.17.19"
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061"
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz"
integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==
"@eslint-community/eslint-utils@^4.2.0":
@@ -2030,6 +2030,11 @@ acorn@^8.8.0, acorn@^8.8.2:
resolved "https://registry.npmmirror.com/acorn/-/acorn-8.8.2.tgz"
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
adler-32@~1.3.0:
version "1.3.1"
resolved "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz"
integrity sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==
aggregate-error@^3.0.0:
version "3.1.0"
resolved "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz"
@@ -2383,6 +2388,14 @@ canvas-confetti@^1.6.0:
resolved "https://registry.npmmirror.com/canvas-confetti/-/canvas-confetti-1.6.0.tgz"
integrity sha512-ej+w/m8Jzpv9Z7W7uJZer14Ke8P2ogsjg4ZMGIuq4iqUOqY2Jq8BNW42iGmNfRwREaaEfFIczLuZZiEVSYNHAA==
cfb@~1.2.1:
version "1.2.2"
resolved "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz#94e687628c700e5155436dac05f74e08df23bc44"
integrity sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==
dependencies:
adler-32 "~1.3.0"
crc-32 "~1.2.0"
chalk@5.2.0:
version "5.2.0"
resolved "https://registry.npmmirror.com/chalk/-/chalk-5.2.0.tgz"
@@ -2481,6 +2494,11 @@ cliui@^8.0.1:
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"
codepage@~1.15.0:
version "1.15.0"
resolved "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz"
integrity sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz"
@@ -2596,6 +2614,11 @@ cosmiconfig@^8.1.3:
parse-json "^5.0.0"
path-type "^4.0.0"
crc-32@~1.2.0, crc-32@~1.2.1:
version "1.2.2"
resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz"
integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.npmmirror.com/cross-env/-/cross-env-7.0.3.tgz"
@@ -3327,6 +3350,11 @@ for-each@^0.3.3:
dependencies:
is-callable "^1.1.3"
frac@~1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz"
integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==
fraction.js@^4.2.0:
version "4.2.0"
resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz"
@@ -5192,6 +5220,13 @@ source-map@^0.7.3, source-map@^0.7.4:
resolved "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz"
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
ssf@~0.11.2:
version "0.11.2"
resolved "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz"
integrity sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==
dependencies:
frac "~1.1.2"
stop-iteration-iterator@^1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz"
@@ -5705,11 +5740,21 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"
wmf@~1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz"
integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==
word-wrap@^1.2.3:
version "1.2.3"
resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
word@~0.3.0:
version "0.3.0"
resolved "https://registry.npmjs.org/word/-/word-0.3.0.tgz"
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
wrap-ansi@^6.2.0:
version "6.2.0"
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz"
@@ -5733,6 +5778,19 @@ wrappy@1:
resolved "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
xlsx@^0.18.5:
version "0.18.5"
resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz"
integrity sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==
dependencies:
adler-32 "~1.3.0"
cfb "~1.2.1"
codepage "~1.15.0"
crc-32 "~1.2.1"
ssf "~0.11.2"
wmf "~1.0.1"
word "~0.3.0"
xtend@^4.0.2:
version "4.0.2"
resolved "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz"