diff --git a/next.config.js b/next.config.js index cb41d95..ec07ce3 100644 --- a/next.config.js +++ b/next.config.js @@ -1,8 +1,9 @@ /** @type {import('next').NextConfig} */ const withPlugins = require('next-compose-plugins'); const withTM = require('next-transpile-modules')(['react-syntax-highlighter']); +const withFonts = require('next-fonts'); -const nextConfig = withPlugins([withTM], { +const nextConfig = withPlugins([withFonts, withTM], { reactStrictMode: false, output: 'export', images: { @@ -11,14 +12,14 @@ const nextConfig = withPlugins([withTM], { basePath: '/tools', async redirects() { return [ - { - source: '/', - destination: '/tools', - basePath: false, - permanent: true - } + { + source: '/', + destination: '/tools', + basePath: false, + permanent: true + } ] -} + } }); module.exports = nextConfig; diff --git a/package-lock.json b/package-lock.json index bf74635..d2b227d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@mui/material": "^5.13.0", "@tanstack/react-query": "^4.29.7", "ace-builds": "^1.32.2", + "ahooks": "^3.7.8", "axios": "^1.4.0", "big-integer": "^1.6.52", "crypto-js": "^4.2.0", @@ -62,7 +63,8 @@ "eslint-plugin-next": "^0.0.0", "eslint-plugin-prettier": "^5.1.0", "husky": "^8.0.0", - "lint-staged": "^15.2.0" + "lint-staged": "^15.2.0", + "next-fonts": "^1.5.1" }, "engines": { "node": ">=16.0.0", @@ -1045,6 +1047,70 @@ "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true, + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@mui/base": { "version": "5.0.0-beta.28", "resolved": "https://registry.npmmirror.com/@mui/base/-/base-5.0.0-beta.28.tgz", @@ -1581,6 +1647,35 @@ "@types/ms": "*" } }, + "node_modules/@types/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-FlsN0p4FhuYRjIxpbdXovvHQhtlG05O1GG/RNWvdAxTboR438IOTwmrY/vLA+Xfgg06BTkP045M3vpFwTMv1dg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "peer": true + }, "node_modules/@types/extend": { "version": "3.0.4", "resolved": "https://registry.npmmirror.com/@types/extend/-/extend-3.0.4.tgz", @@ -1594,6 +1689,17 @@ "@types/unist": "^2" } }, + "node_modules/@types/js-cookie": { + "version": "2.2.7", + "resolved": "https://registry.npmmirror.com/@types/js-cookie/-/js-cookie-2.2.7.tgz", + "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz", @@ -1778,6 +1884,181 @@ "node": "^16.0.0 || >=18.0.0" } }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "peer": true + }, "node_modules/ace-builds": { "version": "1.32.2", "resolved": "https://registry.npmmirror.com/ace-builds/-/ace-builds-1.32.2.tgz", @@ -1794,6 +2075,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -1810,6 +2101,34 @@ "node": ">=0.8" } }, + "node_modules/ahooks": { + "version": "3.7.8", + "resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.7.8.tgz", + "integrity": "sha512-e/NMlQWoCjaUtncNFIZk3FG1ImSkV/JhScQSkTqnftakRwdfZWSw6zzoWSG9OMYqPNs2MguDYBUFFC6THelWXA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@types/js-cookie": "^2.x.x", + "ahooks-v3-count": "^1.0.0", + "dayjs": "^1.9.1", + "intersection-observer": "^0.12.0", + "js-cookie": "^2.x.x", + "lodash": "^4.17.21", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.0.0", + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/ahooks-v3-count": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/ahooks-v3-count/-/ahooks-v3-count-1.0.0.tgz", + "integrity": "sha512-V7uUvAwnimu6eh/PED4mCDjE7tokeZQLKlxg9lCTMPhN+NjsSbtdacByVlR1oluXQzD3MOw55wylDmQo4+S9ZQ==" + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", @@ -1821,6 +2140,15 @@ "uri-js": "^4.2.2" } }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/ansi-escapes": { "version": "6.2.0", "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-6.2.0.tgz", @@ -2077,6 +2405,15 @@ "node": ">=0.6" } }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmmirror.com/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -2109,6 +2446,25 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer-from": { "version": "0.1.2", "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-0.1.2.tgz", @@ -2227,6 +2583,16 @@ "resolved": "https://registry.npmmirror.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/classnames": { "version": "2.3.2", "resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.3.2.tgz", @@ -2520,6 +2886,11 @@ "node": ">=8" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", @@ -2779,11 +3150,27 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/electron-to-chromium": { + "version": "1.4.616", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz", + "integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==", + "dev": true, + "peer": true + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -2874,6 +3261,13 @@ "safe-array-concat": "^1.0.1" } }, + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", + "dev": true, + "peer": true + }, "node_modules/es-set-tostringtag": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", @@ -3346,6 +3740,16 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "7.2.0", "resolved": "https://registry.npmmirror.com/execa/-/execa-7.2.0.tgz", @@ -3445,6 +3849,22 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz", @@ -4160,6 +4580,11 @@ "node": ">= 0.4" } }, + "node_modules/intersection-observer": { + "version": "0.12.2", + "resolved": "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz", + "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==" + }, "node_modules/is-alphabetical": { "version": "1.0.4", "resolved": "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz", @@ -4543,6 +4968,34 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jiti": { "version": "1.21.0", "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.0.tgz", @@ -4558,6 +5011,11 @@ "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", "peer": true }, + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, "node_modules/js-sdsl": { "version": "4.4.2", "resolved": "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.4.2.tgz", @@ -4846,6 +5304,42 @@ "node": ">=18.0.0" } }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", @@ -4860,8 +5354,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -5672,6 +6165,13 @@ "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "peer": true + }, "node_modules/next": { "version": "14.0.4", "resolved": "https://registry.npmmirror.com/next/-/next-14.0.4.tgz", @@ -5723,6 +6223,19 @@ "resolved": "https://registry.npmmirror.com/next-compose-plugins/-/next-compose-plugins-2.2.1.tgz", "integrity": "sha512-OjJ+fV15FXO2uQXQagLD4C0abYErBjyjE0I0FHpOEIB8upw0hg1ldFP6cqHTJBH1cZqy96OeR3u1dJ+Ez2D4Bg==" }, + "node_modules/next-fonts": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/next-fonts/-/next-fonts-1.5.1.tgz", + "integrity": "sha512-pgEJ40xO1oRhM6RqhQJ9CzuZOFp6Zq+aAD/V1P9sq/wdepvLzhFxDm3lCZNoE7+78NSuMKgT6b1qeXSsqWuUMQ==", + "dev": true, + "dependencies": { + "file-loader": "^6.0.0", + "url-loader": "^4.0.0" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, "node_modules/next-transpile-modules": { "version": "10.0.1", "resolved": "https://registry.npmmirror.com/next-transpile-modules/-/next-transpile-modules-10.0.1.tgz", @@ -5731,6 +6244,13 @@ "enhanced-resolve": "^5.10.0" } }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true, + "peer": true + }, "node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -6169,6 +6689,16 @@ "node": ">=8" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/rc-resize-observer": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz", @@ -6914,6 +7444,28 @@ "loose-envify": "^1.1.0" } }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", @@ -6928,6 +7480,16 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.1.1.tgz", @@ -7044,6 +7606,34 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "peer": true + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/space-separated-tokens": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", @@ -7363,6 +7953,63 @@ "node": ">=6" } }, + "node_modules/terser": { + "version": "5.26.0", + "resolved": "https://registry.npmmirror.com/terser/-/terser-5.26.0.tgz", + "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "engines": { + "node": ">= 10.13.0" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "peer": true + }, "node_modules/text-extensions": { "version": "2.4.0", "resolved": "https://registry.npmmirror.com/text-extensions/-/text-extensions-2.4.0.tgz", @@ -7650,6 +8297,23 @@ "node": ">=8" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "peer": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", @@ -7658,6 +8322,29 @@ "punycode": "^2.1.0" } }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -7730,6 +8417,84 @@ "node": ">=10.13.0" } }, + "node_modules/webpack": { + "version": "5.89.0", + "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index c703958..a24a9cd 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@mui/material": "^5.13.0", "@tanstack/react-query": "^4.29.7", "ace-builds": "^1.32.2", + "ahooks": "^3.7.8", "axios": "^1.4.0", "big-integer": "^1.6.52", "crypto-js": "^4.2.0", @@ -78,6 +79,7 @@ "eslint-plugin-next": "^0.0.0", "eslint-plugin-prettier": "^5.1.0", "husky": "^8.0.0", - "lint-staged": "^15.2.0" + "lint-staged": "^15.2.0", + "next-fonts": "^1.5.1" } } diff --git a/public/cnzz.js b/public/cnzz.js index eda09d7..b33a41a 100644 --- a/public/cnzz.js +++ b/public/cnzz.js @@ -2,8 +2,8 @@ if ( [ 'rivers.chaitin.cn', 'dev.rivers.ctopt.cn', - '127.0.0.1', - 'localhost', + // '127.0.0.1', + // 'localhost', ].includes(document.domain) ) { window.is_river = true; diff --git a/src/asset/tag/dev.svg b/src/asset/tag/dev.svg new file mode 100644 index 0000000..319fbd8 --- /dev/null +++ b/src/asset/tag/dev.svg @@ -0,0 +1,25 @@ + + + 开发工具(未选中) + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/dev_check.svg b/src/asset/tag/dev_check.svg new file mode 100644 index 0000000..aaf8ccc --- /dev/null +++ b/src/asset/tag/dev_check.svg @@ -0,0 +1,25 @@ + + + 开发工具(选中) + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/encode.svg b/src/asset/tag/encode.svg new file mode 100644 index 0000000..0deadf3 --- /dev/null +++ b/src/asset/tag/encode.svg @@ -0,0 +1,24 @@ + + + 编码解码(未选中) + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/encode_check.svg b/src/asset/tag/encode_check.svg new file mode 100644 index 0000000..d18a955 --- /dev/null +++ b/src/asset/tag/encode_check.svg @@ -0,0 +1,24 @@ + + + 编码解码(选中) + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/encryption.svg b/src/asset/tag/encryption.svg new file mode 100644 index 0000000..259832b --- /dev/null +++ b/src/asset/tag/encryption.svg @@ -0,0 +1,22 @@ + + + 加密解密(未选中) + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/encryption_check.svg b/src/asset/tag/encryption_check.svg new file mode 100644 index 0000000..236a9a2 --- /dev/null +++ b/src/asset/tag/encryption_check.svg @@ -0,0 +1,22 @@ + + + 加密解密(选中) + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/hot.svg b/src/asset/tag/hot.svg new file mode 100644 index 0000000..9383ebf --- /dev/null +++ b/src/asset/tag/hot.svg @@ -0,0 +1,23 @@ + + + 热门工具(未选中) 2 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/hot_check.svg b/src/asset/tag/hot_check.svg new file mode 100644 index 0000000..29f12c1 --- /dev/null +++ b/src/asset/tag/hot_check.svg @@ -0,0 +1,23 @@ + + + 热门工具(选中) 2 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/hover_red_like.svg b/src/asset/tag/hover_red_like.svg new file mode 100644 index 0000000..dede3ee --- /dev/null +++ b/src/asset/tag/hover_red_like.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/image.svg b/src/asset/tag/image.svg new file mode 100644 index 0000000..36d1098 --- /dev/null +++ b/src/asset/tag/image.svg @@ -0,0 +1,23 @@ + + + 图像处理(未选中) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/image_check.svg b/src/asset/tag/image_check.svg new file mode 100644 index 0000000..7aa9d6f --- /dev/null +++ b/src/asset/tag/image_check.svg @@ -0,0 +1,23 @@ + + + 图像处理(选中) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/json.svg b/src/asset/tag/json.svg new file mode 100644 index 0000000..dbb819c --- /dev/null +++ b/src/asset/tag/json.svg @@ -0,0 +1,24 @@ + + + json (未选中) + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/json_check.svg b/src/asset/tag/json_check.svg new file mode 100644 index 0000000..e89ffe2 --- /dev/null +++ b/src/asset/tag/json_check.svg @@ -0,0 +1,24 @@ + + + json (选中) + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/like.svg b/src/asset/tag/like.svg new file mode 100644 index 0000000..6564274 --- /dev/null +++ b/src/asset/tag/like.svg @@ -0,0 +1,24 @@ + + + 我的收藏(未选中) + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/like_check.svg b/src/asset/tag/like_check.svg new file mode 100644 index 0000000..d812add --- /dev/null +++ b/src/asset/tag/like_check.svg @@ -0,0 +1,24 @@ + + + 我的收藏(选中) + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/no_like.png b/src/asset/tag/no_like.png new file mode 100644 index 0000000..28191f1 Binary files /dev/null and b/src/asset/tag/no_like.png differ diff --git a/src/asset/tag/other.svg b/src/asset/tag/other.svg new file mode 100644 index 0000000..8a65f70 --- /dev/null +++ b/src/asset/tag/other.svg @@ -0,0 +1,23 @@ + + + 杂项(未选中) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/other_check.svg b/src/asset/tag/other_check.svg new file mode 100644 index 0000000..8f3f5ad --- /dev/null +++ b/src/asset/tag/other_check.svg @@ -0,0 +1,23 @@ + + + 杂项(选中) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/red_like.png b/src/asset/tag/red_like.png new file mode 100644 index 0000000..ee015f1 Binary files /dev/null and b/src/asset/tag/red_like.png differ diff --git a/src/asset/tag/red_like_check.png b/src/asset/tag/red_like_check.png new file mode 100644 index 0000000..22a7ccf Binary files /dev/null and b/src/asset/tag/red_like_check.png differ diff --git a/src/asset/tag/text.svg b/src/asset/tag/text.svg new file mode 100644 index 0000000..cd5e891 --- /dev/null +++ b/src/asset/tag/text.svg @@ -0,0 +1,26 @@ + + + 文字处理(未选中) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/asset/tag/text_check.svg b/src/asset/tag/text_check.svg new file mode 100644 index 0000000..7431db7 --- /dev/null +++ b/src/asset/tag/text_check.svg @@ -0,0 +1,26 @@ + + + 文字处理(选中) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index 942405d..682f820 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -105,7 +105,7 @@ const ButtonStyle: { [key in ButtonType]: object } = { }, }, highlight: { - background: 'linear-gradient(270deg, #11AF60 0%, #52C41A 100%)', + background: 'linear-gradient(270deg, #11AF60 0%, rgba(52, 90, 255, 1) 100%)', boxShadow: '0px 10px 30px 0px rgba(82,196,26,0.2)', color: '#fff', }, diff --git a/src/components/LikeIcon/index.tsx b/src/components/LikeIcon/index.tsx new file mode 100644 index 0000000..fed025d --- /dev/null +++ b/src/components/LikeIcon/index.tsx @@ -0,0 +1,50 @@ +import RedTag from '@/asset/tag/red_like.png'; +import RedTagCheck from '@/asset/tag/red_like_check.png'; +import HoverRedTagCheck from '@/asset/tag/hover_red_like.svg'; +import { LikeContext } from '@/hooks/useLikeList'; +import React, { useContext, useMemo } from 'react'; +import { Box } from '@mui/material'; + +const LikeIcon: React.FC<{ path: string, style: React.CSSProperties }> = (props) => { + const { path, style = {} } = props + const { likeList, updateLikeList } = useContext(LikeContext) + + const hanldeLike = (event: React.MouseEvent, path: string) => { + event.preventDefault() + if (typeof window !== 'undefined' && window.localStorage) { + try { + let newLike = [] + if (likeList?.includes(path)) { + newLike = likeList.filter(item => item !== path) + } else { + newLike = [...(likeList || []), path] + } + updateLikeList(newLike) + } finally { + console.log('Get like list error') + } + } + } + const isCheck = useMemo(() => { + return likeList?.includes(path) + }, [likeList, path]) + + return ( + hanldeLike(event, path)}/> + ); +}; + +export default LikeIcon; diff --git a/src/components/MenuView/components.tsx b/src/components/MenuView/components.tsx deleted file mode 100644 index 966894d..0000000 --- a/src/components/MenuView/components.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { defaultText, grayBg, grayBorder, grayText } from '@/constant'; -import { Box } from '@mui/material'; -import { styled } from '@mui/material/styles'; - -export const MenuPage = styled(Box)(() => ({ - display: 'flex', - height: '100% ', - backgroundColor: grayBg, - borderTop: `1px solid ${grayBorder}`, - paddingBottom: '16px', - paddingRight: '16px', - '& .Mui-selected': { - backgroundColor: 'rgba(0, 0, 0, 0.04)!important', - }, -})); - -export const Main = styled('div')(() => ({ - flex: 1, - padding: '24px', - paddingBottom: '0', - overflowY: 'auto', - display: 'flex', - flexDirection: 'column', - gap: '24px', - '& code, & code *': { - fontFamily: 'Mono', - }, - '& code .linenumber': { - backgroundColor: 'rgba(82, 196, 26, 0.08)', - marginRight: '24px', - paddingRight: '5px!important', - minWidth: '2em!important', - }, - '& pre': { - borderRadius: '4px', - minHeight: '150px', - }, -})); - -export const SideMenu = styled('div')(() => ({ - display: 'flex', - flexDirection: 'column', - alignItems: 'stretch', - padding: '16px 16px 0 16px', - gap: '8px', - height: '100%', -})); - -export const SubMenu = styled('div', { - shouldForwardProp: (prop) => prop !== 'expand' && prop !== 'height', -})<{ expand: string; height: number | string }>(({ expand, height }) => ({ - display: 'flex', - flexDirection: 'column', - alignItems: 'stretch', - gap: '12px', - height: 0, - transition: 'height 0.2s linear', - overflow: 'hidden', - ...(expand === 'true' && { - height: height ?? 'auto', - transition: 'height 0.2s linear', - }), -})); - -export const Breadcrumbs = styled('div')(() => ({ - display: 'flex', - alignItems: 'center', - width: '100%', - gap: '4px', - marginTop: '16px', -})); - -export const BreadcrumbsItem = styled(Box)(() => ({ - color: grayText, - cursor: 'pointer', - '&:hover': { - color: defaultText, - }, - fontSize: '14px', -})); - -export const ToolsIcon = styled(Box)(() => ({ - width: '60px', - height: '60px', - background: 'linear-gradient(180deg, #230970 0%, #1C0254 100%)', - borderRadius: '8px', -})); - -export const Tag = styled('div', { - shouldForwardProp: (prop) => prop !== 'highlight', -})<{ highlight: 'true' | 'false' }>(({ highlight }) => ({ - display: 'inline-block', - width: '24px', - lineHeight: '14px', - fontSize: '10px', - borderRadius: '4px', - textAlign: 'center', - color: '#fff', - backgroundColor: '#FF665D', - ...(highlight === 'true' && { color: '#FF665D', backgroundColor: '#fff' }), -})); - -export const ScrollY = styled('div')(() => ({ - height: '100%', - overflow: 'auto', - ...(navigator?.userAgent?.indexOf('Chrome') != -1 && { overflow: 'overlay' }), -})); - -export const SubTitle = styled('div')(() => ({ - height: '20px', - padding: '12px', - background: 'rgba(30, 111, 255, 0.1)', - borderRadius: '4px', -})); - -export const Container = styled(Box)(() => ({ - background: '#fff', - borderRadius: '4px', - boxShadow: '0px 4px 10px 0px rgba(145,158,171,0.1)', - overflow: 'auto', - minHeight: '100%', - '& > *': { - '@media(min-width: 1520px)': { - marginLeft: 'auto', - marginRight: 'auto', - }, - }, - '& > .MuiGrid-container': { - '@media(min-width: 1520px)': { - marginLeft: 'auto!important', - marginRight: 'auto!important', - '& > .MuiGrid-item:first-of-type': { - paddingLeft: '0!important', - }, - }, - }, -})); diff --git a/src/components/MenuView/index.tsx b/src/components/MenuView/index.tsx index 74758ed..b4f65ee 100644 --- a/src/components/MenuView/index.tsx +++ b/src/components/MenuView/index.tsx @@ -1,25 +1,14 @@ -import { defaultTextClick, secondaryClick } from '@/constant'; +import { grayText } from '@/constant'; import { usePath } from '@/hooks'; -import { AllTags, Tags, Tool, routesMenu } from '@/utils/tools'; -import ErrorIcon from '@mui/icons-material/Error'; -import SearchIcon from '@mui/icons-material/Search'; +import { allTags } from '@/utils/tags'; +import { Tool, allTools } from '@/utils/tools'; import { Box, - Button, - Grid, - IconButton, - InputBase, - ListItemText, Paper, - Typography, + Stack, + Typography } from '@mui/material'; -import List from '@mui/material/List'; -import ListItemButton from '@mui/material/ListItemButton'; -import Head from 'next/head'; -import { useRouter } from 'next/router'; -import React, { useEffect, useMemo } from 'react'; -import { Container, Main, MenuPage, SideMenu } from './components'; -import Link from 'next/link'; +import React from 'react'; export interface MenuProps { children: React.ReactElement; @@ -31,224 +20,41 @@ const ifChecked = (currentPath: string, itemPath: string) => { const MenuView: React.FC = ({ children }) => { const { path } = usePath(); - const [tags, setTags] = React.useState([]); - const [tools, setTools] = React.useState(routesMenu); - const [searchText, setSearchText] = React.useState(''); - const [openStaus, setOpenStatus] = React.useState( - routesMenu.map((item) => true) - ); - const router = useRouter(); - - const currentItem = useMemo(() => { - const _item = routesMenu.find((item) => item.path === path); - if (_item) return _item; - }, [path]); - const checkTags = (tag: Tags) => { - const _index = tags.findIndex((item) => item === tag); - const _tags = [...tags]; - if (_index >= 0) { - _tags.splice(_index, 1); - } else { - _tags.push(tag); - } - setTags(_tags); - }; - - useEffect(() => { - let toolsFilter: Tool[] = []; - if (tags.length) - toolsFilter = routesMenu.filter((item) => - item.tags.some((tag) => tags.includes(tag)) - ); - else toolsFilter = routesMenu; - setTools( - toolsFilter.filter((item) => { - return ( - item.label.toUpperCase().includes(searchText?.toUpperCase()) || - item.subTitle.toUpperCase().includes(searchText?.toUpperCase()) - ); - }) - ); - }, [tags, searchText]); + const [tool] = React.useState(allTools.find(item => item.path === path)); return ( - <> - - {currentItem?.label + ' - 长亭百川云工具库'} - - - - - + + + - - - - - setSearchText(event.target.value)} - placeholder='输入关键词搜索工具' - inputProps={{ 'aria-label': 'search icons' }} - sx={{ grow: 1, fontSize: '12px' }} - /> - - - {AllTags.map((item) => ( - - ))} - - - - {tools.map((item, index) => ( - - - - - - ))} - - - -
- - - - - - - {currentItem?.label} - - - - {currentItem?.subTitle ? ( - - - {currentItem?.subTitle} - - ) : null} - - - - - {children} - -
-
- + {tool?.label} + +
+ + {allTags.filter(tag => tool?.tags.includes(tag.name)).map(tag => {tag.label})} + + + {tool?.subTitle} + {children} + ); }; diff --git a/src/components/ToolCard/index.tsx b/src/components/ToolCard/index.tsx new file mode 100644 index 0000000..6aac4bd --- /dev/null +++ b/src/components/ToolCard/index.tsx @@ -0,0 +1,34 @@ +import { Tag } from "@/utils/tags" +import { Tool } from "@/utils/tools" +import { Avatar, CardHeader, Typography } from "@mui/material" +import LikeIcon from "../LikeIcon" +import { grayText2 } from "@/constant" + +export const ToolCard = (props: { tool: Tool, tag?: Tag, showStar?: boolean }) => { + const { tool, tag, showStar = true } = props + return ( + <> + + {tool.label[0]} + + } + action={ + + } + title={tool.label} + /> + {tool.subTitle} + + ) +} \ No newline at end of file diff --git a/src/components/Tools/home.tsx b/src/components/Tools/home.tsx deleted file mode 100644 index 3b7da73..0000000 --- a/src/components/Tools/home.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { - Autocomplete, - Box, - Button, - IconButton, - Paper, - Stack, - TextField, - Typography, -} from '@mui/material'; - -import { primary } from '@/constant'; -import { AllTags, routesMenu, type Tool } from '@/utils/tools'; -import SearchIcon from '@mui/icons-material/Search'; -import Link from 'next/link'; -import { useRouter } from 'next/router'; -import { useState } from 'react'; - -export default function App() { - const router = useRouter(); - - // const [searchText, setSearchText] = useState('') - const [tools] = useState(routesMenu); - const [tags] = useState<(typeof AllTags)[number][]>(AllTags); - - return ( - - - - - X - - TOOLS - - - - menu.filter((item) => { - return ( - item.label - .toUpperCase() - .includes(state.inputValue?.toUpperCase()) || - item.subTitle - .toUpperCase() - .includes(state.inputValue?.toUpperCase()) - ); - }) - } - onInputChange={(v: any) => { - setTimeout(() => { - const item = routesMenu.find((item) => - [v.target.textContent, v.target.value].includes(item.label) - ); - if (item) { - router.push(item.path); - } - }); - }} - options={routesMenu} - sx={{ height: '100%' }} - renderInput={(params) => ( - - - - - - - )} - /> - - - - {tags.map((section) => [ - - - {section.label} - , - - {tools - .filter((tool) => tool.tags.includes(section.name)) - .map((item) => ( - - - - ))} - , - ])} - - - ); -} diff --git a/src/components/Tools/index.tsx b/src/components/Tools/index.tsx index 3c4b0eb..2934244 100644 --- a/src/components/Tools/index.tsx +++ b/src/components/Tools/index.tsx @@ -18,7 +18,7 @@ export const ToolsForm = styled(Box)(() => ({ pt: '10px', display: 'flex', flexDirection: 'column', - width: '838px', + margin: 'auto', gap: 16, '& .MuiOutlinedInput-root': { @@ -35,7 +35,6 @@ export const ToolsForm = styled(Box)(() => ({ width: '155px', color: defaultTextClick, fontSize: '14px', - maxWidth: '838px', }, '& .MuiOutlinedInput-notchedOutline': { borderColor: '#E3E8EF', diff --git a/src/constant/color.ts b/src/constant/color.ts index 404b5c6..bda3d69 100644 --- a/src/constant/color.ts +++ b/src/constant/color.ts @@ -1,4 +1,4 @@ -export const primary = '#52C41A'; +export const primary = 'rgba(52, 90, 255, 1)'; export const primaryHover = '#73D13C'; export const primaryClick = '#389E0E'; @@ -10,7 +10,7 @@ export const secondary = '#ECF9E6'; export const secondaryHover = '#F3FDEE'; export const secondaryClick = '#DAE8D4'; -export const special = 'linear-gradient(270deg, #11AF60 0%, #52C41A 100%)'; +export const special = 'linear-gradient(270deg, #11AF60 0%, rgba(52, 90, 255, 1) 100%)'; export const dangerHover = 'linear-gradient(225deg, #FF1F1F 0%, #F78900 100%)'; export const disabled = '#F7F7F7'; @@ -31,12 +31,12 @@ export const leastTextClick = '#737373'; export const disabledText = '#BFBFBF'; export const specialText = '#737971'; -export const success = '#52C41A'; +export const success = 'rgba(52, 90, 255, 1)'; export const warning = '#FFBF00'; export const error = '#FF1F1F'; -export const grayText = '#0B310440'; -export const grayText2 = '#97A4B0'; +export const grayText = '#0B2562'; +export const grayText2 = 'rgba(11,37,98,0.5)'; export const errorText = '#FF1F1F'; export const infoText = '#2F7CE9'; diff --git a/src/hooks/useAnchor.tsx b/src/hooks/useAnchor.tsx new file mode 100644 index 0000000..13bc791 --- /dev/null +++ b/src/hooks/useAnchor.tsx @@ -0,0 +1,22 @@ +import { useLocalStorageState } from 'ahooks'; +import { createContext, useState } from "react"; + +export interface Anchor { + anchor: string; + updateAnchor: (value: string) => void; +} + +export const AnchorContext = createContext(null as any); + +export const AnchorContextProvider = ({ children }: any) => { + const [anchor, setAnchor] = useState(''); + const updateAnchor = (newValue: string) => { + setAnchor(newValue) + }; + const contextValue = { + anchor, + updateAnchor, + }; + + return {children}; +}; \ No newline at end of file diff --git a/src/hooks/useLikeList.tsx b/src/hooks/useLikeList.tsx new file mode 100644 index 0000000..a195037 --- /dev/null +++ b/src/hooks/useLikeList.tsx @@ -0,0 +1,24 @@ +import { useLocalStorageState } from 'ahooks'; +import { createContext, useState } from "react"; + +export interface Like { + likeList: string[]; + updateLikeList: (value: string[]) => void; +} + +export const LikeContext = createContext(null as any); + +export const LikeContextProvider = ({ children }: any) => { + const [localList = [], setLocalList] = useLocalStorageState('like_list', { defaultValue: [] }) + const [likeList, setValue] = useState(localList); + const updateLikeList = (newValue: string[]) => { + setValue(newValue); + setLocalList(newValue) + }; + const contextValue = { + likeList, + updateLikeList, + }; + + return {children}; +}; \ No newline at end of file diff --git a/src/icon/Copy.tsx b/src/icon/Copy.tsx index 355e8e5..78820d2 100644 --- a/src/icon/Copy.tsx +++ b/src/icon/Copy.tsx @@ -10,7 +10,7 @@ export const Copy = (props: SvgIconProps) => { ({ + background: 'linear-gradient(180deg, rgba(255, 182, 85, 0.2) 0%, rgba(255, 58, 116, 0.2) 100%)', + filter: 'blur(1px)', + width: '14px', + height: '14px', + borderRadius: '50%', + justifyContent: 'center', + alignItems: 'center', + marginLeft: 'auto', +})); +export const IssueIcon = styled(Box)(() => ({ + width: '8px', + height: '8px', + borderRadius: '50%', + background: 'linear-gradient(141deg, #FFB655 0%, #FF3A74 100%)', +})); diff --git a/src/layouts/Header/index.tsx b/src/layouts/Header/index.tsx new file mode 100644 index 0000000..b066be4 --- /dev/null +++ b/src/layouts/Header/index.tsx @@ -0,0 +1,151 @@ +import { usePath } from '@/hooks'; +import { Tags } from '@/utils/tags'; +import { Tool, allTools } from '@/utils/tools'; +import GitHubIcon from '@mui/icons-material/GitHub'; +import SearchIcon from '@mui/icons-material/Search'; +import { + Autocomplete, + Box, + Paper, + Stack, + TextField, + Typography +} from '@mui/material'; +import Link from 'next/link'; +import { useRouter } from 'next/router'; +import React, { useEffect, useMemo } from 'react'; +import { IssueIcon, IssueIconWrap } from './components'; +import { ToolCard } from '@/components/ToolCard'; + +const ifChecked = (currentPath: string, itemPath: string) => { + return currentPath === itemPath; +}; + +const Header: React.FC<{}> = () => { + const { path } = usePath(); + const [tags, setTags] = React.useState([]); + const [tools, setTools] = React.useState(allTools); + const [openSearch, setOpenSearch] = React.useState(false); + const [searchText, setSearchText] = React.useState(''); + + const [openStaus, setOpenStatus] = React.useState( + allTools.map((item) => true) + ); + const router = useRouter(); + + const currentItem = useMemo(() => { + const _item = allTools.find((item) => item.path === path); + if (_item) return _item; + }, [path]); + const checkTags = (tag: Tags) => { + const _index = tags.findIndex((item) => item === tag); + const _tags = [...tags]; + if (_index >= 0) { + _tags.splice(_index, 1); + } else { + _tags.push(tag); + } + setTags(_tags); + }; + + useEffect(() => { + let toolsFilter: Tool[] = []; + if (tags.length) + toolsFilter = allTools.filter((item) => + item.tags.some((tag) => tags.includes(tag)) + ); + else toolsFilter = allTools; + setTools( + toolsFilter.filter((item) => { + return ( + item.label.toUpperCase().includes(searchText?.toUpperCase()) || + item.subTitle.toUpperCase().includes(searchText?.toUpperCase()) + ); + }) + ); + }, [tags, searchText]); + + return ( + + + 百川在线工具箱 + + + + + + 工具不够?提个需求 + + + setOpenSearch(false)} + onOpen={() => setOpenSearch(true)} + filterOptions={(menu, state) => + menu.filter((item) => { + return ( + item.label + .toUpperCase() + .includes(state.inputValue?.toUpperCase()) || + item.subTitle + .toUpperCase() + .includes(state.inputValue?.toUpperCase()) + ); + }) + } + onInputChange={(v: any) => { + const item = allTools.find((item) => + [v.target.textContent, v.target.value].includes(item.label) + ); + }} + options={allTools} + renderOption={(props: object, option: Tool, state: object, ownerState: object) => ( + + setOpenSearch(false)}> + + + + )} + sx={{ height: '100%' }} + renderInput={(params) => ( + + + + + )} + /> + + + ); +}; + +export default Header; diff --git a/src/layouts/LoginLayout/copyright.tsx b/src/layouts/LoginLayout/copyright.tsx deleted file mode 100644 index b4d0468..0000000 --- a/src/layouts/LoginLayout/copyright.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useCallback } from 'react'; -import emblem from '@/asset/img/emblem.webp'; -import { ImageIcon } from '@/icon'; -import { Stack, SxProps } from '@mui/material'; -import Text from '@/components/Text'; -import { useMobileView } from '@/hooks'; -import { Side_Margin } from '@/styles/colors'; - -const Copyright: React.FC<{ sx?: SxProps; fontColor?: string }> = ({ - sx, - fontColor, -}) => { - const isMobile = useMobileView(); - - const openBeianLink = useCallback(() => { - window.open('https://beian.miit.gov.cn/#/Integrated/recordQuery', '_blank'); - }, []); - - return ( - - - Copyright ©2022 北京长亭未来科技有限公司 - - a': { color: 'inherit' }, - }} - onClick={openBeianLink} - > - - 京ICP备 19035216号-1 - - - ); -}; - -export default Copyright; diff --git a/src/layouts/LoginLayout/index.tsx b/src/layouts/LoginLayout/index.tsx deleted file mode 100644 index 9b1a5a8..0000000 --- a/src/layouts/LoginLayout/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import Navbar from '@/layouts/Navbar'; -import { Stack } from '@mui/material'; -import Copyright from './copyright'; -import { useMobileView } from '@/hooks'; -import banner1 from '@/asset/img/banner1.webp'; -import mobile_login from '@/asset/img/mobile_login.webp'; -import Image from 'next/image'; - -const LoginLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const isMobile = useMobileView(); - return ( - <> - - - background - {children} - - - - ); -}; - -export default LoginLayout; diff --git a/src/layouts/Logo/index.tsx b/src/layouts/Logo/index.tsx deleted file mode 100644 index 60234b5..0000000 --- a/src/layouts/Logo/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useCallback } from 'react'; -import { styled } from '@mui/material/styles'; -import { useNavigateParams } from '@/hooks'; -import { Logo as CompanyLogo } from '@/icon'; -import Text from '@/components/Text'; -import { useMobileView } from '@/hooks'; -import { primary, defaultText } from '@/styles/colors'; - -const LogoComponent = styled('a')(({ theme }) => ({ - display: 'flex', - userSelect: 'none', - margin: '8px', - marginRight: '60px', - fontSize: '20px', - cursor: 'pointer', - [theme.breakpoints.down('sm')]: { - margin: '24px 8px', - marginRight: '0px', - }, -})); - -const Logo: React.FC = () => { - const navigate = useNavigateParams(); - const isMobile = useMobileView(); - - const toHome = useCallback(() => navigate('/'), [navigate]); - - return ( - - - - 长亭百川云平台 - - - ); -}; - -export default Logo; diff --git a/src/layouts/Navbar/components.tsx b/src/layouts/Navbar/components.tsx deleted file mode 100644 index 6a7f8d1..0000000 --- a/src/layouts/Navbar/components.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import { styled } from '@mui/material/styles'; - -export interface BarProps { - children: React.ReactNode; - view?: 'default' | 'login'; - id?: string; -} - -const BarBase = styled('div', { - shouldForwardProp: (prop) => prop !== 'view', -})<{ view?: 'default' | 'login' }>(({ theme, view = 'default' }) => ({ - width: '100%', - position: 'fixed', - top: '0px', - zIndex: theme.zIndex.drawer + 3, - // boxShadow: theme.shadows[4], - backgroundColor: view === 'default' ? 'rgba(255,255,255,0.6)' : 'transparent', - backdropFilter: 'blur(4px)', - [theme.breakpoints.down('sm')]: { - zIndex: theme.zIndex.drawer - 1, - }, -})); -const BarContent = styled('div')(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - height: '64px', - justifyContent: 'flex-start', - margin: `0 28px`, - marginRight: '0', - [theme.breakpoints.down('sm')]: { marginLeft: '12px' }, -})); - -const Bar: React.FC = (props) => { - const { children, view, id } = props; - return ( - - {children} - - ); -}; - -export default Bar; diff --git a/src/layouts/Navbar/index.tsx b/src/layouts/Navbar/index.tsx deleted file mode 100644 index b81f207..0000000 --- a/src/layouts/Navbar/index.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import Bar from './components'; -import Logo from '@/layouts/Logo'; -import NavMenu from './items/menu'; -import { ApplicationInfo } from '@/types'; - -interface NavBarMenuData { - apps: ApplicationInfo[]; -} - -const renderNavContent = (menuData: ApplicationInfo[]) => { - return [, ]; -}; - -const DefaultNavbar: React.FC = ({ apps }) => { - return ( - - {renderNavContent(apps)} - - ); -}; - -const LoginNavbar: React.FC<{}> = () => { - return ( - - - - ); -}; - -const Navbar = { default: DefaultNavbar, login: LoginNavbar }; - -export default Navbar; diff --git a/src/layouts/Navbar/items/menu/index.tsx b/src/layouts/Navbar/items/menu/index.tsx deleted file mode 100644 index 8ec0e2b..0000000 --- a/src/layouts/Navbar/items/menu/index.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { useMobileView } from '@/hooks'; -import { ApplicationInfo, NavMenuProps } from '@/types'; -import DehazeIcon from '@mui/icons-material/Dehaze'; -import { styled } from '@mui/material/styles'; -import { useRouter } from 'next/router'; -import React, { useCallback, useState } from 'react'; -import MenuItem from './item'; -import MobilPanel from './mobileView'; -import NavPanel from './panel'; - -const NavMenuComponent = styled('div')(() => ({ - display: 'flex', - gap: '40px', -})); - -const menu: NavMenuProps[] = [ - { title: '最新活动', link: '/' }, - { title: '产品', expand: true }, -]; - -const NavMenu: React.FC<{ apps: ApplicationInfo[] }> = ({ apps }) => { - const isMobile = useMobileView(); - const router = useRouter(); - const [panelContent, setPanelContent] = useState(); - const [openMobile, setOpenMobile] = useState(false); - const [openPanel, setOpenPanel] = useState(false); - - const handleOpen = useCallback( - (open: boolean) => () => { - setOpenMobile(open); - }, - [] - ); - - const handleClickMenu = useCallback( - (link?: string) => () => { - if (link) { - router.push(link); - } - }, - [router] - ); - - const handleShowPanel = useCallback( - (menuContent: NavMenuProps) => () => { - if (menuContent?.expand) setOpenPanel(true); - }, - [] - ); - - const handleLeavePanel = useCallback(() => { - setOpenPanel(false); - }, []); - - return ( - <> - {isMobile ? ( - - ) : ( - - {menu?.map((menuitem, index) => ( - - ))} - - )} - - {isMobile && ( - - )} - - ); -}; - -export default NavMenu; diff --git a/src/layouts/Navbar/items/menu/item.tsx b/src/layouts/Navbar/items/menu/item.tsx deleted file mode 100644 index 92a9b27..0000000 --- a/src/layouts/Navbar/items/menu/item.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { ArrowDown } from '@/icon'; -import { defaultText, primary, secondaryText } from '@/styles/colors'; -import { styled, SxProps } from '@mui/material/styles'; -import React from 'react'; - -export interface MenuItemProps { - title: string | React.ReactElement; - subTitle?: string; - highlight?: string | boolean; - hoverview?: string | boolean; - expand?: string | boolean; - sx?: SxProps; - icon?: React.ReactElement; - onClick?: () => void; - onHover?: () => void; - onLeave?: () => void; -} - -const MenuItemComponent = styled('div', { - shouldForwardProp: (prop) => prop !== 'hightlight' && prop !== 'expand', -})>( - ({ highlight, hoverview }) => ({ - fontSize: 16, - fontWeight: 'bold', - color: highlight === 'true' ? primary : defaultText, - lineHeight: '22px', - ...(hoverview === 'true' && { - transition: 'color 0.1s linear', - }), - transition: 'color 0.1s linear', - display: 'flex', - alignItems: 'center', - - '& > svg': { - display: 'inline-block', - marginLeft: '8px', - transformOrigin: 'center', - transition: 'transform 0.1s linear', - }, - '&:hover': { - ...(hoverview === 'true' && { - color: primary, - transition: 'color 0.1s linear', - }), - '& > svg': { - transform: 'rotate(0.5turn)', - transition: 'transform 0.1s linear', - }, - }, - }) -); - -const MenuItemContent = styled('div')(() => ({ - fontSize: 'inherit', - color: secondaryText, -})); - -const MenuItemWrapper = styled('div')(() => ({ - display: 'flex', - flexDirection: 'column', - cursor: 'pointer', -})); - -const MenuItem: React.FC = (props) => { - const { - title, - subTitle, - highlight, - expand, - sx, - onClick, - onHover, - onLeave, - hoverview, - icon, - } = props; - return ( - - - {icon} - {title} - {expand && } - - {subTitle && {subTitle}} - - ); -}; - -export default MenuItem; diff --git a/src/layouts/Navbar/items/menu/mobileView.tsx b/src/layouts/Navbar/items/menu/mobileView.tsx deleted file mode 100644 index a93400b..0000000 --- a/src/layouts/Navbar/items/menu/mobileView.tsx +++ /dev/null @@ -1,166 +0,0 @@ -import search_icon from '@/asset/img/search_icon.webp'; -import { useDebounce } from '@/hooks'; -import { Side_Margin, grayLight, primary } from '@/styles/colors'; -import { ApplicationInfo } from '@/types'; -import ClearIcon from '@mui/icons-material/Clear'; -import { Drawer, TextField } from '@mui/material'; -import { styled } from '@mui/material/styles'; -import Image, { StaticImageData } from 'next/image'; -import { useRouter } from 'next/router'; -import React, { useCallback, useEffect, useState } from 'react'; -import MenuItem from './item'; -import { NavPanelProps } from './panel'; - -const PanelContent = styled('div')(() => ({ - display: 'flex', - flexDirection: 'column', - width: '100%', - padding: `${parseInt(Side_Margin) / 2}% ${Side_Margin}`, - justifyContent: 'flex-start', - gap: 20, -})); - -const Content = styled('div')(() => ({ - display: 'flex', - gap: 40, -})); - -const Header = styled('div')(() => ({ - display: 'flex', - justifyContent: 'flex-end', - alignItems: 'center', -})); - -const ContentCol = styled('div')(() => ({ - display: 'flex', - flexDirection: 'column', - height: '100%', - justifyContent: 'flex-start', - alignItems: 'stretch', - gap: 20, -})); - -export const Icon = ({ - style, - src, -}: { - style?: React.CSSProperties; - src: string | StaticImageData; -}) => { - return ( - search - ); -}; - -const ContentCard = styled('div')(() => ({ - padding: '12px 24px', - transition: 'background-color 0.2s linear', - '&:hover': { - backgroundColor: grayLight, - transition: 'background-color 0.2s linear', - }, -})); - -const NavPanel: React.FC = (props) => { - const { open, onClose, apps } = props; - const [menuContent, setMenuContent] = useState([]); - const [input, setInput] = useState(''); - const debounceInput = useDebounce(input, 500); - const router = useRouter(); - - const handleSearch = useCallback( - (search: string) => { - if (apps && search.length > 0) { - const data = JSON.parse(JSON.stringify(apps)) as ApplicationInfo[]; - const searchResult = data?.filter((item) => item.name.includes(search)); - setMenuContent([...searchResult]); - } else { - setMenuContent(apps); - } - }, - [apps] - ); - - const toWorkbench = useCallback( - (app: ApplicationInfo) => () => { - if (app?.scope) { - router.push(`/landing/${app?.scope}`); - } else { - router.push('/workbench'); - } - onClose?.(); - }, - [onClose, router] - ); - - const handleSetInput = useCallback((event: React.ChangeEvent) => { - const target = event.target as HTMLInputElement; - const input = target?.value?.trim(); - setInput(input ?? ''); - }, []); - - useEffect(() => { - handleSearch(debounceInput); - }, [debounceInput, handleSearch]); - - return ( - - -
- -
- - ), - }} - /> - - - {menuContent?.map((contentItem, index) => ( - - - - ))} - - -
-
- ); -}; - -export default NavPanel; diff --git a/src/layouts/Navbar/items/menu/panel.tsx b/src/layouts/Navbar/items/menu/panel.tsx deleted file mode 100644 index 3296f8c..0000000 --- a/src/layouts/Navbar/items/menu/panel.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import search_icon from '@/asset/img/search_icon.webp'; -import { useDebounce } from '@/hooks'; -import { Side_Margin, grayLight } from '@/styles/colors'; -import { ApplicationInfo } from '@/types'; -import { Drawer, TextField } from '@mui/material'; -import { styled } from '@mui/material/styles'; -import Image, { StaticImageData } from 'next/image'; -import { useRouter } from 'next/router'; -import React, { useCallback, useEffect, useState } from 'react'; -import MenuItem from './item'; - -export interface NavPanelProps { - open: boolean; - onClose?: () => void; - apps: ApplicationInfo[]; -} - -const PanelContent = styled('div')(() => ({ - display: 'flex', - flexDirection: 'column', - width: '100%', - padding: `${parseInt(Side_Margin) / 1.5}% ${Side_Margin}`, - paddingTop: `calc(${parseInt(Side_Margin) / 4}% + 64px)`, - justifyContent: 'flex-start', - gap: 20, -})); - -const Content = styled('div')(() => ({ - display: 'flex', - gap: 40, - flexWrap: 'wrap', - alignItems: 'stretch', -})); - -export const Icon = ({ - style, - src, -}: { - style?: React.CSSProperties; - src: string | StaticImageData; -}) => { - return ( - search - ); -}; - -const ContentCard = styled('div')(() => ({ - flex: '0 0 220px', - padding: '12px 24px', - transition: 'background-color 0.2s linear', - '&:hover': { - backgroundColor: grayLight, - transition: 'background-color 0.2s linear', - }, -})); - -const NavPanel: React.FC = (props) => { - const { open, onClose, apps } = props; - const [menuContent, setMenuContent] = useState(apps ?? []); - const [input, setInput] = useState(''); - const router = useRouter(); - const debounceInput = useDebounce(input, 500); - - const handleSearch = useCallback( - (search: string) => { - if (apps && search.length > 0) { - const data = JSON.parse(JSON.stringify(apps)) as ApplicationInfo[]; - const searchResult = data?.filter((item) => item.name.includes(search)); - setMenuContent([...searchResult]); - } else { - setMenuContent(apps); - } - }, - [apps] - ); - - const handleSetInput = useCallback((event: React.ChangeEvent) => { - const target = event.target as HTMLInputElement; - const input = target?.value?.trim(); - setInput(input ?? ''); - }, []); - - const toWorkbench = useCallback( - (app: ApplicationInfo) => () => { - if (app?.scope) { - router.push(`/landing/${app?.scope}`); - } else { - router.push('/workbench'); - } - onClose?.(); - }, - [router, onClose] - ); - - useEffect(() => { - handleSearch(debounceInput); - }, [debounceInput, handleSearch]); - - return ( - - - - ), - }} - /> - - {menuContent?.map((item, index) => ( - - - - ))} - - - - ); -}; - -export default NavPanel; diff --git a/src/layouts/SideBar/components.tsx b/src/layouts/SideBar/components.tsx new file mode 100644 index 0000000..0ab79a7 --- /dev/null +++ b/src/layouts/SideBar/components.tsx @@ -0,0 +1,20 @@ +import { defaultText, grayBg, grayBorder, grayText } from '@/constant'; +import { Box, Stack } from '@mui/material'; +import { styled } from '@mui/material/styles'; + +export const IssueIconWrap = styled(Stack)(() => ({ + background: 'linear-gradient(180deg, rgba(255, 182, 85, 0.2) 0%, rgba(255, 58, 116, 0.2) 100%)', + filter: 'blur(1px)', + width: '14px', + height: '14px', + borderRadius: '50%', + justifyContent: 'center', + alignItems: 'center', + marginLeft: 'auto', +})); +export const IssueIcon = styled(Box)(() => ({ + width: '8px', + height: '8px', + borderRadius: '50%', + background: 'linear-gradient(141deg, #FFB655 0%, #FF3A74 100%)', +})); diff --git a/src/layouts/SideBar/index.tsx b/src/layouts/SideBar/index.tsx new file mode 100644 index 0000000..5dc185d --- /dev/null +++ b/src/layouts/SideBar/index.tsx @@ -0,0 +1,47 @@ +import { grayText } from '@/constant/color'; +import { AnchorContext } from '@/hooks/useAnchor'; +import { allTags } from '@/utils/tags'; +import { + Box, + Button, + Link, + Paper, + Stack, + Typography +} from '@mui/material'; +import Image from 'next/image'; +import React, { useContext, useEffect, useState } from 'react'; + +const SideBar: React.FC<{}> = () => { + const { anchor } = useContext(AnchorContext) + const [checkAnchor, setChectAnchor] = useState('') + useEffect(() => { + setChectAnchor(anchor) + }, [anchor]) + return ( + + + {allTags.map(item => + setChectAnchor(item.name)} href={'/tools#' + item.name} sx={{ alignSelf: 'stretch' }} className='custom-link'> + + + )} + + + ); +}; + +export default SideBar; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index d6e40c1..320eec4 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -7,11 +7,19 @@ import '@/styles/static/swiper.css'; import theme from '@/styles/theme'; import createEmotionCache from '@/utils/emotionCache'; import { CacheProvider } from '@emotion/react'; -import { ThemeProvider } from '@mui/material'; +import { Box, Stack, ThemeProvider } from '@mui/material'; import CssBaseline from '@mui/material/CssBaseline'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import PropTypes from 'prop-types'; +import Header from '@/layouts/Header'; +import SideBar from '@/layouts/SideBar'; +import { LikeContextProvider } from '@/hooks/useLikeList'; +import { AnchorContextProvider } from '@/hooks/useAnchor'; +import { usePath } from '@/hooks'; +import { useMemo } from 'react'; +import { allTools } from '@/utils/tools'; +import Head from 'next/head'; const clientSideEmotionCache = createEmotionCache(); @@ -19,24 +27,45 @@ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } }, }); + export default function App({ Component, emotionCache = clientSideEmotionCache, }: any) { + const { path } = usePath() + + const currentItem = useMemo(() => { + return allTools.find(item => item.path === path) + }, [path]) return ( <> - - - + + + + + + + + - - - - - + + + + +
+ + + + + + + + + + ); } diff --git a/src/pages/aes.tsx b/src/pages/aes.tsx index 02f3284..fc40a3a 100644 --- a/src/pages/aes.tsx +++ b/src/pages/aes.tsx @@ -101,7 +101,7 @@ const AES: React.FC = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/pages/ascii.tsx b/src/pages/ascii.tsx index b1f107e..f5f0bea 100644 --- a/src/pages/ascii.tsx +++ b/src/pages/ascii.tsx @@ -112,7 +112,7 @@ const Hash: React.FC = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/pages/base64.tsx b/src/pages/base64.tsx index c46dbdb..124bce2 100644 --- a/src/pages/base64.tsx +++ b/src/pages/base64.tsx @@ -66,9 +66,9 @@ const Base64: React.FC = () => { sx={{ mt: '24px', gap: '18px', - maxWidth: '1020px', + // maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/pages/case_convert.tsx b/src/pages/case_convert.tsx index 1c7f6cf..ed66d6c 100644 --- a/src/pages/case_convert.tsx +++ b/src/pages/case_convert.tsx @@ -16,7 +16,7 @@ const CaseConvert: React.FC = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/pages/hash.tsx b/src/pages/hash.tsx index 7ba6a68..4265836 100644 --- a/src/pages/hash.tsx +++ b/src/pages/hash.tsx @@ -85,7 +85,7 @@ const Hash: React.FC = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/pages/img2base64.tsx b/src/pages/img2base64.tsx index f8ed443..5796440 100644 --- a/src/pages/img2base64.tsx +++ b/src/pages/img2base64.tsx @@ -90,7 +90,7 @@ const ImgBase64: React.FC = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 35e8521..844f1fe 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,3 +1,99 @@ -import Home from '@/components/Tools/home'; +import { + Avatar, + Box, + Card, + CardHeader, + Paper, + Stack, + Typography +} from '@mui/material'; -export default Home; +import LikeIcon from '@/components/LikeIcon'; +import { grayText2 } from '@/constant'; +import { LikeContext } from '@/hooks/useLikeList'; +import { Tag, Tags, allTags } from '@/utils/tags'; +import { Tool, allTools } from '@/utils/tools'; +import Image from 'next/image'; +import Link from 'next/link'; +import { useContext, useEffect, useMemo, useRef } from 'react'; + +import NoLike from '@/asset/tag/no_like.png'; +import { AnchorContext } from '@/hooks/useAnchor'; +import { ToolCard } from '@/components/ToolCard'; + +export default function App() { + const { updateAnchor } = useContext(AnchorContext) + const { likeList } = useContext(LikeContext) + const mainPageRef = useRef(null) + + useEffect(() => { + const handleScroll = () => { + if (!mainPageRef.current?.children) return + const scrollPosition = mainPageRef?.current.scrollTop; + for (let liElement of mainPageRef?.current.children) { + const liTop = liElement.offsetTop; + const liBottom = liTop + liElement.offsetHeight; + + if (scrollPosition >= liTop - 100 && scrollPosition < liBottom - 100) { + const liId = liElement.getAttribute('id'); + updateAnchor(liId) + break; + } + } + }; + mainPageRef.current?.addEventListener('scroll', handleScroll); + return () => { + mainPageRef.current?.removeEventListener('scroll', handleScroll); + updateAnchor('') + }; + }, []); + + const tagAndTools = useMemo(() => { + const addLikeTagForTools = allTools.map(item => { + if (likeList?.includes(item.path)) return ({ ...item, tags: [...item.tags, Tags.LIKE] }) + return item + }) + return allTags.map(tag => { + (tag as any).tools = addLikeTagForTools.filter(tool => tool.tags.includes(tag.name)) + return tag + }) + }, [likeList]) + + return ( + + { + tagAndTools.map(tag => ( + + {tag.label} + + { + ((tag as any).tools.length ? + (tag as any).tools.map((tool: any) => ( + + + + + + )) : + no_like + ) + } + + + ) + ) + } + + ); +} diff --git a/src/pages/jsontocsv.tsx b/src/pages/jsontocsv.tsx index a07398b..5da4fde 100644 --- a/src/pages/jsontocsv.tsx +++ b/src/pages/jsontocsv.tsx @@ -136,7 +136,7 @@ const JSONToCSV = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', '.rc-table': { border: '1px solid #eee', diff --git a/src/pages/radix_convert.tsx b/src/pages/radix_convert.tsx index fb93db6..7ba05d0 100644 --- a/src/pages/radix_convert.tsx +++ b/src/pages/radix_convert.tsx @@ -146,7 +146,7 @@ const RadixConvert: React.FC = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/pages/random.tsx b/src/pages/random.tsx index 92683e2..eb3fb8f 100644 --- a/src/pages/random.tsx +++ b/src/pages/random.tsx @@ -107,8 +107,8 @@ const Random: React.FC = () => { return ( - - + +
diff --git a/src/pages/uncolor.tsx b/src/pages/uncolor.tsx index 55cc0e7..746578a 100644 --- a/src/pages/uncolor.tsx +++ b/src/pages/uncolor.tsx @@ -96,7 +96,7 @@ const ImgBase64: React.FC = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/pages/unix.tsx b/src/pages/unix.tsx index 192e5a7..9b77bbc 100644 --- a/src/pages/unix.tsx +++ b/src/pages/unix.tsx @@ -237,7 +237,7 @@ const Unix: React.FC = () => { > { diff --git a/src/pages/urlencoder.tsx b/src/pages/urlencoder.tsx index 6cc0733..837f1f0 100644 --- a/src/pages/urlencoder.tsx +++ b/src/pages/urlencoder.tsx @@ -63,7 +63,7 @@ const URLEncoder: React.FC = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/pages/word_count.tsx b/src/pages/word_count.tsx index 9233c37..87dce16 100644 --- a/src/pages/word_count.tsx +++ b/src/pages/word_count.tsx @@ -94,7 +94,7 @@ const WordCount: React.FC = () => { gap: '18px', maxWidth: '1020px', fontFamily: 'Mono', - width: '838px', + mx: 'auto', }} > diff --git a/src/styles/colors.ts b/src/styles/colors.ts index 56bac18..4ba72cc 100644 --- a/src/styles/colors.ts +++ b/src/styles/colors.ts @@ -1,4 +1,4 @@ -export const primary = '#52C41A'; +export const primary = 'rgba(52, 90, 255, 1)'; export const primaryHover = '#73D13C'; export const primaryClick = '#389E0E'; @@ -10,7 +10,7 @@ export const secondary = '#ECF9E6'; export const secondaryHover = '#F3FDEE'; export const secondaryClick = '#DAE8D4'; -export const special = 'linear-gradient(270deg, #11AF60 0%, #52C41A 100%)'; +export const special = 'linear-gradient(270deg, #11AF60 0%, rgba(52, 90, 255, 1) 100%)'; export const dangerHover = 'linear-gradient(225deg, #FF1F1F 0%, #F78900 100%)'; export const disabled = '#F7F7F7'; @@ -31,7 +31,7 @@ export const leastTextClick = '#737373'; export const disabledText = '#BFBFBF'; export const specialText = '#737971'; -export const success = '#52C41A'; +export const success = 'rgba(52, 90, 255, 1)'; export const warning = '#FFBF00'; export const error = '#FF1F1F'; diff --git a/src/styles/global.css b/src/styles/global.css index 2eac3a1..30a0b58 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -4,7 +4,7 @@ #SM_BTN_1 { width: auto !important; - background-color: #52c41a !important; + background-color: rgba(52, 90, 255, 1) !important; } .sm-txt { @@ -36,4 +36,12 @@ body, html { overflow: auto !important; + background: rgba(247, 249, 252, 1); } + +@font-face { + font-family: "Mono"; + font-weight: 250; + src: url('/Mono.ttf') format("truetype"); + font-display: swap; +} \ No newline at end of file diff --git a/src/styles/static/slick-theme.css b/src/styles/static/slick-theme.css index 3ec2a3b..1b9195b 100644 --- a/src/styles/static/slick-theme.css +++ b/src/styles/static/slick-theme.css @@ -181,5 +181,5 @@ } .slick-dots li.slick-active button:before { opacity: 1; - color: #52c41aff; + color: rgba(52, 90, 255, 1)ff; } diff --git a/src/utils/tags.ts b/src/utils/tags.ts new file mode 100644 index 0000000..c2ae9af --- /dev/null +++ b/src/utils/tags.ts @@ -0,0 +1,74 @@ + +import icon_encode from '@/asset/tag/encode.svg'; +import icon_encode_check from '@/asset/tag/encode_check.svg'; +import icon_encryption from '@/asset/tag/encryption.svg'; +import icon_encryption_check from '@/asset/tag/encryption_check.svg'; +import icon_hot from '@/asset/tag/hot.svg'; +import icon_hot_check from '@/asset/tag/hot_check.svg'; +import icon_like from '@/asset/tag/like.svg'; +import icon_like_check from '@/asset/tag/like_check.svg'; +import icon_text from '@/asset/tag/text.svg'; +import icon_text_check from '@/asset/tag/text_check.svg'; +import dev from '@/asset/tag/dev.svg'; +import dev_check from '@/asset/tag/dev_check.svg'; +import other from '@/asset/tag/other.svg'; +import other_check from '@/asset/tag/other_check.svg'; +import json from '@/asset/tag/json.svg'; +import json_check from '@/asset/tag/json_check.svg'; + +export enum Tags { + CODE = 'code', + TEXT = 'text', + DEV = 'dev', + PASSWORD = 'password', + JSON = 'json', + OTHER = 'other', + HOT = 'hot', + LIKE = 'like', +} +const tagColor = [ + ['rgba(245, 117, 130, 1)', 'rgba(245, 117, 130, 0.2)',], + ['rgba(17, 35, 90, 1)', 'rgba(17, 35, 90, 0.2)',], + ['rgba(117, 106, 182, 1)', 'rgba(117, 106, 182, 0.2)',], + ['rgba(123, 211, 234, 1)', 'rgba(123, 211, 234, 0.2)',], + ['rgba(125, 10, 10, 1)', 'rgba(125, 10, 10, 0.2)',], + ['rgba(95, 134, 112, 1)', 'rgba(95, 134, 112, 0.2)',], + ['rgba(57, 36, 103, 1)', 'rgba(57, 36, 103, 0.2)',], + ['rgba(89, 111, 183, 1)', 'rgba(89, 111, 183, 0.2)',], + ['rgba(172, 135, 197, 1)', 'rgba(172, 135, 197, 0.2)',], + ['rgba(191, 49, 49, 1)', 'rgba(191, 49, 49, 0.2)',], + ['rgba(56, 135, 190, 1)', 'rgba(56, 135, 190, 0.2)',], + ['rgba(255, 152, 0, 1)', 'rgba(255, 152, 0, 0.2)',], + ['rgba(93, 53, 135, 1)', 'rgba(93, 53, 135, 0.2)',], + ['rgba(198, 207, 155, 1)', 'rgba(198, 207, 155, 0.2)',], + ['rgba(184, 0, 0, 1)', 'rgba(184, 0, 0, 0.2)',], + ['rgba(56, 65, 157, 1)', 'rgba(56, 65, 157, 0.2)',], + ['rgba(205, 141, 122, 1)', 'rgba(205, 141, 122, 0.2)',], + ['rgba(255, 214, 102, 1)', 'rgba(255, 214, 102, 0.2)',], + ['rgba(248, 141, 72, 1)', 'rgba(248, 141, 72, 0.2)',], + ['rgba(184, 137, 234, 1)', 'rgba(184, 137, 234, 0.2)',], + ['rgba(242, 115, 181, 1)', 'rgba(242, 115, 181, 0.2)',], + ['rgba(69, 218, 209, 1)', 'rgba(69, 218, 209, 0.2)',], + ['rgba(64, 169, 255, 1)', 'rgba(64, 169, 255, 0.2)',], + ['rgba(200, 167, 134, 1)', 'rgba(200, 167, 134, 0.2)',], +] + +export type Tag = { + name: Tags, icon: string, icon_check: string, label: string, bg_color: string, avatar_color: string +} +export const allTags: Tag[] = [ + { name: Tags.HOT, icon: icon_hot, icon_check: icon_hot_check, label: '热门工具', bg_color: '', avatar_color: '' }, + { name: Tags.LIKE, icon: icon_like, icon_check: icon_like_check, label: '我的收藏', bg_color: '', avatar_color: '' }, + { name: Tags.CODE, icon: icon_encode, icon_check: icon_encode_check, label: '编码转码', bg_color: '', avatar_color: '' }, + { name: Tags.DEV, icon: dev, icon_check: dev_check, label: '开发', bg_color: '', avatar_color: '' }, + { name: Tags.TEXT, icon: icon_text, icon_check: icon_text_check, label: '文字处理', bg_color: '', avatar_color: '' }, + { name: Tags.PASSWORD, icon: icon_encryption, icon_check: icon_encryption_check, label: '密码学', bg_color: '', avatar_color: '' }, + { name: Tags.JSON, icon: json, icon_check: json_check, label: 'JSON', bg_color: '', avatar_color: '' }, + { name: Tags.OTHER, icon: other, icon_check: other_check, label: '杂项', bg_color: '', avatar_color: '' }, +].map((item, index) => { + const [avatarColor, bgColor] = tagColor[index] + item.bg_color = bgColor + item.avatar_color = avatarColor + return item +}); + diff --git a/src/utils/tools.ts b/src/utils/tools.ts index 4fc4f49..4554c1d 100644 --- a/src/utils/tools.ts +++ b/src/utils/tools.ts @@ -1,52 +1,33 @@ -import OtherIcon from '@mui/icons-material/AutoAwesome'; -import CodeIcon from '@mui/icons-material/Code'; -import DataArrayIcon from '@mui/icons-material/DataArray'; -import DeveloperModeIcon from '@mui/icons-material/DeveloperMode'; -import LockIcon from '@mui/icons-material/Lock'; -import TextSnippetIcon from '@mui/icons-material/TextSnippet'; - -export enum Tags { - CODE = 'code', - TEXT = 'text', - DEV = 'dev', - PASSWORD = 'password', - JSON = 'json', - OTHER = 'other', -} - -export const AllTags = [ - { name: Tags.CODE, icon: CodeIcon, label: '编码转码' }, - { name: Tags.DEV, icon: DeveloperModeIcon, label: '开发' }, - { name: Tags.TEXT, icon: TextSnippetIcon, label: '文字处理' }, - { name: Tags.PASSWORD, icon: LockIcon, label: '密码学' }, - { name: Tags.JSON, icon: DataArrayIcon, label: 'JSON' }, - { name: Tags.OTHER, icon: OtherIcon, label: '杂项' }, -]; +import { Tags } from "./tags"; export type Tool = { label: string; tags: Tags[]; path: string; subTitle: string; + key: string[] }; -export const routesMenu: Tool[] = [ +export const allTools: Tool[] = [ { label: 'URL 编解码', tags: [Tags.CODE], path: '/urlencoder', + key: [], subTitle: 'URL 编码解码小工具。', }, { label: 'Base64 编解码', - tags: [Tags.CODE], + tags: [Tags.CODE, Tags.HOT], path: '/base64', + key: [], subTitle: 'Base64 编码解码小工具。', }, { label: '图片转 Base64', tags: [Tags.CODE], path: '/img2base64', + key: [], subTitle: '图片的 BASE64 编码就是将图片数据编码成字符串,使用该字符串代替图片地址,从而不需要使用图片的 URL 地址。', }, @@ -54,6 +35,7 @@ export const routesMenu: Tool[] = [ label: '进制转换', tags: [Tags.CODE], path: '/radix_convert', + key: [], subTitle: '进制转换小工具,支持二进制、八进制、十进制、十六进制等之间的互相转换。', }, @@ -61,18 +43,21 @@ export const routesMenu: Tool[] = [ label: '字数统计', tags: [Tags.TEXT], path: '/word_count', + key: [], subTitle: '字数统计小工具,支持中文、英文、数字、标点符号等的统计。', }, { label: '大小写转换', tags: [Tags.TEXT], path: '/case_convert', + key: [], subTitle: '大小写转换小工具,支持大写、小写、首字母大写、大小写互转等。', }, { label: 'Unix 时间戳转换', - tags: [Tags.DEV], + tags: [Tags.DEV, Tags.HOT], path: '/unix', + key: [], subTitle: 'Unix 时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。', }, @@ -80,37 +65,43 @@ export const routesMenu: Tool[] = [ label: 'ASCII 码表', tags: [Tags.DEV], path: '/ascii', + key: [], subTitle: 'ASCII 码查询表。', }, { label: '密码哈希', tags: [Tags.PASSWORD], path: '/hash', + key: [], subTitle: '在线 MD5,SHA256 哈希算法计算小工具。', }, { label: 'AES 加解密', tags: [Tags.PASSWORD], path: '/aes', + key: [], subTitle: '在线 AES 算法加解密工具。', }, { label: 'JSON 转 CSV', tags: [Tags.JSON], path: '/jsontocsv', + key: [], subTitle: 'JSON 转 CSV 和 EXCEL 小工具。', }, { label: '随机数/密码生成', - tags: [Tags.OTHER], + tags: [Tags.OTHER, Tags.HOT], path: '/random', + key: [], subTitle: '该功能由浏览器在本地完成,您的任何输入都不会提交到服务端。', }, { label: '图片去色', tags: [Tags.OTHER], path: '/uncolor', + key: [], subTitle: '将彩色图片转换为黑白图片', }, ];