├── src ├── vite-env.d.ts ├── assets │ ├── ak.jpg │ ├── lol.jpg │ └── react.svg ├── main.tsx ├── utils │ ├── type.ts │ ├── line.ts │ └── canny.ts ├── App.tsx └── index.css ├── README.md ├── vite.config.ts ├── tsconfig.node.json ├── .gitignore ├── index.html ├── package.json ├── tsconfig.json ├── public └── vite.svg ├── .github └── workflows │ └── main.yml └── pnpm-lock.yaml /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/assets/ak.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lishaobos/demo-outline-animation/HEAD/src/assets/ak.jpg -------------------------------------------------------------------------------- /src/assets/lol.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lishaobos/demo-outline-animation/HEAD/src/assets/lol.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 轨迹动画(演示项目) 2 | 3 | 以轨迹动画来过渡图片的加载。 4 | 5 | ## [在线体验](https://lishaobos.github.io/demo-outline-animation) 6 | 7 | ## 使用 8 | 9 | ```js 10 | pnpm i 11 | ``` 12 | 13 | ## 运行 14 | ```js 15 | pnpm dev 16 | ``` -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | base: '/demo-outline-animation', 7 | plugins: [react()] 8 | }) 9 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | import './utils/line' 6 | 7 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 8 | 9 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /src/utils/type.ts: -------------------------------------------------------------------------------- 1 | export type Point = [number, number] 2 | 3 | export type Line = Point[] 4 | 5 | export type Lines = Line[] 6 | 7 | export type PointMap = Map 8 | 9 | export enum Directions { 10 | Left, 11 | LeftTop, 12 | Top, 13 | TopRight, 14 | Right, 15 | RightBottom, 16 | Bottom, 17 | BottomLeft, 18 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 轨迹动画 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-outline-animation", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^18.0.24", 17 | "@types/react-dom": "^18.0.8", 18 | "@vitejs/plugin-react": "^2.2.0", 19 | "typescript": "^4.6.4", 20 | "vite": "^3.2.3" 21 | } 22 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import ak from './assets/ak.jpg' 3 | 4 | function App() { 5 | const [url, setUrl] = useState(ak) 6 | 7 | const changeUrl = async () => { 8 | // @ts-ignore 9 | const [fileHandle] = await window.showOpenFilePicker({ 10 | types: [ 11 | { 12 | description: 'Images', 13 | accept: { 14 | 'image/*': ['.png', '.gif', '.jpeg', '.jpg'], 15 | }, 16 | }, 17 | ], 18 | }) 19 | const fileData = await fileHandle.getFile(); 20 | const reader = new FileReader() 21 | reader.readAsDataURL(fileData) 22 | reader.onload = () => { 23 | setUrl(reader.result as string) 24 | } 25 | } 26 | 27 | return ( 28 |
29 | 30 |
31 | {/* @ts-ignore */} 32 | 33 |
34 | ) 35 | } 36 | 37 | export default App 38 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 3 | font-size: 16px; 4 | line-height: 24px; 5 | font-weight: 400; 6 | /* color-scheme: light dark; */ 7 | /* color: rgba(255, 255, 255, 0.87); */ 8 | /* background-color: #242424; */ 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | /* place-items: center; */ 30 | min-width: 320px; 31 | min-height: 100vh; 32 | background-color: #242424; 33 | } 34 | 35 | h1 { 36 | font-size: 3.2em; 37 | line-height: 1.1; 38 | } 39 | 40 | /* button { 41 | border-radius: 8px; 42 | border: 1px solid transparent; 43 | padding: 0.6em 1.2em; 44 | font-size: 1em; 45 | font-weight: 500; 46 | font-family: inherit; 47 | background-color: #1a1a1a; 48 | cursor: pointer; 49 | transition: border-color 0.25s; 50 | } 51 | button:hover { 52 | border-color: #646cff; 53 | } 54 | button:focus, 55 | button:focus-visible { 56 | outline: 4px auto -webkit-focus-ring-color; 57 | } */ 58 | 59 | @media (prefers-color-scheme: light) { 60 | :root { 61 | color: #213547; 62 | background-color: #ffffff; 63 | } 64 | a:hover { 65 | color: #747bff; 66 | } 67 | button { 68 | background-color: #f9f9f9; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | # Single deploy job since we're just deploying 25 | deploy: 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3 33 | - name: Setup Pages 34 | uses: actions/configure-pages@v2 35 | - name: Setup Node 36 | uses: actions/setup-node@v3.5.1 37 | with: 38 | node-version: 16 39 | - name: Setup pnpm 40 | uses: pnpm/action-setup@v2.2.4 41 | with: 42 | version: 7.17.0 43 | - name: install 44 | run: pnpm i 45 | - name: Build 46 | run: pnpm build 47 | - name: Upload artifact 48 | uses: actions/upload-pages-artifact@v1 49 | with: 50 | # Upload entire repository 51 | path: 'dist' 52 | - name: Deploy to GitHub Pages 53 | id: deployment 54 | uses: actions/deploy-pages@v1 55 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/line.ts: -------------------------------------------------------------------------------- 1 | import type { Point, Line, Lines, PointMap } from './type' 2 | import { Directions } from './type' 3 | import { CannyJS } from './canny' 4 | 5 | const directions = [ 6 | Directions.Left, 7 | Directions.LeftTop, 8 | Directions.Top, 9 | Directions.TopRight, 10 | Directions.Right, 11 | Directions.RightBottom, 12 | Directions.Bottom, 13 | Directions.BottomLeft, 14 | ] 15 | 16 | const compose = (...fncs: ((...args: any) => any)[]) => (...args: any[]) => fncs.reduce((p, n, i) => i ? n(p) : n(...p), args) 17 | 18 | const getPointByDirection = ([x, y]: Point, dirction: Directions): Point => { 19 | switch (dirction) { 20 | case Directions.Left: 21 | return [x - 1, y] 22 | case Directions.LeftTop: 23 | return [x - 1, y - 1] 24 | case Directions.Top: 25 | return [x, y - 1] 26 | case Directions.TopRight: 27 | return [x + 1, y - 1] 28 | case Directions.Right: 29 | return [x + 1, y] 30 | case Directions.RightBottom: 31 | return [x + 1, y + 1] 32 | case Directions.Bottom: 33 | return [x, y + 1] 34 | case Directions.BottomLeft: 35 | return [x - 1, y + 1] 36 | } 37 | } 38 | 39 | const getPoints = (imgData: number[], w: number, h: number): PointMap => { 40 | const pointMap: PointMap = new Map() 41 | for (let y = h - 1; y >= 0; y--) { 42 | for (let x = w - 1; x >= 0; x--) { 43 | const i = x * 4 + y * w * 4 44 | 45 | if (imgData[i] === 255) pointMap.set(`${x}-${y}`, [x, y]) 46 | } 47 | } 48 | 49 | return pointMap 50 | } 51 | 52 | const getLines = (pointMap: PointMap) => { 53 | const pointCache = new Map() 54 | const lines: Lines = [] 55 | const { random } = Math 56 | for (const [x, y] of pointMap.values()) { 57 | if (pointCache.has(`${x}-${y}`)) continue 58 | 59 | 60 | directions.sort(() => random() - random()) 61 | const line: Line = [] 62 | let start: Point = [x, y] 63 | lines.push(line) 64 | line.push(start) 65 | let i = 0 66 | while (i < directions.length) { 67 | const [x, y] = getPointByDirection(start, directions[i]) 68 | if (!pointMap.has(`${x}-${y}`) || pointCache.has(`${x}-${y}`)) { 69 | i++ 70 | continue 71 | } 72 | 73 | i = 0 74 | line.push(start = [x, y]) 75 | pointCache.set(`${x}-${y}`, true) 76 | } 77 | } 78 | 79 | return lines 80 | } 81 | 82 | const getCanvasLines: (imgData: number[], w: number, h: number) => Lines = compose( 83 | getPoints, 84 | getLines 85 | ) 86 | 87 | class Draw extends HTMLElement { 88 | initial = false 89 | url: string | null = null 90 | width: string | null | undefined 91 | height: string | null | undefined 92 | svg: SVGSVGElement 93 | img: HTMLImageElement 94 | canvas: HTMLCanvasElement 95 | ctx: CanvasRenderingContext2D 96 | shadowRoot: ShadowRoot 97 | styleNode: HTMLStyleElement 98 | wrapper: HTMLElement 99 | defaultStyle = ` 100 | .wrapper { 101 | position: relative; 102 | } 103 | img { 104 | position: absolute; 105 | opacity: 0; 106 | z-index: 1; 107 | /*animation-start*/ 108 | animation: img 1s ease var(--time) var(--state) forwards; 109 | /*animation-end*/ 110 | } 111 | svg { 112 | position: absolute; 113 | } 114 | svg polyline { 115 | stroke-dasharray: var(--offset); 116 | stroke-dashoffset: var(--offset); 117 | /*animation-start*/ 118 | animation: line var(--time); 119 | /*animation-end*/ 120 | } 121 | @keyframes line { 122 | 100% { 123 | stroke-dashoffset: 0; 124 | } 125 | } 126 | @keyframes img { 127 | 100% { 128 | opacity: 1 129 | } 130 | } 131 | ` 132 | 133 | constructor() { 134 | super() 135 | 136 | this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') 137 | this.img = new Image() 138 | this.canvas = document.createElement('canvas') 139 | this.ctx = this.canvas.getContext('2d')! 140 | this.shadowRoot = this.attachShadow({ mode: 'open' }) 141 | this.wrapper = document.createElement('div') 142 | this.wrapper.classList.add('wrapper') 143 | this.wrapper.style.setProperty('--time', '2s') 144 | this.wrapper.style.setProperty('--state', 'paused') 145 | this.styleNode = document.createElement('style') 146 | this.styleNode.setAttribute('type', 'text/css') 147 | this.styleNode.innerHTML = this.defaultStyle 148 | } 149 | 150 | async connectedCallback() { 151 | this.initial = true 152 | this.url = this.getAttribute('url') 153 | if (!this.url) throw new Error('需要 url') 154 | 155 | this.width = this.getAttribute('width') 156 | this.height = this.getAttribute('height') 157 | this.width && this.img.setAttribute('width', this.width) 158 | this.height && this.img.setAttribute('height', this.height) 159 | this.shadowRoot.appendChild(this.styleNode) 160 | this.wrapper.appendChild(this.img) 161 | this.shadowRoot.appendChild(this.wrapper) 162 | this.wrapper.appendChild(this.svg) 163 | this.wrapper.style.setProperty('--state', 'running') 164 | await this.start() 165 | } 166 | 167 | static get observedAttributes() { 168 | return ['width', 'height', 'url'] 169 | } 170 | 171 | async attributeChangedCallback(key: string, oldVal: string, newVal: string) { 172 | this[key as 'width' | 'height' | 'url'] = newVal 173 | 174 | if (!this.initial) return 175 | this.svg.innerHTML = '' 176 | this.styleNode.innerHTML = this.defaultStyle.replace(/(\/\*animation-start\*\/)[^/]*(\/\*animation-end\*\/)/g, '') 177 | this.wrapper.style.setProperty('--state', 'paused') 178 | this.wrapper.style.setProperty('--state', 'running') 179 | await new Promise(r => setTimeout(r)) 180 | await this.start() 181 | setTimeout(() => { 182 | this.styleNode.innerHTML = this.defaultStyle 183 | }) 184 | } 185 | 186 | async start() { 187 | const img = await this.loadImg(this.url!) 188 | this.canvas.setAttribute('width', this.width!) 189 | this.canvas.setAttribute('height', this.height!) 190 | this.ctx.drawImage(img, 0, 0, +this.width!, +this.height!) 191 | const canny = CannyJS.canny(this.canvas, 80, 10, 1.4, 3) 192 | const data = getCanvasLines(canny.toImageDataArray(), +this.width!, +this.height!) 193 | this.svg.setAttribute('viewBox', `0,0 ${this.width} ${this.height}`) 194 | this.svg.setAttribute('width', this.width!) 195 | this.svg.setAttribute('height', this.height!) 196 | this.svg.innerHTML = this.createPolyLines(data) 197 | } 198 | 199 | createPolyLines(lines: Lines) { 200 | return lines.map(line => ``).join('') 201 | } 202 | 203 | loadImg(url: string) { 204 | return new Promise((resolve, reject) => { 205 | const { img } = this 206 | img.src = url 207 | img.onload = () => { 208 | this.width = img.width + '' 209 | this.height = img.height + '' 210 | resolve(img) 211 | } 212 | img.onerror = reject 213 | }) 214 | } 215 | } 216 | 217 | customElements.define('active-img-draw', Draw) -------------------------------------------------------------------------------- /src/utils/canny.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 3 | /** 4 | * Utility object 5 | */ 6 | 7 | var CannyJS: any, GrayImageData, Util; 8 | 9 | Util = {}; 10 | 11 | Util.generateMatrix = function(w, h, initialValue) { 12 | var matrix, x, y, _i, _j, _ref, _ref1; 13 | matrix = []; 14 | for (x = _i = 0, _ref = w - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; x = 0 <= _ref ? ++_i : --_i) { 15 | matrix[x] = []; 16 | for (y = _j = 0, _ref1 = h - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; y = 0 <= _ref1 ? ++_j : --_j) { 17 | matrix[x][y] = initialValue; 18 | } 19 | } 20 | return matrix; 21 | }; 22 | 23 | 24 | /** 25 | * Class that represents gray-scaled image data 26 | */ 27 | 28 | GrayImageData = (function() { 29 | 30 | /** 31 | * construct a new image data 32 | * @param {number} width of the image 33 | * @param {number} height of the image 34 | */ 35 | function GrayImageData(width, height) { 36 | this.width = width; 37 | this.height = height; 38 | this.data = Util.generateMatrix(this.width, this.height, 0); 39 | this; 40 | } 41 | 42 | 43 | /** 44 | * load image data from canvas and store it as a matrix of gray-scaled pixels 45 | * @param {object} canvas object 46 | */ 47 | 48 | GrayImageData.prototype.loadCanvas = function(canvas) { 49 | var b, ctx, d, g, i, r, rawdata, x, y, _i, _len; 50 | ctx = canvas.getContext('2d'); 51 | rawdata = ctx.getImageData(0, 0, canvas.width, canvas.height).data; 52 | x = 0; 53 | y = 0; 54 | for (i = _i = 0, _len = rawdata.length; _i < _len; i = _i += 4) { 55 | d = rawdata[i]; 56 | r = rawdata[i]; 57 | g = rawdata[i + 1]; 58 | b = rawdata[i + 2]; 59 | this.data[x][y] = Math.round(0.298 * r + 0.586 * g + 0.114 * b); 60 | if (x === this.width - 1) { 61 | x = 0; 62 | y += 1; 63 | } else { 64 | x += 1; 65 | } 66 | } 67 | return this; 68 | }; 69 | 70 | 71 | /** 72 | * get the neighbor of a given point 73 | * @param {number} x corrdinate of the point 74 | * @param {number} y corrdinate of the point 75 | * @param {number} size of the neighbors 76 | * @return {array} matrix of the neighbor of the point 77 | */ 78 | 79 | GrayImageData.prototype.getNeighbors = function(x, y, size) { 80 | var i, j, neighbors, trnsX, trnsY, _i, _j, _ref, _ref1; 81 | neighbors = Util.generateMatrix(size, size, 0); 82 | for (i = _i = 0, _ref = size - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { 83 | neighbors[i] = []; 84 | for (j = _j = 0, _ref1 = size - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; j = 0 <= _ref1 ? ++_j : --_j) { 85 | trnsX = x - (size - 1) / 2 + i; 86 | trnsY = y - (size - 1) / 2 + j; 87 | if (this.data[trnsX] && this.data[trnsX][trnsY]) { 88 | neighbors[i][j] = this.data[trnsX][trnsY]; 89 | } else { 90 | neighbors[i][j] = 0; 91 | } 92 | } 93 | } 94 | return neighbors; 95 | }; 96 | 97 | 98 | /** 99 | * iterate all the pixel in the image data 100 | * @param {number} size of the neighbors given to 101 | * @param {function} function that will applied to the pixel 102 | */ 103 | 104 | GrayImageData.prototype.eachPixel = function(neighborSize, func) { 105 | var current, neighbors, x, y, _i, _j, _ref, _ref1; 106 | for (x = _i = 0, _ref = this.width - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; x = 0 <= _ref ? ++_i : --_i) { 107 | for (y = _j = 0, _ref1 = this.height - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; y = 0 <= _ref1 ? ++_j : --_j) { 108 | current = this.data[x][y]; 109 | neighbors = this.getNeighbors(x, y, neighborSize); 110 | func(x, y, current, neighbors); 111 | } 112 | } 113 | return this; 114 | }; 115 | 116 | 117 | /** 118 | * return linear array of the image data 119 | * @return {array} array of the pixel color data 120 | */ 121 | 122 | GrayImageData.prototype.toImageDataArray = function() { 123 | var ary, i, x, y, _i, _j, _k, _ref, _ref1; 124 | ary = []; 125 | for (y = _i = 0, _ref = this.height - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; y = 0 <= _ref ? ++_i : --_i) { 126 | for (x = _j = 0, _ref1 = this.width - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; x = 0 <= _ref1 ? ++_j : --_j) { 127 | for (i = _k = 0; _k <= 2; i = ++_k) { 128 | ary.push(this.data[x][y]); 129 | } 130 | ary.push(255); 131 | } 132 | } 133 | return ary; 134 | }; 135 | 136 | 137 | /** 138 | * return a deep copy of this object 139 | * @return {object} the copy of this object 140 | */ 141 | 142 | GrayImageData.prototype.copy = function() { 143 | var copied, x, y, _i, _j, _ref, _ref1; 144 | copied = new GrayImageData(this.width, this.height); 145 | for (x = _i = 0, _ref = this.width - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; x = 0 <= _ref ? ++_i : --_i) { 146 | for (y = _j = 0, _ref1 = this.height - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; y = 0 <= _ref1 ? ++_j : --_j) { 147 | copied.data[x][y] = this.data[x][y]; 148 | } 149 | } 150 | copied.width = this.width; 151 | copied.height = this.height; 152 | return copied; 153 | }; 154 | 155 | 156 | /** 157 | * draw the image on a given canvas 158 | * @param {object} target canvas object 159 | */ 160 | 161 | GrayImageData.prototype.drawOn = function(canvas) { 162 | var color, ctx, i, imgData, _i, _len, _ref; 163 | ctx = canvas.getContext('2d'); 164 | imgData = ctx.createImageData(canvas.width, canvas.height); 165 | _ref = this.toImageDataArray(); 166 | for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { 167 | color = _ref[i]; 168 | imgData.data[i] = color; 169 | } 170 | return ctx.putImageData(imgData, 0, 0); 171 | }; 172 | 173 | 174 | /** 175 | * fill the image with given color 176 | * @param {number} color to fill 177 | */ 178 | 179 | GrayImageData.prototype.fill = function(color) { 180 | var x, y, _i, _ref, _results; 181 | _results = []; 182 | for (y = _i = 0, _ref = this.height - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; y = 0 <= _ref ? ++_i : --_i) { 183 | _results.push((function() { 184 | var _j, _ref1, _results1; 185 | _results1 = []; 186 | for (x = _j = 0, _ref1 = this.width - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; x = 0 <= _ref1 ? ++_j : --_j) { 187 | _results1.push(this.data[x][y] = color); 188 | } 189 | return _results1; 190 | }).call(this)); 191 | } 192 | return _results; 193 | }; 194 | 195 | return GrayImageData; 196 | 197 | })(); 198 | 199 | 200 | /** 201 | * object that holds methods for image processing 202 | */ 203 | 204 | CannyJS = {}; 205 | 206 | 207 | /** 208 | * apply gaussian blur to the image data 209 | * @param {object} GrayImageData object 210 | * @param {number} [sigmma=1.4] value of sigmma of gauss function 211 | * @param {number} [size=3] size of the kernel (must be an odd number) 212 | * @return {object} GrayImageData object 213 | */ 214 | 215 | CannyJS.gaussianBlur = function(imgData, sigmma, size) { 216 | var copy, kernel; 217 | if (sigmma == null) { 218 | sigmma = 1.4; 219 | } 220 | if (size == null) { 221 | size = 3; 222 | } 223 | kernel = CannyJS.generateKernel(sigmma, size); 224 | copy = imgData.copy(); 225 | copy.fill(0); 226 | imgData.eachPixel(size, function(x, y, current, neighbors) { 227 | var i, j, _results; 228 | i = 0; 229 | _results = []; 230 | while (i <= size - 1) { 231 | j = 0; 232 | while (j <= size - 1) { 233 | copy.data[x][y] += neighbors[i][j] * kernel[i][j]; 234 | j++; 235 | } 236 | _results.push(i++); 237 | } 238 | return _results; 239 | }); 240 | return copy; 241 | }; 242 | 243 | 244 | /** 245 | * generate kernel matrix 246 | * @param {number} [sigmma] value of sigmma of gauss function 247 | * @param {number} [size] size of the kernel (must be an odd number) 248 | * @return {array} kernel matrix 249 | */ 250 | 251 | CannyJS.generateKernel = function(sigmma, size) { 252 | var e, gaussian, i, j, kernel, s, sum, x, y, _i, _j, _k, _l, _ref, _ref1, _ref2, _ref3; 253 | s = sigmma; 254 | e = 2.718; 255 | kernel = Util.generateMatrix(size, size, 0); 256 | sum = 0; 257 | for (i = _i = 0, _ref = size - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { 258 | x = -(size - 1) / 2 + i; 259 | for (j = _j = 0, _ref1 = size - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; j = 0 <= _ref1 ? ++_j : --_j) { 260 | y = -(size - 1) / 2 + j; 261 | gaussian = (1 / (2 * Math.PI * s * s)) * Math.pow(e, -(x * x + y * y) / (2 * s * s)); 262 | kernel[i][j] = gaussian; 263 | sum += gaussian; 264 | } 265 | } 266 | for (i = _k = 0, _ref2 = size - 1; 0 <= _ref2 ? _k <= _ref2 : _k >= _ref2; i = 0 <= _ref2 ? ++_k : --_k) { 267 | for (j = _l = 0, _ref3 = size - 1; 0 <= _ref3 ? _l <= _ref3 : _l >= _ref3; j = 0 <= _ref3 ? ++_l : --_l) { 268 | kernel[i][j] = (kernel[i][j] / sum).toFixed(3); 269 | } 270 | } 271 | console.log("kernel", kernel); 272 | return kernel; 273 | }; 274 | 275 | 276 | /** 277 | * appy sobel filter to image data 278 | * @param {object} GrayImageData object 279 | * @return {object} GrayImageData object 280 | */ 281 | 282 | CannyJS.sobel = function(imgData) { 283 | var copy, xFiler, yFiler; 284 | yFiler = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]; 285 | xFiler = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]]; 286 | copy = imgData.copy(); 287 | copy.fill(0); 288 | imgData.eachPixel(3, function(x, y, current, neighbors) { 289 | var ghs, gvs, i, j, _i, _j; 290 | ghs = 0; 291 | gvs = 0; 292 | for (i = _i = 0; _i <= 2; i = ++_i) { 293 | for (j = _j = 0; _j <= 2; j = ++_j) { 294 | ghs += yFiler[i][j] * neighbors[i][j]; 295 | gvs += xFiler[i][j] * neighbors[i][j]; 296 | } 297 | } 298 | return copy.data[x][y] = Math.sqrt(ghs * ghs + gvs * gvs); 299 | }); 300 | return copy; 301 | }; 302 | 303 | 304 | /** 305 | * appy non-maximum suppression to image data 306 | * @param {object} GrayImageData object 307 | * @return {object} GrayImageData object 308 | */ 309 | 310 | CannyJS.nonMaximumSuppression = function(imgData) { 311 | var copy; 312 | copy = imgData.copy(); 313 | copy.fill(0); 314 | imgData.eachPixel(3, function(x, y, c, n) { 315 | if (n[1][1] > n[0][1] && n[1][1] > n[2][1]) { 316 | copy.data[x][y] = n[1][1]; 317 | } else { 318 | copy.data[x][y] = 0; 319 | } 320 | if (n[1][1] > n[0][2] && n[1][1] > n[2][0]) { 321 | copy.data[x][y] = n[1][1]; 322 | } else { 323 | copy.data[x][y] = 0; 324 | } 325 | if (n[1][1] > n[1][0] && n[1][1] > n[1][2]) { 326 | copy.data[x][y] = n[1][1]; 327 | } else { 328 | copy.data[x][y] = 0; 329 | } 330 | if (n[1][1] > n[0][0] && n[1][1] > n[2][2]) { 331 | return copy.data[x][y] = n[1][1]; 332 | } else { 333 | return copy.data[x][y] = 0; 334 | } 335 | }); 336 | return copy; 337 | }; 338 | 339 | 340 | /** 341 | * appy hysteresis threshold to image data 342 | * @param {object} GrayImageData object 343 | * @param {number} [ht=150] value of high threshold 344 | * @param {number} [lt=100] value of low threshold 345 | * @return {object} GrayImageData object 346 | */ 347 | 348 | CannyJS.hysteresis = function(imgData, ht, lt) { 349 | var copy, isCandidate, isStrong, isWeak, traverseEdge; 350 | copy = imgData.copy(); 351 | isStrong = function(edge) { 352 | return edge > ht; 353 | }; 354 | isCandidate = function(edge) { 355 | return edge <= ht && edge >= lt; 356 | }; 357 | isWeak = function(edge) { 358 | return edge < lt; 359 | }; 360 | imgData.eachPixel(3, function(x, y, current, neighbors) { 361 | if (isStrong(current)) { 362 | return copy.data[x][y] = 255; 363 | } else if (isWeak(current) || isCandidate(current)) { 364 | return copy.data[x][y] = 0; 365 | } 366 | }); 367 | traverseEdge = function(x, y) { 368 | var i, j, neighbors, _i, _results; 369 | if (x === 0 || y === 0 || x === imgData.width - 1 || y === imgData.height - 1) { 370 | return; 371 | } 372 | if (isStrong(copy.data[x][y])) { 373 | neighbors = copy.getNeighbors(x, y, 3); 374 | _results = []; 375 | for (i = _i = 0; _i <= 2; i = ++_i) { 376 | _results.push((function() { 377 | var _j, _results1; 378 | _results1 = []; 379 | for (j = _j = 0; _j <= 2; j = ++_j) { 380 | if (isCandidate(neighbors[i][j])) { 381 | copy.data[x - 1 + i][y - 1 + j] = 255; 382 | _results1.push(traverseEdge(x - 1 + i, y - 1 + j)); 383 | } else { 384 | _results1.push(void 0); 385 | } 386 | } 387 | return _results1; 388 | })()); 389 | } 390 | return _results; 391 | } 392 | }; 393 | copy.eachPixel(3, function(x, y) { 394 | return traverseEdge(x, y); 395 | }); 396 | copy.eachPixel(1, function(x, y, current) { 397 | if (!isStrong(current)) { 398 | return copy.data[x][y] = 0; 399 | } 400 | }); 401 | return copy; 402 | }; 403 | 404 | 405 | /** 406 | * appy canny edge detection algorithm to canvas 407 | * @param {object} canvas object 408 | * @param {number} [ht=100] value of high threshold 409 | * @param {number} [lt=50] value of low threshold 410 | * @param {number} [sigmma=1.4] value of sigmma of gauss function 411 | * @param {number} [size=3] size of the kernel (must be an odd number) 412 | * @return {object} GrayImageData object 413 | */ 414 | 415 | CannyJS.canny = function(canvas, ht, lt, sigmma, kernelSize) { 416 | var blur, imgData, nms, sobel; 417 | if (ht == null) { 418 | ht = 100; 419 | } 420 | if (lt == null) { 421 | lt = 50; 422 | } 423 | if (sigmma == null) { 424 | sigmma = 1.4; 425 | } 426 | if (kernelSize == null) { 427 | kernelSize = 3; 428 | } 429 | imgData = new GrayImageData(canvas.width, canvas.height); 430 | imgData.loadCanvas(canvas); 431 | blur = CannyJS.gaussianBlur(imgData, sigmma, kernelSize); 432 | sobel = CannyJS.sobel(blur); 433 | nms = CannyJS.nonMaximumSuppression(sobel); 434 | return CannyJS.hysteresis(nms, ht, lt); 435 | }; 436 | 437 | 438 | 439 | 440 | 441 | export { 442 | CannyJS, 443 | GrayImageData, 444 | } -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | '@types/react': ^18.0.24 5 | '@types/react-dom': ^18.0.8 6 | '@vitejs/plugin-react': ^2.2.0 7 | react: ^18.2.0 8 | react-dom: ^18.2.0 9 | typescript: ^4.6.4 10 | vite: ^3.2.3 11 | 12 | dependencies: 13 | react: 18.2.0 14 | react-dom: 18.2.0_react@18.2.0 15 | 16 | devDependencies: 17 | '@types/react': 18.0.25 18 | '@types/react-dom': 18.0.9 19 | '@vitejs/plugin-react': 2.2.0_vite@3.2.4 20 | typescript: 4.9.3 21 | vite: 3.2.4 22 | 23 | packages: 24 | 25 | /@ampproject/remapping/2.2.0: 26 | resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} 27 | engines: {node: '>=6.0.0'} 28 | dependencies: 29 | '@jridgewell/gen-mapping': 0.1.1 30 | '@jridgewell/trace-mapping': 0.3.17 31 | dev: true 32 | 33 | /@babel/code-frame/7.18.6: 34 | resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} 35 | engines: {node: '>=6.9.0'} 36 | dependencies: 37 | '@babel/highlight': 7.18.6 38 | dev: true 39 | 40 | /@babel/compat-data/7.20.1: 41 | resolution: {integrity: sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ==} 42 | engines: {node: '>=6.9.0'} 43 | dev: true 44 | 45 | /@babel/core/7.20.2: 46 | resolution: {integrity: sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==} 47 | engines: {node: '>=6.9.0'} 48 | dependencies: 49 | '@ampproject/remapping': 2.2.0 50 | '@babel/code-frame': 7.18.6 51 | '@babel/generator': 7.20.4 52 | '@babel/helper-compilation-targets': 7.20.0_@babel+core@7.20.2 53 | '@babel/helper-module-transforms': 7.20.2 54 | '@babel/helpers': 7.20.1 55 | '@babel/parser': 7.20.3 56 | '@babel/template': 7.18.10 57 | '@babel/traverse': 7.20.1 58 | '@babel/types': 7.20.2 59 | convert-source-map: 1.9.0 60 | debug: 4.3.4 61 | gensync: r2.cnpmjs.org/gensync/1.0.0-beta.2 62 | json5: 2.2.1 63 | semver: r2.cnpmjs.org/semver/6.3.0 64 | transitivePeerDependencies: 65 | - supports-color 66 | dev: true 67 | 68 | /@babel/generator/7.20.4: 69 | resolution: {integrity: sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA==} 70 | engines: {node: '>=6.9.0'} 71 | dependencies: 72 | '@babel/types': 7.20.2 73 | '@jridgewell/gen-mapping': 0.3.2 74 | jsesc: r2.cnpmjs.org/jsesc/2.5.2 75 | dev: true 76 | 77 | /@babel/helper-annotate-as-pure/7.18.6: 78 | resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} 79 | engines: {node: '>=6.9.0'} 80 | dependencies: 81 | '@babel/types': 7.20.2 82 | dev: true 83 | 84 | /@babel/helper-compilation-targets/7.20.0_@babel+core@7.20.2: 85 | resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} 86 | engines: {node: '>=6.9.0'} 87 | peerDependencies: 88 | '@babel/core': ^7.0.0 89 | dependencies: 90 | '@babel/compat-data': 7.20.1 91 | '@babel/core': 7.20.2 92 | '@babel/helper-validator-option': 7.18.6 93 | browserslist: 4.21.4 94 | semver: r2.cnpmjs.org/semver/6.3.0 95 | dev: true 96 | 97 | /@babel/helper-environment-visitor/7.18.9: 98 | resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} 99 | engines: {node: '>=6.9.0'} 100 | dev: true 101 | 102 | /@babel/helper-function-name/7.19.0: 103 | resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} 104 | engines: {node: '>=6.9.0'} 105 | dependencies: 106 | '@babel/template': 7.18.10 107 | '@babel/types': 7.20.2 108 | dev: true 109 | 110 | /@babel/helper-hoist-variables/7.18.6: 111 | resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} 112 | engines: {node: '>=6.9.0'} 113 | dependencies: 114 | '@babel/types': 7.20.2 115 | dev: true 116 | 117 | /@babel/helper-module-imports/7.18.6: 118 | resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} 119 | engines: {node: '>=6.9.0'} 120 | dependencies: 121 | '@babel/types': 7.20.2 122 | dev: true 123 | 124 | /@babel/helper-module-transforms/7.20.2: 125 | resolution: {integrity: sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==} 126 | engines: {node: '>=6.9.0'} 127 | dependencies: 128 | '@babel/helper-environment-visitor': 7.18.9 129 | '@babel/helper-module-imports': 7.18.6 130 | '@babel/helper-simple-access': 7.20.2 131 | '@babel/helper-split-export-declaration': 7.18.6 132 | '@babel/helper-validator-identifier': 7.19.1 133 | '@babel/template': 7.18.10 134 | '@babel/traverse': 7.20.1 135 | '@babel/types': 7.20.2 136 | transitivePeerDependencies: 137 | - supports-color 138 | dev: true 139 | 140 | /@babel/helper-plugin-utils/7.20.2: 141 | resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} 142 | engines: {node: '>=6.9.0'} 143 | dev: true 144 | 145 | /@babel/helper-simple-access/7.20.2: 146 | resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} 147 | engines: {node: '>=6.9.0'} 148 | dependencies: 149 | '@babel/types': 7.20.2 150 | dev: true 151 | 152 | /@babel/helper-split-export-declaration/7.18.6: 153 | resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} 154 | engines: {node: '>=6.9.0'} 155 | dependencies: 156 | '@babel/types': 7.20.2 157 | dev: true 158 | 159 | /@babel/helper-string-parser/7.19.4: 160 | resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} 161 | engines: {node: '>=6.9.0'} 162 | dev: true 163 | 164 | /@babel/helper-validator-identifier/7.19.1: 165 | resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} 166 | engines: {node: '>=6.9.0'} 167 | dev: true 168 | 169 | /@babel/helper-validator-option/7.18.6: 170 | resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} 171 | engines: {node: '>=6.9.0'} 172 | dev: true 173 | 174 | /@babel/helpers/7.20.1: 175 | resolution: {integrity: sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg==} 176 | engines: {node: '>=6.9.0'} 177 | dependencies: 178 | '@babel/template': 7.18.10 179 | '@babel/traverse': 7.20.1 180 | '@babel/types': 7.20.2 181 | transitivePeerDependencies: 182 | - supports-color 183 | dev: true 184 | 185 | /@babel/highlight/7.18.6: 186 | resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} 187 | engines: {node: '>=6.9.0'} 188 | dependencies: 189 | '@babel/helper-validator-identifier': 7.19.1 190 | chalk: r2.cnpmjs.org/chalk/2.4.2 191 | js-tokens: r2.cnpmjs.org/js-tokens/4.0.0 192 | dev: true 193 | 194 | /@babel/parser/7.20.3: 195 | resolution: {integrity: sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg==} 196 | engines: {node: '>=6.0.0'} 197 | hasBin: true 198 | dependencies: 199 | '@babel/types': 7.20.2 200 | dev: true 201 | 202 | /@babel/plugin-syntax-jsx/7.18.6_@babel+core@7.20.2: 203 | resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==} 204 | engines: {node: '>=6.9.0'} 205 | peerDependencies: 206 | '@babel/core': ^7.0.0-0 207 | dependencies: 208 | '@babel/core': 7.20.2 209 | '@babel/helper-plugin-utils': 7.20.2 210 | dev: true 211 | 212 | /@babel/plugin-transform-react-jsx-development/7.18.6_@babel+core@7.20.2: 213 | resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} 214 | engines: {node: '>=6.9.0'} 215 | peerDependencies: 216 | '@babel/core': ^7.0.0-0 217 | dependencies: 218 | '@babel/core': 7.20.2 219 | '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.20.2 220 | dev: true 221 | 222 | /@babel/plugin-transform-react-jsx-self/7.18.6_@babel+core@7.20.2: 223 | resolution: {integrity: sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==} 224 | engines: {node: '>=6.9.0'} 225 | peerDependencies: 226 | '@babel/core': ^7.0.0-0 227 | dependencies: 228 | '@babel/core': 7.20.2 229 | '@babel/helper-plugin-utils': 7.20.2 230 | dev: true 231 | 232 | /@babel/plugin-transform-react-jsx-source/7.19.6_@babel+core@7.20.2: 233 | resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} 234 | engines: {node: '>=6.9.0'} 235 | peerDependencies: 236 | '@babel/core': ^7.0.0-0 237 | dependencies: 238 | '@babel/core': 7.20.2 239 | '@babel/helper-plugin-utils': 7.20.2 240 | dev: true 241 | 242 | /@babel/plugin-transform-react-jsx/7.19.0_@babel+core@7.20.2: 243 | resolution: {integrity: sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==} 244 | engines: {node: '>=6.9.0'} 245 | peerDependencies: 246 | '@babel/core': ^7.0.0-0 247 | dependencies: 248 | '@babel/core': 7.20.2 249 | '@babel/helper-annotate-as-pure': 7.18.6 250 | '@babel/helper-module-imports': 7.18.6 251 | '@babel/helper-plugin-utils': 7.20.2 252 | '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.20.2 253 | '@babel/types': 7.20.2 254 | dev: true 255 | 256 | /@babel/template/7.18.10: 257 | resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} 258 | engines: {node: '>=6.9.0'} 259 | dependencies: 260 | '@babel/code-frame': 7.18.6 261 | '@babel/parser': 7.20.3 262 | '@babel/types': 7.20.2 263 | dev: true 264 | 265 | /@babel/traverse/7.20.1: 266 | resolution: {integrity: sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA==} 267 | engines: {node: '>=6.9.0'} 268 | dependencies: 269 | '@babel/code-frame': 7.18.6 270 | '@babel/generator': 7.20.4 271 | '@babel/helper-environment-visitor': 7.18.9 272 | '@babel/helper-function-name': 7.19.0 273 | '@babel/helper-hoist-variables': 7.18.6 274 | '@babel/helper-split-export-declaration': 7.18.6 275 | '@babel/parser': 7.20.3 276 | '@babel/types': 7.20.2 277 | debug: 4.3.4 278 | globals: r2.cnpmjs.org/globals/11.12.0 279 | transitivePeerDependencies: 280 | - supports-color 281 | dev: true 282 | 283 | /@babel/types/7.20.2: 284 | resolution: {integrity: sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==} 285 | engines: {node: '>=6.9.0'} 286 | dependencies: 287 | '@babel/helper-string-parser': 7.19.4 288 | '@babel/helper-validator-identifier': 7.19.1 289 | to-fast-properties: r2.cnpmjs.org/to-fast-properties/2.0.0 290 | dev: true 291 | 292 | /@esbuild/android-arm/0.15.15: 293 | resolution: {integrity: sha512-JJjZjJi2eBL01QJuWjfCdZxcIgot+VoK6Fq7eKF9w4YHm9hwl7nhBR1o2Wnt/WcANk5l9SkpvrldW1PLuXxcbw==} 294 | engines: {node: '>=12'} 295 | cpu: [arm] 296 | os: [android] 297 | requiresBuild: true 298 | dev: true 299 | optional: true 300 | 301 | /@esbuild/linux-loong64/0.15.15: 302 | resolution: {integrity: sha512-lhz6UNPMDXUhtXSulw8XlFAtSYO26WmHQnCi2Lg2p+/TMiJKNLtZCYUxV4wG6rZMzXmr8InGpNwk+DLT2Hm0PA==} 303 | engines: {node: '>=12'} 304 | cpu: [loong64] 305 | os: [linux] 306 | requiresBuild: true 307 | dev: true 308 | optional: true 309 | 310 | /@jridgewell/gen-mapping/0.1.1: 311 | resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} 312 | engines: {node: '>=6.0.0'} 313 | dependencies: 314 | '@jridgewell/set-array': 1.1.2 315 | '@jridgewell/sourcemap-codec': 1.4.14 316 | dev: true 317 | 318 | /@jridgewell/gen-mapping/0.3.2: 319 | resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} 320 | engines: {node: '>=6.0.0'} 321 | dependencies: 322 | '@jridgewell/set-array': 1.1.2 323 | '@jridgewell/sourcemap-codec': 1.4.14 324 | '@jridgewell/trace-mapping': 0.3.17 325 | dev: true 326 | 327 | /@jridgewell/resolve-uri/3.1.0: 328 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} 329 | engines: {node: '>=6.0.0'} 330 | dev: true 331 | 332 | /@jridgewell/set-array/1.1.2: 333 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 334 | engines: {node: '>=6.0.0'} 335 | dev: true 336 | 337 | /@jridgewell/sourcemap-codec/1.4.14: 338 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} 339 | dev: true 340 | 341 | /@jridgewell/trace-mapping/0.3.17: 342 | resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} 343 | dependencies: 344 | '@jridgewell/resolve-uri': 3.1.0 345 | '@jridgewell/sourcemap-codec': 1.4.14 346 | dev: true 347 | 348 | /@types/prop-types/15.7.5: 349 | resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} 350 | dev: true 351 | 352 | /@types/react-dom/18.0.9: 353 | resolution: {integrity: sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==} 354 | dependencies: 355 | '@types/react': 18.0.25 356 | dev: true 357 | 358 | /@types/react/18.0.25: 359 | resolution: {integrity: sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==} 360 | dependencies: 361 | '@types/prop-types': 15.7.5 362 | '@types/scheduler': r2.cnpmjs.org/@types/scheduler/0.16.2 363 | csstype: 3.1.1 364 | dev: true 365 | 366 | /@vitejs/plugin-react/2.2.0_vite@3.2.4: 367 | resolution: {integrity: sha512-FFpefhvExd1toVRlokZgxgy2JtnBOdp4ZDsq7ldCWaqGSGn9UhWMAVm/1lxPL14JfNS5yGz+s9yFrQY6shoStA==} 368 | engines: {node: ^14.18.0 || >=16.0.0} 369 | peerDependencies: 370 | vite: ^3.0.0 371 | dependencies: 372 | '@babel/core': 7.20.2 373 | '@babel/plugin-transform-react-jsx': 7.19.0_@babel+core@7.20.2 374 | '@babel/plugin-transform-react-jsx-development': 7.18.6_@babel+core@7.20.2 375 | '@babel/plugin-transform-react-jsx-self': 7.18.6_@babel+core@7.20.2 376 | '@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.20.2 377 | magic-string: 0.26.7 378 | react-refresh: 0.14.0 379 | vite: 3.2.4 380 | transitivePeerDependencies: 381 | - supports-color 382 | dev: true 383 | 384 | /browserslist/4.21.4: 385 | resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} 386 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 387 | hasBin: true 388 | dependencies: 389 | caniuse-lite: 1.0.30001434 390 | electron-to-chromium: 1.4.284 391 | node-releases: 2.0.6 392 | update-browserslist-db: 1.0.10_browserslist@4.21.4 393 | dev: true 394 | 395 | /caniuse-lite/1.0.30001434: 396 | resolution: {integrity: sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==} 397 | dev: true 398 | 399 | /convert-source-map/1.9.0: 400 | resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} 401 | dev: true 402 | 403 | /csstype/3.1.1: 404 | resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} 405 | dev: true 406 | 407 | /debug/4.3.4: 408 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 409 | engines: {node: '>=6.0'} 410 | peerDependencies: 411 | supports-color: '*' 412 | peerDependenciesMeta: 413 | supports-color: 414 | optional: true 415 | dependencies: 416 | ms: r2.cnpmjs.org/ms/2.1.2 417 | dev: true 418 | 419 | /electron-to-chromium/1.4.284: 420 | resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} 421 | dev: true 422 | 423 | /esbuild-android-64/0.15.15: 424 | resolution: {integrity: sha512-F+WjjQxO+JQOva3tJWNdVjouFMLK6R6i5gjDvgUthLYJnIZJsp1HlF523k73hELY20WPyEO8xcz7aaYBVkeg5Q==} 425 | engines: {node: '>=12'} 426 | cpu: [x64] 427 | os: [android] 428 | requiresBuild: true 429 | dev: true 430 | optional: true 431 | 432 | /esbuild-android-arm64/0.15.15: 433 | resolution: {integrity: sha512-attlyhD6Y22jNyQ0fIIQ7mnPvDWKw7k6FKnsXlBvQE6s3z6s6cuEHcSgoirquQc7TmZgVCK5fD/2uxmRN+ZpcQ==} 434 | engines: {node: '>=12'} 435 | cpu: [arm64] 436 | os: [android] 437 | requiresBuild: true 438 | dev: true 439 | optional: true 440 | 441 | /esbuild-darwin-64/0.15.15: 442 | resolution: {integrity: sha512-ohZtF8W1SHJ4JWldsPVdk8st0r9ExbAOSrBOh5L+Mq47i696GVwv1ab/KlmbUoikSTNoXEhDzVpxUR/WIO19FQ==} 443 | engines: {node: '>=12'} 444 | cpu: [x64] 445 | os: [darwin] 446 | requiresBuild: true 447 | dev: true 448 | optional: true 449 | 450 | /esbuild-darwin-arm64/0.15.15: 451 | resolution: {integrity: sha512-P8jOZ5zshCNIuGn+9KehKs/cq5uIniC+BeCykvdVhx/rBXSxmtj3CUIKZz4sDCuESMbitK54drf/2QX9QHG5Ag==} 452 | engines: {node: '>=12'} 453 | cpu: [arm64] 454 | os: [darwin] 455 | requiresBuild: true 456 | dev: true 457 | optional: true 458 | 459 | /esbuild-freebsd-64/0.15.15: 460 | resolution: {integrity: sha512-KkTg+AmDXz1IvA9S1gt8dE24C8Thx0X5oM0KGF322DuP+P3evwTL9YyusHAWNsh4qLsR80nvBr/EIYs29VSwuA==} 461 | engines: {node: '>=12'} 462 | cpu: [x64] 463 | os: [freebsd] 464 | requiresBuild: true 465 | dev: true 466 | optional: true 467 | 468 | /esbuild-freebsd-arm64/0.15.15: 469 | resolution: {integrity: sha512-FUcML0DRsuyqCMfAC+HoeAqvWxMeq0qXvclZZ/lt2kLU6XBnDA5uKTLUd379WYEyVD4KKFctqWd9tTuk8C/96g==} 470 | engines: {node: '>=12'} 471 | cpu: [arm64] 472 | os: [freebsd] 473 | requiresBuild: true 474 | dev: true 475 | optional: true 476 | 477 | /esbuild-linux-32/0.15.15: 478 | resolution: {integrity: sha512-q28Qn5pZgHNqug02aTkzw5sW9OklSo96b5nm17Mq0pDXrdTBcQ+M6Q9A1B+dalFeynunwh/pvfrNucjzwDXj+Q==} 479 | engines: {node: '>=12'} 480 | cpu: [ia32] 481 | os: [linux] 482 | requiresBuild: true 483 | dev: true 484 | optional: true 485 | 486 | /esbuild-linux-64/0.15.15: 487 | resolution: {integrity: sha512-217KPmWMirkf8liO+fj2qrPwbIbhNTGNVtvqI1TnOWJgcMjUWvd677Gq3fTzXEjilkx2yWypVnTswM2KbXgoAg==} 488 | engines: {node: '>=12'} 489 | cpu: [x64] 490 | os: [linux] 491 | requiresBuild: true 492 | dev: true 493 | optional: true 494 | 495 | /esbuild-linux-arm/0.15.15: 496 | resolution: {integrity: sha512-RYVW9o2yN8yM7SB1yaWr378CwrjvGCyGybX3SdzPHpikUHkME2AP55Ma20uNwkNyY2eSYFX9D55kDrfQmQBR4w==} 497 | engines: {node: '>=12'} 498 | cpu: [arm] 499 | os: [linux] 500 | requiresBuild: true 501 | dev: true 502 | optional: true 503 | 504 | /esbuild-linux-arm64/0.15.15: 505 | resolution: {integrity: sha512-/ltmNFs0FivZkYsTzAsXIfLQX38lFnwJTWCJts0IbCqWZQe+jjj0vYBNbI0kmXLb3y5NljiM5USVAO1NVkdh2g==} 506 | engines: {node: '>=12'} 507 | cpu: [arm64] 508 | os: [linux] 509 | requiresBuild: true 510 | dev: true 511 | optional: true 512 | 513 | /esbuild-linux-mips64le/0.15.15: 514 | resolution: {integrity: sha512-PksEPb321/28GFFxtvL33yVPfnMZihxkEv5zME2zapXGp7fA1X2jYeiTUK+9tJ/EGgcNWuwvtawPxJG7Mmn86A==} 515 | engines: {node: '>=12'} 516 | cpu: [mips64el] 517 | os: [linux] 518 | requiresBuild: true 519 | dev: true 520 | optional: true 521 | 522 | /esbuild-linux-ppc64le/0.15.15: 523 | resolution: {integrity: sha512-ek8gJBEIhcpGI327eAZigBOHl58QqrJrYYIZBWQCnH3UnXoeWMrMZLeeZL8BI2XMBhP+sQ6ERctD5X+ajL/AIA==} 524 | engines: {node: '>=12'} 525 | cpu: [ppc64] 526 | os: [linux] 527 | requiresBuild: true 528 | dev: true 529 | optional: true 530 | 531 | /esbuild-linux-riscv64/0.15.15: 532 | resolution: {integrity: sha512-H5ilTZb33/GnUBrZMNJtBk7/OXzDHDXjIzoLXHSutwwsLxSNaLxzAaMoDGDd/keZoS+GDBqNVxdCkpuiRW4OSw==} 533 | engines: {node: '>=12'} 534 | cpu: [riscv64] 535 | os: [linux] 536 | requiresBuild: true 537 | dev: true 538 | optional: true 539 | 540 | /esbuild-linux-s390x/0.15.15: 541 | resolution: {integrity: sha512-jKaLUg78mua3rrtrkpv4Or2dNTJU7bgHN4bEjT4OX4GR7nLBSA9dfJezQouTxMmIW7opwEC5/iR9mpC18utnxQ==} 542 | engines: {node: '>=12'} 543 | cpu: [s390x] 544 | os: [linux] 545 | requiresBuild: true 546 | dev: true 547 | optional: true 548 | 549 | /esbuild-netbsd-64/0.15.15: 550 | resolution: {integrity: sha512-aOvmF/UkjFuW6F36HbIlImJTTx45KUCHJndtKo+KdP8Dhq3mgLRKW9+6Ircpm8bX/RcS3zZMMmaBLkvGY06Gvw==} 551 | engines: {node: '>=12'} 552 | cpu: [x64] 553 | os: [netbsd] 554 | requiresBuild: true 555 | dev: true 556 | optional: true 557 | 558 | /esbuild-openbsd-64/0.15.15: 559 | resolution: {integrity: sha512-HFFX+WYedx1w2yJ1VyR1Dfo8zyYGQZf1cA69bLdrHzu9svj6KH6ZLK0k3A1/LFPhcEY9idSOhsB2UyU0tHPxgQ==} 560 | engines: {node: '>=12'} 561 | cpu: [x64] 562 | os: [openbsd] 563 | requiresBuild: true 564 | dev: true 565 | optional: true 566 | 567 | /esbuild-sunos-64/0.15.15: 568 | resolution: {integrity: sha512-jOPBudffG4HN8yJXcK9rib/ZTFoTA5pvIKbRrt3IKAGMq1EpBi4xoVoSRrq/0d4OgZLaQbmkHp8RO9eZIn5atA==} 569 | engines: {node: '>=12'} 570 | cpu: [x64] 571 | os: [sunos] 572 | requiresBuild: true 573 | dev: true 574 | optional: true 575 | 576 | /esbuild-windows-32/0.15.15: 577 | resolution: {integrity: sha512-MDkJ3QkjnCetKF0fKxCyYNBnOq6dmidcwstBVeMtXSgGYTy8XSwBeIE4+HuKiSsG6I/mXEb++px3IGSmTN0XiA==} 578 | engines: {node: '>=12'} 579 | cpu: [ia32] 580 | os: [win32] 581 | requiresBuild: true 582 | dev: true 583 | optional: true 584 | 585 | /esbuild-windows-64/0.15.15: 586 | resolution: {integrity: sha512-xaAUIB2qllE888SsMU3j9nrqyLbkqqkpQyWVkfwSil6BBPgcPk3zOFitTTncEKCLTQy3XV9RuH7PDj3aJDljWA==} 587 | engines: {node: '>=12'} 588 | cpu: [x64] 589 | os: [win32] 590 | requiresBuild: true 591 | dev: true 592 | optional: true 593 | 594 | /esbuild-windows-arm64/0.15.15: 595 | resolution: {integrity: sha512-ttuoCYCIJAFx4UUKKWYnFdrVpoXa3+3WWkXVI6s09U+YjhnyM5h96ewTq/WgQj9LFSIlABQvadHSOQyAVjW5xQ==} 596 | engines: {node: '>=12'} 597 | cpu: [arm64] 598 | os: [win32] 599 | requiresBuild: true 600 | dev: true 601 | optional: true 602 | 603 | /esbuild/0.15.15: 604 | resolution: {integrity: sha512-TEw/lwK4Zzld9x3FedV6jy8onOUHqcEX3ADFk4k+gzPUwrxn8nWV62tH0udo8jOtjFodlEfc4ypsqX3e+WWO6w==} 605 | engines: {node: '>=12'} 606 | hasBin: true 607 | requiresBuild: true 608 | optionalDependencies: 609 | '@esbuild/android-arm': 0.15.15 610 | '@esbuild/linux-loong64': 0.15.15 611 | esbuild-android-64: 0.15.15 612 | esbuild-android-arm64: 0.15.15 613 | esbuild-darwin-64: 0.15.15 614 | esbuild-darwin-arm64: 0.15.15 615 | esbuild-freebsd-64: 0.15.15 616 | esbuild-freebsd-arm64: 0.15.15 617 | esbuild-linux-32: 0.15.15 618 | esbuild-linux-64: 0.15.15 619 | esbuild-linux-arm: 0.15.15 620 | esbuild-linux-arm64: 0.15.15 621 | esbuild-linux-mips64le: 0.15.15 622 | esbuild-linux-ppc64le: 0.15.15 623 | esbuild-linux-riscv64: 0.15.15 624 | esbuild-linux-s390x: 0.15.15 625 | esbuild-netbsd-64: 0.15.15 626 | esbuild-openbsd-64: 0.15.15 627 | esbuild-sunos-64: 0.15.15 628 | esbuild-windows-32: 0.15.15 629 | esbuild-windows-64: 0.15.15 630 | esbuild-windows-arm64: 0.15.15 631 | dev: true 632 | 633 | /is-core-module/2.11.0: 634 | resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} 635 | dependencies: 636 | has: r2.cnpmjs.org/has/1.0.3 637 | dev: true 638 | 639 | /json5/2.2.1: 640 | resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} 641 | engines: {node: '>=6'} 642 | hasBin: true 643 | dev: true 644 | 645 | /magic-string/0.26.7: 646 | resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==} 647 | engines: {node: '>=12'} 648 | dependencies: 649 | sourcemap-codec: r2.cnpmjs.org/sourcemap-codec/1.4.8 650 | dev: true 651 | 652 | /nanoid/3.3.4: 653 | resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} 654 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 655 | hasBin: true 656 | dev: true 657 | 658 | /node-releases/2.0.6: 659 | resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} 660 | dev: true 661 | 662 | /postcss/8.4.19: 663 | resolution: {integrity: sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==} 664 | engines: {node: ^10 || ^12 || >=14} 665 | dependencies: 666 | nanoid: 3.3.4 667 | picocolors: r2.cnpmjs.org/picocolors/1.0.0 668 | source-map-js: 1.0.2 669 | dev: true 670 | 671 | /react-dom/18.2.0_react@18.2.0: 672 | resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} 673 | peerDependencies: 674 | react: ^18.2.0 675 | dependencies: 676 | loose-envify: r2.cnpmjs.org/loose-envify/1.4.0 677 | react: 18.2.0 678 | scheduler: 0.23.0 679 | dev: false 680 | 681 | /react-refresh/0.14.0: 682 | resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} 683 | engines: {node: '>=0.10.0'} 684 | dev: true 685 | 686 | /react/18.2.0: 687 | resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} 688 | engines: {node: '>=0.10.0'} 689 | dependencies: 690 | loose-envify: r2.cnpmjs.org/loose-envify/1.4.0 691 | dev: false 692 | 693 | /resolve/1.22.1: 694 | resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} 695 | hasBin: true 696 | dependencies: 697 | is-core-module: 2.11.0 698 | path-parse: r2.cnpmjs.org/path-parse/1.0.7 699 | supports-preserve-symlinks-flag: 1.0.0 700 | dev: true 701 | 702 | /rollup/2.79.1: 703 | resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} 704 | engines: {node: '>=10.0.0'} 705 | hasBin: true 706 | optionalDependencies: 707 | fsevents: r2.cnpmjs.org/fsevents/2.3.2 708 | dev: true 709 | 710 | /scheduler/0.23.0: 711 | resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} 712 | dependencies: 713 | loose-envify: r2.cnpmjs.org/loose-envify/1.4.0 714 | dev: false 715 | 716 | /source-map-js/1.0.2: 717 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 718 | engines: {node: '>=0.10.0'} 719 | dev: true 720 | 721 | /supports-preserve-symlinks-flag/1.0.0: 722 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 723 | engines: {node: '>= 0.4'} 724 | dev: true 725 | 726 | /typescript/4.9.3: 727 | resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} 728 | engines: {node: '>=4.2.0'} 729 | hasBin: true 730 | dev: true 731 | 732 | /update-browserslist-db/1.0.10_browserslist@4.21.4: 733 | resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} 734 | hasBin: true 735 | peerDependencies: 736 | browserslist: '>= 4.21.0' 737 | dependencies: 738 | browserslist: 4.21.4 739 | escalade: r2.cnpmjs.org/escalade/3.1.1 740 | picocolors: r2.cnpmjs.org/picocolors/1.0.0 741 | dev: true 742 | 743 | /vite/3.2.4: 744 | resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} 745 | engines: {node: ^14.18.0 || >=16.0.0} 746 | hasBin: true 747 | peerDependencies: 748 | '@types/node': '>= 14' 749 | less: '*' 750 | sass: '*' 751 | stylus: '*' 752 | sugarss: '*' 753 | terser: ^5.4.0 754 | peerDependenciesMeta: 755 | '@types/node': 756 | optional: true 757 | less: 758 | optional: true 759 | sass: 760 | optional: true 761 | stylus: 762 | optional: true 763 | sugarss: 764 | optional: true 765 | terser: 766 | optional: true 767 | dependencies: 768 | esbuild: 0.15.15 769 | postcss: 8.4.19 770 | resolve: 1.22.1 771 | rollup: 2.79.1 772 | optionalDependencies: 773 | fsevents: r2.cnpmjs.org/fsevents/2.3.2 774 | dev: true 775 | 776 | r2.cnpmjs.org/@types/scheduler/0.16.2: 777 | resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz} 778 | name: '@types/scheduler' 779 | version: 0.16.2 780 | dev: true 781 | 782 | r2.cnpmjs.org/ansi-styles/3.2.1: 783 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz} 784 | name: ansi-styles 785 | version: 3.2.1 786 | engines: {node: '>=4'} 787 | dependencies: 788 | color-convert: r2.cnpmjs.org/color-convert/1.9.3 789 | dev: true 790 | 791 | r2.cnpmjs.org/chalk/2.4.2: 792 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/chalk/-/chalk-2.4.2.tgz} 793 | name: chalk 794 | version: 2.4.2 795 | engines: {node: '>=4'} 796 | dependencies: 797 | ansi-styles: r2.cnpmjs.org/ansi-styles/3.2.1 798 | escape-string-regexp: r2.cnpmjs.org/escape-string-regexp/1.0.5 799 | supports-color: r2.cnpmjs.org/supports-color/5.5.0 800 | dev: true 801 | 802 | r2.cnpmjs.org/color-convert/1.9.3: 803 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/color-convert/-/color-convert-1.9.3.tgz} 804 | name: color-convert 805 | version: 1.9.3 806 | dependencies: 807 | color-name: r2.cnpmjs.org/color-name/1.1.3 808 | dev: true 809 | 810 | r2.cnpmjs.org/color-name/1.1.3: 811 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/color-name/-/color-name-1.1.3.tgz} 812 | name: color-name 813 | version: 1.1.3 814 | dev: true 815 | 816 | r2.cnpmjs.org/escalade/3.1.1: 817 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/escalade/-/escalade-3.1.1.tgz} 818 | name: escalade 819 | version: 3.1.1 820 | engines: {node: '>=6'} 821 | dev: true 822 | 823 | r2.cnpmjs.org/escape-string-regexp/1.0.5: 824 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz} 825 | name: escape-string-regexp 826 | version: 1.0.5 827 | engines: {node: '>=0.8.0'} 828 | dev: true 829 | 830 | r2.cnpmjs.org/fsevents/2.3.2: 831 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/fsevents/-/fsevents-2.3.2.tgz} 832 | name: fsevents 833 | version: 2.3.2 834 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 835 | os: [darwin] 836 | requiresBuild: true 837 | dev: true 838 | optional: true 839 | 840 | r2.cnpmjs.org/function-bind/1.1.1: 841 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/function-bind/-/function-bind-1.1.1.tgz} 842 | name: function-bind 843 | version: 1.1.1 844 | dev: true 845 | 846 | r2.cnpmjs.org/gensync/1.0.0-beta.2: 847 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz} 848 | name: gensync 849 | version: 1.0.0-beta.2 850 | engines: {node: '>=6.9.0'} 851 | dev: true 852 | 853 | r2.cnpmjs.org/globals/11.12.0: 854 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/globals/-/globals-11.12.0.tgz} 855 | name: globals 856 | version: 11.12.0 857 | engines: {node: '>=4'} 858 | dev: true 859 | 860 | r2.cnpmjs.org/has-flag/3.0.0: 861 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/has-flag/-/has-flag-3.0.0.tgz} 862 | name: has-flag 863 | version: 3.0.0 864 | engines: {node: '>=4'} 865 | dev: true 866 | 867 | r2.cnpmjs.org/has/1.0.3: 868 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/has/-/has-1.0.3.tgz} 869 | name: has 870 | version: 1.0.3 871 | engines: {node: '>= 0.4.0'} 872 | dependencies: 873 | function-bind: r2.cnpmjs.org/function-bind/1.1.1 874 | dev: true 875 | 876 | r2.cnpmjs.org/js-tokens/4.0.0: 877 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/js-tokens/-/js-tokens-4.0.0.tgz} 878 | name: js-tokens 879 | version: 4.0.0 880 | 881 | r2.cnpmjs.org/jsesc/2.5.2: 882 | resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/jsesc/-/jsesc-2.5.2.tgz} 883 | name: jsesc 884 | version: 2.5.2 885 | engines: {node: '>=4'} 886 | hasBin: true 887 | dev: true 888 | 889 | r2.cnpmjs.org/loose-envify/1.4.0: 890 | resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/loose-envify/-/loose-envify-1.4.0.tgz} 891 | name: loose-envify 892 | version: 1.4.0 893 | hasBin: true 894 | dependencies: 895 | js-tokens: r2.cnpmjs.org/js-tokens/4.0.0 896 | dev: false 897 | 898 | r2.cnpmjs.org/ms/2.1.2: 899 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/ms/-/ms-2.1.2.tgz} 900 | name: ms 901 | version: 2.1.2 902 | dev: true 903 | 904 | r2.cnpmjs.org/path-parse/1.0.7: 905 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/path-parse/-/path-parse-1.0.7.tgz} 906 | name: path-parse 907 | version: 1.0.7 908 | dev: true 909 | 910 | r2.cnpmjs.org/picocolors/1.0.0: 911 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/picocolors/-/picocolors-1.0.0.tgz} 912 | name: picocolors 913 | version: 1.0.0 914 | dev: true 915 | 916 | r2.cnpmjs.org/semver/6.3.0: 917 | resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/semver/-/semver-6.3.0.tgz} 918 | name: semver 919 | version: 6.3.0 920 | hasBin: true 921 | dev: true 922 | 923 | r2.cnpmjs.org/sourcemap-codec/1.4.8: 924 | resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz} 925 | name: sourcemap-codec 926 | version: 1.4.8 927 | dev: true 928 | 929 | r2.cnpmjs.org/supports-color/5.5.0: 930 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/supports-color/-/supports-color-5.5.0.tgz} 931 | name: supports-color 932 | version: 5.5.0 933 | engines: {node: '>=4'} 934 | dependencies: 935 | has-flag: r2.cnpmjs.org/has-flag/3.0.0 936 | dev: true 937 | 938 | r2.cnpmjs.org/to-fast-properties/2.0.0: 939 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==, registry: https://r.cnpmjs.org/, tarball: https://r2.cnpmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz} 940 | name: to-fast-properties 941 | version: 2.0.0 942 | engines: {node: '>=4'} 943 | dev: true 944 | --------------------------------------------------------------------------------