├── .eslintrc.json ├── .github └── workflows │ └── nextjs.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .lintstagedrc.js ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── commitlint.config.js ├── declare.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── public ├── Mono.ttf ├── cnzz.js ├── favicon.ico ├── pkg │ ├── cidr_aggregator.js │ └── cidr_aggregator_bg.wasm └── wasm │ ├── crackzip.wasm │ ├── fastcoll.wasm │ ├── file.wasm │ ├── pycdas.wasm │ ├── pycdc.wasm │ └── sqlite.wasm ├── src ├── asset │ ├── img │ │ ├── copy_icon.webp │ │ ├── default_avatar.png │ │ ├── edit_icon.webp │ │ ├── emblem.webp │ │ ├── logo.svg │ │ └── pixel-img.jpeg │ ├── json2go │ │ └── index.js │ ├── tag │ │ ├── dev.svg │ │ ├── dev_check.svg │ │ ├── encode.svg │ │ ├── encode_check.svg │ │ ├── encryption.svg │ │ ├── encryption_check.svg │ │ ├── hot.svg │ │ ├── hot_check.svg │ │ ├── hover_red_like.svg │ │ ├── image.svg │ │ ├── image_check.svg │ │ ├── json.svg │ │ ├── json_check.svg │ │ ├── like.svg │ │ ├── like_check.svg │ │ ├── no_like.png │ │ ├── other.svg │ │ ├── other_check.svg │ │ ├── red_like.png │ │ ├── red_like.svg │ │ ├── red_like_check.png │ │ ├── text.svg │ │ └── text_check.svg │ ├── tools │ │ ├── cloud_bg.png │ │ ├── dot_black.png │ │ ├── dot_blue.png │ │ ├── dot_yellow.png │ │ └── sunglasses.png │ └── wasm │ │ ├── crackzip.js │ │ ├── fastcoll.js │ │ ├── file.js │ │ ├── pycdas.js │ │ └── pycdc.js ├── components │ ├── Alert │ │ └── index.tsx │ ├── Button │ │ └── index.tsx │ ├── CIDR │ │ ├── InputEditor.tsx │ │ ├── OptionsControl.tsx │ │ ├── OutputEditor.tsx │ │ ├── OutputStatusLine.tsx │ │ └── WarningFab.tsx │ ├── CusTabs │ │ └── index.tsx │ ├── Dynamic │ │ ├── AudioFmt.tsx │ │ ├── Frame.tsx │ │ ├── Video2Gif.tsx │ │ └── VideoFmt.tsx │ ├── FormItem │ │ └── index.tsx │ ├── Formater │ │ └── index.tsx │ ├── Header │ │ ├── components.tsx │ │ ├── index.tsx │ │ ├── loggedInView.tsx │ │ ├── profilePanel.tsx │ │ └── toolkit.ts │ ├── ImageDownload │ │ └── index.tsx │ ├── LikeIcon │ │ └── index.tsx │ ├── MainContent │ │ └── index.tsx │ ├── Text │ │ └── index.tsx │ ├── TextFieldWithClean │ │ └── index.tsx │ ├── TextFieldWithCopy │ │ └── index.tsx │ ├── ToolCard │ │ └── index.tsx │ ├── Tools │ │ └── index.tsx │ └── Watermark │ │ ├── context.ts │ │ ├── index.tsx │ │ ├── useClips.ts │ │ ├── useWatermark.tsx │ │ └── utils.ts ├── constant │ ├── color.ts │ ├── index.tsx │ └── style.ts ├── hooks │ ├── index.ts │ ├── useAnchor.tsx │ ├── useCSV.ts │ ├── useDebounce.ts │ ├── useLikeList.tsx │ ├── useMobileView.ts │ ├── useNavigationWithPramas.ts │ ├── usePath.ts │ └── useWasm.ts ├── icon │ ├── ArrowDown.tsx │ ├── Check.tsx │ ├── CheckCircleFilled.tsx │ ├── CheckCircleOutlined.tsx │ ├── Copy.tsx │ ├── DashedArrow.tsx │ ├── Delete.tsx │ ├── Exclamation.tsx │ ├── Home.tsx │ ├── ImageIcon.tsx │ ├── Logo.tsx │ ├── LogoWhite.tsx │ ├── RightLined.tsx │ ├── Sort.tsx │ ├── WeChat.tsx │ ├── index.tsx │ └── menu │ │ └── index.tsx ├── layouts │ ├── Header │ │ └── index.tsx │ └── SideBar │ │ └── index.tsx ├── pages │ ├── 3des.tsx │ ├── 404.tsx │ ├── _app.tsx │ ├── _document.tsx │ ├── aes.tsx │ ├── ascii.tsx │ ├── audiofmt.tsx │ ├── base64.tsx │ ├── camelcase.tsx │ ├── case_convert.tsx │ ├── chart_line.tsx │ ├── chart_nightingale.tsx │ ├── chart_radar.tsx │ ├── cidr.tsx │ ├── cidr_aggregator.tsx │ ├── cn_space_en.tsx │ ├── color_convert.tsx │ ├── color_picker.tsx │ ├── countdown.tsx │ ├── css_minifier.tsx │ ├── cssfmt.tsx │ ├── cvencode.tsx │ ├── de_qrcode.tsx │ ├── des.tsx │ ├── diff.tsx │ ├── dir_tree.tsx │ ├── docker_run_to_docker_compose.tsx │ ├── eatwhat.tsx │ ├── excalidraw.tsx │ ├── figlet.tsx │ ├── file.tsx │ ├── generate_qrcode.tsx │ ├── getvideoaudio.tsx │ ├── git.tsx │ ├── hash.tsx │ ├── hex.tsx │ ├── hexeditor.tsx │ ├── home.tsx │ ├── htmlentity.tsx │ ├── htmlfmt.tsx │ ├── htpasswd.tsx │ ├── img2base64.tsx │ ├── img_conversion.tsx │ ├── img_radius.tsx │ ├── img_sharp.tsx │ ├── img_split.tsx │ ├── jsfmt.tsx │ ├── jsfuck.tsx │ ├── json2go.tsx │ ├── json2js.tsx │ ├── json2xml.tsx │ ├── json2yaml.tsx │ ├── jsonfmt.tsx │ ├── jsontocsv.tsx │ ├── leet_convert.tsx │ ├── less2css.tsx │ ├── lifecount.tsx │ ├── line_number.tsx │ ├── md2html.tsx │ ├── md5fastcollision.tsx │ ├── morse.tsx │ ├── ocr.tsx │ ├── pixel_img.tsx │ ├── pyc2asm.tsx │ ├── pyc2py.tsx │ ├── radix_convert.tsx │ ├── random.tsx │ ├── random_email.tsx │ ├── random_ip.tsx │ ├── random_ua.tsx │ ├── rsa.tsx │ ├── shuffle_text_generator.tsx │ ├── sqlfmt.tsx │ ├── sqlite.tsx │ ├── tsfmt.tsx │ ├── uncolor.tsx │ ├── unix.tsx │ ├── urlencoder.tsx │ ├── uuid_gen.tsx │ ├── video2gif.tsx │ ├── videofmt.tsx │ ├── videoframe.tsx │ ├── watermark.tsx │ ├── word_count.tsx │ ├── xmlfmt.tsx │ ├── xssvector.tsx │ ├── yamlfmt.tsx │ └── zipcrack.tsx ├── styles │ ├── colors.ts │ ├── global.css │ ├── static │ │ ├── effect-creative.min.css │ │ ├── navigation.min.css │ │ ├── slick-theme.css │ │ ├── slick.css │ │ ├── swiper-customize.css │ │ └── swiper.css │ └── theme.ts ├── types │ ├── app.ts │ ├── auth.ts │ ├── data.ts │ ├── declare.d.ts │ ├── general.ts │ ├── index.ts │ ├── navbar.ts │ ├── notifications.ts │ ├── orders.ts │ ├── org.ts │ ├── ticket.ts │ └── user.ts └── utils │ ├── bufferToWave.ts │ ├── color.ts │ ├── core-values-encoder.ts │ ├── download.ts │ ├── emotionCache.ts │ ├── enterApp.ts │ ├── getDefaultOrg.ts │ ├── index.ts │ ├── meta.ts │ ├── tags.ts │ ├── tools.ts │ └── url.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "prettier"], 3 | "plugins": ["@typescript-eslint", "unused-imports"], 4 | "rules": { 5 | "unused-imports/no-unused-imports": "error" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.github/workflows/nextjs.yml: -------------------------------------------------------------------------------- 1 | name: Build XTools 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | tags: 7 | - 'v*.*.*' 8 | pull_request: 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: write 14 | 15 | jobs: 16 | # Build job 17 | build: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | - name: Detect package manager 23 | id: detect-package-manager 24 | run: | 25 | echo "manager=npm" >> $GITHUB_OUTPUT 26 | echo "command=install" >> $GITHUB_OUTPUT 27 | echo "runner=npx --no-install" >> $GITHUB_OUTPUT 28 | exit 0 29 | - name: Setup Node 30 | uses: actions/setup-node@v4 31 | with: 32 | node-version: '20' 33 | cache: ${{ steps.detect-package-manager.outputs.manager }} 34 | - name: Restore cache 35 | uses: actions/cache@v3 36 | with: 37 | path: | 38 | .next/cache 39 | # Generate a new cache whenever packages or source files change. 40 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} 41 | # If source files changed but packages didn't, rebuild from a prior cache. 42 | restore-keys: | 43 | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- 44 | - name: Install dependencies 45 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} 46 | - name: Build with Next.js 47 | run: ${{ steps.detect-package-manager.outputs.runner }} npm run build 48 | - name: 'Tar files' 49 | run: tar -cvzf out.tgz ./out 50 | - name: Upload artifact 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: xtools 54 | path: out.tgz 55 | - name: Release 56 | uses: softprops/action-gh-release@v1 57 | if: startsWith(github.ref, 'refs/tags/') 58 | with: 59 | files: out.tgz 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .idea/ 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | next-env.d.ts 36 | *.tsbuildinfo 37 | .yalc 38 | yalc 39 | yalc.lock -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.{ts,tsx}': ['npm run lint --fix', 'git add'], 3 | '*.{html,json,yaml,yml}': ['prettier --write', 'git add'], 4 | }; 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | registry=https://registry.npmmirror.com 3 | 4 | sentrycli_cdnurl=https://cdn.npm.taobao.org/dist/sentry-cli 5 | ENTRYCLI_CDNURL=https://cdn.npm.taobao.org/dist/sentry-cli 6 | 7 | strict-ssl=false 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | 3 | **/src/asset/ -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": true, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "jsxSingleQuote": true 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Chaitin Tech 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /declare.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'css2less'; 2 | declare module 'clean-css' { 3 | export type Options = any; 4 | export default any; 5 | } 6 | declare module 'composerize' { 7 | export default any; 8 | } 9 | declare module 'figlet/importable-fonts/Standard.js'; 10 | declare module 'figlet/importable-fonts/1Row.js'; 11 | declare module 'figlet/importable-fonts/3D-ASCII'; 12 | declare module 'figlet/importable-fonts/ANSI Regular'; 13 | declare module 'figlet/importable-fonts/Alligator'; 14 | declare module 'figlet/importable-fonts/Whimsy'; 15 | declare module 'figlet/importable-fonts/Wow'; 16 | declare module 'figlet/importable-fonts/Weird'; 17 | declare module 'figlet/importable-fonts/Wavy'; 18 | declare module 'figlet/importable-fonts/3D Diagonal'; 19 | declare module 'figlet/importable-fonts/Banner'; 20 | declare module 'figlet/importable-fonts/Block'; 21 | declare module 'figlet/importable-fonts/Bear'; 22 | declare module 'figlet/importable-fonts/Big Money-ne'; 23 | declare module 'figlet/importable-fonts/Delta Corps Priest 1'; 24 | declare module 'figlet/importable-fonts/Doh'; 25 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const withPlugins = require('next-compose-plugins'); 3 | const withTM = require('next-transpile-modules')([ 4 | 'react-syntax-highlighter', 5 | 'shrinkpng', 6 | 'figlet', 7 | ]); 8 | 9 | const nextConfig = withPlugins([withTM], { 10 | reactStrictMode: false, 11 | output: 'export', 12 | images: { 13 | unoptimized: true, 14 | }, 15 | basePath: process.env.NODE_ENV === 'production' ? '/tools' : '', 16 | redirects: process.env.NODE_ENV === 'development' ? async () => { 17 | return [ 18 | { 19 | source: '/', 20 | destination: '/home', 21 | permanent: true, 22 | }, 23 | ]; 24 | } : undefined, 25 | webpack: (config) => { 26 | config.resolve.fallback = { fs: false }; 27 | config.experiments = { 28 | asyncWebAssembly: true, 29 | }; 30 | return config; 31 | }, 32 | }); 33 | 34 | module.exports = nextConfig; 35 | -------------------------------------------------------------------------------- /public/Mono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/public/Mono.ttf -------------------------------------------------------------------------------- /public/cnzz.js: -------------------------------------------------------------------------------- 1 | if ( 2 | [ 3 | 'rivers.chaitin.cn', 4 | 'dev.rivers.ctopt.cn', 5 | // '127.0.0.1', 6 | // 'localhost', 7 | ].includes(document.domain) 8 | ) { 9 | window.is_river = true; 10 | var _czc = _czc || []; 11 | var cnzzTag = document.createElement('script'); 12 | cnzzTag.src = 'https://s4.cnzz.com/z.js?id=1281132544&async=1'; 13 | document.head.append(cnzzTag); 14 | } 15 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/public/favicon.ico -------------------------------------------------------------------------------- /public/pkg/cidr_aggregator_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/public/pkg/cidr_aggregator_bg.wasm -------------------------------------------------------------------------------- /public/wasm/crackzip.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/public/wasm/crackzip.wasm -------------------------------------------------------------------------------- /public/wasm/fastcoll.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/public/wasm/fastcoll.wasm -------------------------------------------------------------------------------- /public/wasm/file.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/public/wasm/file.wasm -------------------------------------------------------------------------------- /public/wasm/pycdas.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/public/wasm/pycdas.wasm -------------------------------------------------------------------------------- /public/wasm/pycdc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/public/wasm/pycdc.wasm -------------------------------------------------------------------------------- /public/wasm/sqlite.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/public/wasm/sqlite.wasm -------------------------------------------------------------------------------- /src/asset/img/copy_icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/img/copy_icon.webp -------------------------------------------------------------------------------- /src/asset/img/default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/img/default_avatar.png -------------------------------------------------------------------------------- /src/asset/img/edit_icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/img/edit_icon.webp -------------------------------------------------------------------------------- /src/asset/img/emblem.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/img/emblem.webp -------------------------------------------------------------------------------- /src/asset/img/pixel-img.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/img/pixel-img.jpeg -------------------------------------------------------------------------------- /src/asset/tag/encode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 编码解码(未选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/asset/tag/encode_check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 编码解码(选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/asset/tag/encryption.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 加密解密(未选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/asset/tag/encryption_check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 加密解密(选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/asset/tag/hot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 热门工具(未选中) 2 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/asset/tag/hot_check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 热门工具(选中) 2 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/asset/tag/hover_red_like.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /src/asset/tag/image.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 图像处理(未选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/asset/tag/image_check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 图像处理(选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/asset/tag/like.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 我的收藏(未选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/asset/tag/no_like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/tag/no_like.png -------------------------------------------------------------------------------- /src/asset/tag/other.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 杂项(未选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/asset/tag/other_check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 杂项(选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/asset/tag/red_like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/tag/red_like.png -------------------------------------------------------------------------------- /src/asset/tag/red_like.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /src/asset/tag/red_like_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/tag/red_like_check.png -------------------------------------------------------------------------------- /src/asset/tag/text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 文字处理(未选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/asset/tag/text_check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 文字处理(选中) 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/asset/tools/cloud_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/tools/cloud_bg.png -------------------------------------------------------------------------------- /src/asset/tools/dot_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/tools/dot_black.png -------------------------------------------------------------------------------- /src/asset/tools/dot_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/tools/dot_blue.png -------------------------------------------------------------------------------- /src/asset/tools/dot_yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/tools/dot_yellow.png -------------------------------------------------------------------------------- /src/asset/tools/sunglasses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chaitin/xtools/5338f18e5a4ebe527951f44f7242397c7d74a169/src/asset/tools/sunglasses.png -------------------------------------------------------------------------------- /src/components/Alert/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { success } from '@/styles/colors'; 3 | import Snackbar from '@mui/material/Snackbar'; 4 | import MuiAlert, { AlertProps, AlertColor } from '@mui/material/Alert'; 5 | import { createRoot } from 'react-dom/client'; 6 | import { useTheme } from '@mui/material/styles'; 7 | 8 | const Alert = React.forwardRef( 9 | function Alert(props, ref) { 10 | return ; 11 | } 12 | ); 13 | 14 | export interface WarningProps { 15 | content: string; 16 | color: AlertColor; 17 | } 18 | 19 | function WarningBar(props: WarningProps) { 20 | const theme = useTheme(); 21 | const [open, setOpen] = useState(true); 22 | 23 | useEffect(() => { 24 | setTimeout(() => { 25 | setOpen(false); 26 | }, 2000); 27 | }, []); 28 | return ( 29 | setOpen(false)} 33 | key={'top-center-warning'} 34 | sx={{ zIndex: theme.zIndex.snackbar + 100 }} 35 | > 36 | 44 | {props?.content ?? ''} 45 | 46 | 47 | ); 48 | } 49 | 50 | export function callAlert(props: WarningProps) { 51 | const warningDom = document.createElement('div'); 52 | document.body.appendChild(warningDom); 53 | warningDom.id = 'warning-window'; 54 | warningDom.style.zIndex = '-2'; 55 | const warningRoot = createRoot(warningDom); 56 | warningRoot.render(); 57 | setTimeout(() => { 58 | warningDom.remove(); 59 | }, 3000); 60 | } 61 | 62 | const alertActions = { 63 | success: (content: string) => callAlert({ color: 'success', content }), 64 | warning: (content: string) => callAlert({ color: 'warning', content }), 65 | error: (content: string) => callAlert({ color: 'error', content }), 66 | }; 67 | 68 | export default alertActions; 69 | -------------------------------------------------------------------------------- /src/components/CIDR/InputEditor.tsx: -------------------------------------------------------------------------------- 1 | import { countLines } from '@/utils'; 2 | import { Box, TextField, Typography } from '@mui/material'; 3 | import { useMemo } from 'react'; 4 | import WarningFab from './WarningFab'; 5 | 6 | export default function InputEditor({ 7 | input, 8 | setInput, 9 | output, 10 | }: { 11 | input: string; 12 | output: any; 13 | setInput: (value: string) => void; 14 | }) { 15 | return ( 16 | 17 | setInput(event.target.value)} 28 | /> 29 | 30 | 31 | 行数: {useMemo(() => countLines(input), [input])}{' '} 32 | 33 | 34 | 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /src/components/CIDR/OptionsControl.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Grid } from '@mui/material'; 2 | import { ForwardedRef, forwardRef } from 'react'; 3 | 4 | function OptionsControl( 5 | { 6 | ipKind, 7 | toggleIpv4, 8 | toggleIpv6, 9 | bogonFilter, 10 | toggleReservedFilter, 11 | handleAggregate, 12 | }: { 13 | ipKind: string; 14 | toggleIpv4: () => void; 15 | toggleIpv6: () => void; 16 | bogonFilter?: string; 17 | toggleReservedFilter: () => void; 18 | handleAggregate: (reverse?: boolean) => void; 19 | }, 20 | ref: ForwardedRef 21 | ) { 22 | return ( 23 | 24 | 25 | 33 | 34 | 35 | ); 36 | } 37 | 38 | export default forwardRef(OptionsControl); 39 | -------------------------------------------------------------------------------- /src/components/CIDR/OutputEditor.tsx: -------------------------------------------------------------------------------- 1 | import { Box, TextField } from '@mui/material'; 2 | import OutputStatusLine from './OutputStatusLine'; 3 | 4 | export default function OutputEditor({ 5 | ipKind, 6 | output, 7 | }: { 8 | ipKind: string; 9 | output: any; 10 | }) { 11 | return ( 12 | 13 | {' '} 14 | v) 27 | .join('\n')} 28 | /> 29 | 30 | 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/components/CIDR/OutputStatusLine.tsx: -------------------------------------------------------------------------------- 1 | import { Grid, Typography } from '@mui/material'; 2 | 3 | function Partial({ name, status }: { name: string; status: any }) { 4 | return ( 5 | 6 | {name}: {status?.line_count_before ?? 0} 7 | / {status?.address_count_before ?? '0'} 个 8 |   ➟   9 | {status?.line_count_after ?? 0} 10 | /{' '} 11 | {status?.address_count_after ?? '0'} 个 12 | 13 | ); 14 | } 15 | 16 | export default function OutputStatusLine({ output }: { output: any }) { 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /src/components/CIDR/WarningFab.tsx: -------------------------------------------------------------------------------- 1 | import { countLines } from '@/utils'; 2 | import WarningIcon from '@mui/icons-material/Warning'; 3 | import { 4 | Badge, 5 | Box, 6 | Fab, 7 | Paper, 8 | Popover, 9 | Tooltip, 10 | Typography, 11 | } from '@mui/material'; 12 | import React, { MouseEvent, useMemo } from 'react'; 13 | 14 | export default function WarningFab({ invalidLines }: { invalidLines: string }) { 15 | const [anchorEl, setAnchorEl] = React.useState(null as any); 16 | const handleOpen = (event: MouseEvent) => { 17 | setAnchorEl(event.currentTarget); 18 | }; 19 | const handleClose = () => { 20 | setAnchorEl(null); 21 | }; 22 | const open = Boolean(anchorEl); 23 | const id = open ? 'invalid-lines-popover' : undefined; 24 | 25 | const invalidCount = useMemo(() => countLines(invalidLines), [invalidLines]); 26 | 27 | return invalidCount > 0 ? ( 28 | <> 29 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 55 | 56 | 57 | 无效的 CIDR 58 | 59 | 64 |
65 |               {invalidLines}
66 |             
67 |
68 |
69 |
70 | 71 | ) : ( 72 | <> 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /src/components/CusTabs/index.tsx: -------------------------------------------------------------------------------- 1 | import { type FC, useState, useEffect } from 'react'; 2 | 3 | import { Tabs, Tab, type SxProps } from '@mui/material'; 4 | 5 | interface ListItem { 6 | label: string | React.ReactNode; 7 | value: string | number; 8 | disabled?: boolean; 9 | } 10 | 11 | interface RadioButtonProps { 12 | list: ListItem[]; 13 | defatValue?: ListItem['value']; 14 | onChange?(value: ListItem['value']): void; 15 | size?: 'small' | 'medium' | 'large'; 16 | sx?: SxProps; 17 | 18 | // 希望父组件控制 19 | change?(value: ListItem['value']): void; 20 | value?: ListItem['value']; 21 | } 22 | 23 | const CusTabs: FC = ({ 24 | list, 25 | defatValue, 26 | onChange, 27 | change, 28 | sx, 29 | value: v, 30 | }) => { 31 | const [value, setValue] = useState( 32 | v || defatValue || list[0].value 33 | ); 34 | const handleChange = ( 35 | event: React.SyntheticEvent, 36 | id: any 37 | ) => { 38 | if (id !== null) { 39 | setValue(id); 40 | onChange?.(id); 41 | } 42 | }; 43 | 44 | useEffect(() => { 45 | if (v) setValue(v); 46 | }, [v]); 47 | 48 | return ( 49 | , value: any) => 54 | change(value) 55 | : handleChange 56 | } 57 | sx={{ 58 | p: '5px', 59 | border: '1px solid', 60 | borderColor: 'divider', 61 | minHeight: 36, 62 | height: 36, 63 | backgroundColor: 'background.paper', 64 | borderRadius: '4px', 65 | 66 | '.MuiTabs-indicator': { 67 | top: 0, 68 | bottom: 0, 69 | height: 'auto', 70 | borderRadius: '4px', 71 | }, 72 | ...sx, 73 | }} 74 | > 75 | {list.map((item) => ( 76 | 92 | ))} 93 | 94 | ); 95 | }; 96 | 97 | export default CusTabs; 98 | -------------------------------------------------------------------------------- /src/components/FormItem/index.tsx: -------------------------------------------------------------------------------- 1 | import { FormControl, InputLabel, Stack } from '@mui/material'; 2 | import React from 'react'; 3 | 4 | const FormItem: React.FC<{ 5 | label: string | React.ReactNode; 6 | children: React.ReactNode; 7 | singleLine?: boolean; 8 | }> = (props) => { 9 | const { singleLine = false } = props; 10 | return ( 11 | 12 | 16 | {props.label} 17 | {props.children} 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default FormItem; 24 | -------------------------------------------------------------------------------- /src/components/Header/loggedInView.tsx: -------------------------------------------------------------------------------- 1 | import { Tooltip, Box } from '@mui/material'; 2 | import { Avatar } from './components'; 3 | import React from 'react'; 4 | import ProfilePanel from './profilePanel'; 5 | 6 | export interface LoggedInProps { 7 | user: any | null; 8 | verified: boolean; 9 | } 10 | 11 | const LoggedInView = ({ user, promotionInfo }: any) => { 12 | return ( 13 | 38 | } 39 | > 40 | 41 | {user.head_img_url ? ( 42 | 头像 52 | ) : ( 53 | 54 | )} 55 | 56 | 57 | ); 58 | }; 59 | 60 | export default LoggedInView; 61 | -------------------------------------------------------------------------------- /src/components/ImageDownload/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box, SxProps } from '@mui/material'; 2 | import React, { useCallback } from 'react'; 3 | 4 | interface Props { 5 | src: string; 6 | sx?: SxProps; 7 | } 8 | 9 | const Module: React.FC = (props) => { 10 | const handleClick = useCallback( 11 | (event: React.MouseEvent) => { 12 | if (props.src) { 13 | const extName = /^data:[a-zA-Z0-9]+\/([a-zA-Z0-9]+)[^;]*;base64,/.exec( 14 | props.src 15 | ); 16 | if (extName && extName[1]) { 17 | const aTag = document.createElement('a'); 18 | aTag.style.display = 'none'; 19 | aTag.href = props.src; 20 | aTag.download = '保存图片.' + extName[1]; 21 | document.body.appendChild(aTag); 22 | aTag.click(); 23 | } 24 | } 25 | }, 26 | [props.src] 27 | ); 28 | 29 | return ( 30 | 42 | {props.src ? ( 43 | 44 | ) : ( 45 | <> 46 | )} 47 | 68 | {' '} 69 | 点击下载图片{' '} 70 | 71 | 72 | ); 73 | }; 74 | 75 | export default Module; 76 | -------------------------------------------------------------------------------- /src/components/LikeIcon/index.tsx: -------------------------------------------------------------------------------- 1 | import RedTag from '@/asset/tag/red_like.svg'; 2 | import RedTagCheck from '@/asset/tag/red_like_check.png'; 3 | import HoverRedTagCheck from '@/asset/tag/hover_red_like.svg'; 4 | import { LikeContext } from '@/hooks/useLikeList'; 5 | import React, { useContext, useMemo } from 'react'; 6 | import { Box } from '@mui/material'; 7 | 8 | const LikeIcon: React.FC<{ path: string; style: React.CSSProperties }> = ( 9 | props 10 | ) => { 11 | const { path, style = {} } = props; 12 | const { likeList, updateLikeList } = useContext(LikeContext); 13 | 14 | const hanldeLike = ( 15 | event: React.MouseEvent, 16 | path: string 17 | ) => { 18 | event.preventDefault(); 19 | if (typeof window !== 'undefined' && window.localStorage) { 20 | try { 21 | let newLike = []; 22 | if (likeList?.includes(path)) { 23 | newLike = likeList.filter((item) => item !== path); 24 | } else { 25 | newLike = [...(likeList || []), path]; 26 | } 27 | updateLikeList(newLike); 28 | } finally { 29 | console.log('Get like list error'); 30 | } 31 | } 32 | }; 33 | const isCheck = useMemo(() => { 34 | return likeList?.includes(path); 35 | }, [likeList, path]); 36 | 37 | return ( 38 | hanldeLike(event, path)} 57 | /> 58 | ); 59 | }; 60 | 61 | export default LikeIcon; 62 | -------------------------------------------------------------------------------- /src/components/Text/index.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '@mui/material/styles'; 2 | import { defaultText } from '@/styles/colors'; 3 | 4 | type sizeType = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'; 5 | type weightType = 'bold' | 'thin' | 'regular'; 6 | type langType = 'zh' | 'en' | 'num'; 7 | export interface TitleTextProps { 8 | size?: sizeType; 9 | weight?: weightType; 10 | lang?: langType; 11 | hover?: 'true' | 'false'; 12 | } 13 | 14 | const sizeChart = { 15 | xs: { fontSize: '14px', lineHeight: '22px' }, 16 | sm: { fontSize: '16px', lineHeight: '24px' }, 17 | md: { fontSize: '24px', lineHeight: '32px' }, 18 | lg: { fontSize: '32px', lineHeight: '40px' }, 19 | xl: { fontSize: '44px', lineHeight: '48px' }, 20 | xxl: { fontSize: '60px', lineHeight: '68px' }, 21 | }; 22 | const weightChart = { 23 | bold: 'AlibabaPuHuiTiBold', 24 | thin: 'AlibabaPuHuiTiThin', 25 | regular: 'AlibabaPuHuiTiRegular', 26 | }; 27 | 28 | const langFont = (weight: weightType) => ({ 29 | zh: `${weightChart[weight]}, "PingFang SC"`, 30 | en: `"Helvetica Neue", Gilroy`, 31 | num: `DIN`, 32 | }); 33 | 34 | const Text = styled('div', { 35 | shouldForwardProp: (prop) => 36 | prop !== 'size' && prop !== 'weight' && prop !== 'lang', 37 | })( 38 | ({ size = 'sm', weight = 'regular', lang = 'zh', theme }) => ({ 39 | fontFamily: langFont(weight)[lang], 40 | ...sizeChart[size], 41 | color: defaultText, 42 | '& > a': { 43 | textDecoration: 'none', 44 | color: 'inherit', 45 | fontFamily: langFont(weight)[lang], 46 | }, 47 | [theme.breakpoints.down('sm')]: { 48 | fontFamily: `"PingFang SC"`, 49 | fontWeight: weight, 50 | }, 51 | }) 52 | ); 53 | 54 | export const TextH1 = styled('h1', { 55 | shouldForwardProp: (prop) => 56 | prop !== 'size' && prop !== 'weight' && prop !== 'lang', 57 | })( 58 | ({ size = 'sm', weight = 'regular', lang = 'zh', theme }) => ({ 59 | fontFamily: langFont(weight)[lang], 60 | ...sizeChart[size], 61 | color: defaultText, 62 | '& > a': { 63 | textDecoration: 'none', 64 | color: 'inherit', 65 | fontFamily: langFont(weight)[lang], 66 | }, 67 | [theme.breakpoints.down('sm')]: { 68 | fontFamily: `"PingFang SC"`, 69 | fontWeight: weight, 70 | }, 71 | }) 72 | ); 73 | 74 | export default Text; 75 | -------------------------------------------------------------------------------- /src/components/TextFieldWithClean/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box, TextField, OutlinedTextFieldProps } from '@mui/material'; 2 | import React from 'react'; 3 | import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; 4 | 5 | interface CustomTextFieldProps extends OutlinedTextFieldProps { 6 | onClean: React.MouseEventHandler; 7 | } 8 | 9 | const TextFieldWithClean: React.FC = (props) => { 10 | const { onClean, ...otherProps } = props; 11 | 12 | return ( 13 | 14 | 19 | 38 | 39 | 40 | 41 | 42 | 43 | ); 44 | }; 45 | 46 | export default TextFieldWithClean; 47 | -------------------------------------------------------------------------------- /src/components/TextFieldWithCopy/index.tsx: -------------------------------------------------------------------------------- 1 | import { Box, TextField, OutlinedTextFieldProps } from '@mui/material'; 2 | import React, { useCallback } from 'react'; 3 | import CopyToClipboard from 'react-copy-to-clipboard'; 4 | import ContentCopyIcon from '@mui/icons-material/ContentCopy'; 5 | import alert from '@/components/Alert'; 6 | 7 | const TextFieldWithCopy: React.FC = (props) => { 8 | const { ...otherProps } = props; 9 | 10 | const handleCopyClick = useCallback(() => { 11 | alert.success('已复制到剪切板'); 12 | }, []); 13 | 14 | return ( 15 | 16 | 21 | 40 | 44 | 45 | 46 | 47 | 48 | ); 49 | }; 50 | 51 | export default TextFieldWithCopy; 52 | -------------------------------------------------------------------------------- /src/components/ToolCard/index.tsx: -------------------------------------------------------------------------------- 1 | import { Tag } from '@/utils/tags'; 2 | import { Tool } from '@/utils/tools'; 3 | import { Avatar, CardHeader, Typography } from '@mui/material'; 4 | import LikeIcon from '../LikeIcon'; 5 | import { grayText2 } from '@/constant'; 6 | 7 | export const ToolCard = (props: { 8 | tool: Tool; 9 | tag?: Tag; 10 | showStar?: boolean; 11 | }) => { 12 | const { tool, tag, showStar = true } = props; 13 | return ( 14 | <> 15 | 35 | {tool.label[0]} 36 | 37 | } 38 | action={ 39 | 47 | } 48 | title={tool.label} 49 | /> 50 | 62 | {tool.subTitle} 63 | 64 | 65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /src/components/Tools/index.tsx: -------------------------------------------------------------------------------- 1 | import { defaultTextClick, primary } from '@/constant'; 2 | import { Box, Stack, Typography } from '@mui/material'; 3 | import { styled } from '@mui/material/styles'; 4 | 5 | type sizeType = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'; 6 | type weightType = 'bold' | 'thin' | 'regular'; 7 | type langType = 'zh' | 'en' | 'num'; 8 | 9 | export const SecondUnitBtn = styled(Typography)(() => ({ 10 | cursor: 'pointer', 11 | minWidth: '30px', 12 | textAlign: 'right', 13 | '&:hover': { 14 | color: primary, 15 | }, 16 | })); 17 | export const ToolsForm = styled(Box)(() => ({ 18 | pt: '10px', 19 | display: 'flex', 20 | flexDirection: 'column', 21 | 22 | mx: 'auto', 23 | gap: 16, 24 | '& .MuiOutlinedInput-root': { 25 | flexShrink: 0, 26 | flexGrow: 1, 27 | }, 28 | '& .MuiOutlinedInput-input': { 29 | paddingTop: '4px', 30 | paddingBottom: '4px', 31 | }, 32 | '& .MuiInputLabel-root': { 33 | position: 'relative', 34 | transform: 'unset', 35 | width: '155px', 36 | color: defaultTextClick, 37 | fontSize: '14px', 38 | }, 39 | '& .MuiOutlinedInput-notchedOutline': { 40 | borderColor: '#E3E8EF', 41 | }, 42 | '& .MuiFormHelperText-root': { 43 | position: 'absolute', 44 | bottom: '-20px', 45 | }, 46 | '& .MuiInputAdornment-positionEnd > p': { 47 | fontSize: '14px!important', 48 | }, 49 | '& input': { 50 | fontSize: '14px!important', 51 | }, 52 | '& .MuiInputLabel-root.Mui-focused': { 53 | color: 'unset', 54 | }, 55 | })); 56 | 57 | export const UnixStartBtn = styled(Stack)(() => ({ 58 | cursor: 'pointer', 59 | alignItems: 'center', 60 | fontSize: '14px', 61 | borderRadius: '4px', 62 | })); 63 | 64 | export const UnixInputWrap = styled(Stack)(() => ({ 65 | '& .MuiOutlinedInput-root': { 66 | '@media(min-width: 1520px)': { 67 | width: '360px', 68 | }, 69 | }, 70 | })); 71 | -------------------------------------------------------------------------------- /src/components/Watermark/context.ts: -------------------------------------------------------------------------------- 1 | import { useEvent } from 'rc-util'; 2 | import * as React from 'react'; 3 | 4 | export interface WatermarkContextProps { 5 | add: (ele: HTMLElement) => void; 6 | remove: (ele: HTMLElement) => void; 7 | } 8 | 9 | function voidFunc() {} 10 | 11 | const WatermarkContext = React.createContext({ 12 | add: voidFunc, 13 | remove: voidFunc, 14 | }); 15 | 16 | export function usePanelRef(panelSelector?: string) { 17 | const watermark = React.useContext(WatermarkContext); 18 | 19 | const panelEleRef = React.useRef(); 20 | const panelRef = useEvent((ele: HTMLElement | null) => { 21 | if (ele) { 22 | const innerContentEle = panelSelector 23 | ? ele.querySelector(panelSelector)! 24 | : ele; 25 | watermark.add(innerContentEle); 26 | panelEleRef.current = innerContentEle; 27 | } else { 28 | watermark.remove(panelEleRef.current!); 29 | } 30 | }); 31 | 32 | return panelRef; 33 | } 34 | 35 | export default WatermarkContext; 36 | -------------------------------------------------------------------------------- /src/components/Watermark/useWatermark.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { getStyleStr } from './utils'; 4 | 5 | /** 6 | * Base size of the canvas, 1 for parallel layout and 2 for alternate layout 7 | * Only alternate layout is currently supported 8 | */ 9 | export const BaseSize = 2; 10 | export const FontGap = 3; 11 | 12 | // Prevent external hidden elements from adding accent styles 13 | const EmphasizedStyles = { 14 | visibility: 'visible !important', 15 | }; 16 | 17 | export type AppendWatermark = ( 18 | base64Url: string, 19 | markWidth: number, 20 | container: HTMLElement 21 | ) => void; 22 | 23 | export default function useWatermark( 24 | markStyle: React.CSSProperties 25 | ): [ 26 | appendWatermark: AppendWatermark, 27 | removeWatermark: (container: HTMLElement) => void, 28 | isWatermarkEle: (ele: Node) => boolean, 29 | ] { 30 | const [watermarkMap] = React.useState( 31 | () => new Map() 32 | ); 33 | 34 | const appendWatermark = ( 35 | base64Url: string, 36 | markWidth: number, 37 | container: HTMLElement 38 | ) => { 39 | if (container) { 40 | if (!watermarkMap.get(container)) { 41 | const newWatermarkEle = document.createElement('div'); 42 | watermarkMap.set(container, newWatermarkEle); 43 | } 44 | 45 | const watermarkEle = watermarkMap.get(container)!; 46 | 47 | watermarkEle.setAttribute( 48 | 'style', 49 | getStyleStr({ 50 | ...markStyle, 51 | backgroundImage: `url('${base64Url}')`, 52 | backgroundSize: `${Math.floor(markWidth)}px`, 53 | ...(EmphasizedStyles as React.CSSProperties), 54 | }) 55 | ); 56 | // Prevents using the browser `Hide Element` to hide watermarks 57 | watermarkEle.removeAttribute('class'); 58 | 59 | container.append(watermarkEle); 60 | } 61 | }; 62 | 63 | const removeWatermark = (container: HTMLElement) => { 64 | const watermarkEle = watermarkMap.get(container); 65 | 66 | if (watermarkEle && container) { 67 | container.removeChild(watermarkEle); 68 | } 69 | 70 | watermarkMap.delete(container); 71 | }; 72 | 73 | const isWatermarkEle = (ele: any) => 74 | Array.from(watermarkMap.values()).includes(ele); 75 | 76 | return [appendWatermark, removeWatermark, isWatermarkEle]; 77 | } 78 | -------------------------------------------------------------------------------- /src/components/Watermark/utils.ts: -------------------------------------------------------------------------------- 1 | /** converting camel-cased strings to be lowercase and link it with Separato */ 2 | export function toLowercaseSeparator(key: string) { 3 | return key.replace(/([A-Z])/g, '-$1').toLowerCase(); 4 | } 5 | 6 | export function getStyleStr(style: React.CSSProperties): string { 7 | return Object.keys(style) 8 | .map( 9 | // @ts-ignore 10 | (key: keyof React.CSSProperties) => 11 | `${toLowercaseSeparator(key)}: ${style[key]};` 12 | ) 13 | .join(' '); 14 | } 15 | 16 | /** Returns the ratio of the device's physical pixel resolution to the css pixel resolution */ 17 | export function getPixelRatio() { 18 | return window.devicePixelRatio || 1; 19 | } 20 | 21 | /** Whether to re-render the watermark */ 22 | export const reRendering = ( 23 | mutation: MutationRecord, 24 | isWatermarkEle: (ele: any) => boolean 25 | ) => { 26 | let flag = false; 27 | // Whether to delete the watermark node 28 | if (mutation.removedNodes.length) { 29 | flag = Array.from(mutation.removedNodes).some((node) => 30 | isWatermarkEle(node) 31 | ); 32 | } 33 | // Whether the watermark dom property value has been modified 34 | if (mutation.type === 'attributes' && isWatermarkEle(mutation.target)) { 35 | flag = true; 36 | } 37 | return flag; 38 | }; 39 | 40 | export const downloadFile = ( 41 | base64Data: string, 42 | name: string = 'image.png' 43 | ) => { 44 | const b64encoded = base64Data.replace(/^data:image\/\w+;base64,/, ''); 45 | const rawData = atob(b64encoded); 46 | const arrayBuffer = Uint8Array.from(rawData, (c) => c.charCodeAt(0)); 47 | const blob = new Blob([arrayBuffer], { type: 'image/png' }); 48 | const url = URL.createObjectURL(blob); 49 | const a = document.createElement('a'); 50 | a.href = url; 51 | a.download = name; 52 | a.click(); 53 | }; 54 | -------------------------------------------------------------------------------- /src/constant/color.ts: -------------------------------------------------------------------------------- 1 | export const primary = 'rgba(52, 90, 255, 1)'; 2 | export const primaryHover = '#73D13C'; 3 | export const primaryClick = '#389E0E'; 4 | 5 | export const primaryLight = '#DDF4D2'; 6 | export const primaryLightGradient = 7 | 'linear-gradient(202deg, rgba(166,216,73,0.1) 0%, rgba(116,193,89,0.1) 100%);'; 8 | 9 | export const secondary = '#ECF9E6'; 10 | export const secondaryHover = '#F3FDEE'; 11 | export const secondaryClick = '#DAE8D4'; 12 | 13 | export const special = 14 | 'linear-gradient(270deg, #11AF60 0%, rgba(52, 90, 255, 1) 100%)'; 15 | export const dangerHover = 'linear-gradient(225deg, #FF1F1F 0%, #F78900 100%)'; 16 | 17 | export const disabled = '#F7F7F7'; 18 | export const shadowColor = '#041B0F'; 19 | 20 | export const defaultText = '#041B0F'; 21 | export const defaultTextHover = '#0E2919'; 22 | export const defaultTextClick = '#000000'; 23 | 24 | export const secondaryText = '#041B0F99'; 25 | export const secondaryTextHover = '#737373'; 26 | export const secondaryTextClick = '#404040'; 27 | 28 | export const leastText = '#041B0F66'; 29 | export const leastTextHover = '#A6A6A6'; 30 | export const leastTextClick = '#737373'; 31 | 32 | export const disabledText = '#BFBFBF'; 33 | export const specialText = '#737971'; 34 | 35 | export const success = 'rgba(52, 90, 255, 1)'; 36 | export const warning = '#FFBF00'; 37 | export const error = '#FF1F1F'; 38 | 39 | export const grayText = '#0B2562'; 40 | export const grayText2 = 'rgba(11,37,98,0.5)'; 41 | export const errorText = '#FF1F1F'; 42 | export const infoText = '#2F7CE9'; 43 | 44 | export const gray = '#DADCE0'; 45 | export const grayLight = '#F8F8F8'; 46 | export const grayLabel = '#717579'; 47 | export const hover = 'rgba(82, 196, 26, 0.8)'; 48 | export const hoverLight = 'rgba(82, 196, 26, 0.11)'; 49 | 50 | export const darkBg = '#072120'; 51 | export const grayBg = '#F3F4F7'; 52 | export const grayBg2 = '#F7F8FA'; 53 | 54 | export const grayBorder = '#E7EAEF'; 55 | 56 | export const caption2 = 'rgba(0, 0, 0, 0.50)'; 57 | 58 | export const notificationsBg = 'rgba(255, 24, 68, 1)'; 59 | -------------------------------------------------------------------------------- /src/constant/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './color'; 2 | export * from './style'; 3 | -------------------------------------------------------------------------------- /src/constant/style.ts: -------------------------------------------------------------------------------- 1 | export const Banner_Animation_Time = 0.5; 2 | 3 | export const Side_Margin = '8%'; 4 | 5 | export const boxShadow = '0 4px 14px 0 #1A041B0F'; 6 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { useMobileView } from './useMobileView'; 2 | export { useNavigateParams } from './useNavigationWithPramas'; 3 | export { useDebounce } from './useDebounce'; 4 | export { usePath } from './usePath'; 5 | export { useCSV } from './useCSV'; 6 | -------------------------------------------------------------------------------- /src/hooks/useAnchor.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useState } from 'react'; 2 | 3 | export interface Anchor { 4 | anchor: string; 5 | updateAnchor: (value: string) => void; 6 | } 7 | 8 | export const AnchorContext = createContext(null as any); 9 | 10 | export const AnchorContextProvider = ({ children }: any) => { 11 | const [anchor, setAnchor] = useState(''); 12 | const updateAnchor = (newValue: string) => { 13 | setAnchor(newValue); 14 | }; 15 | const contextValue = { 16 | anchor, 17 | updateAnchor, 18 | }; 19 | 20 | return ( 21 | 22 | {children} 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/hooks/useCSV.ts: -------------------------------------------------------------------------------- 1 | import { setCellWidthInExcel } from '@/utils'; 2 | import { useCallback, useEffect, useState } from 'react'; 3 | import { utils, writeFile } from 'xlsx'; 4 | 5 | export function useCSV( 6 | value: T[] 7 | ): [T[], number[], React.Dispatch>, () => void] { 8 | const [pres, setPres] = useState(value); 9 | const [widths, setWidths] = useState([]); 10 | const [rcWidths, setRcWidths] = useState([]); 11 | 12 | useEffect(() => { 13 | const [wchs, tcwchs] = setCellWidthInExcel(pres); 14 | setWidths(wchs); 15 | setRcWidths(tcwchs); 16 | }, [pres]); 17 | 18 | const exportFile = useCallback(() => { 19 | const ws = utils.json_to_sheet(pres); 20 | const wb = utils.book_new(); 21 | utils.book_append_sheet(wb, ws, 'jsontoexcel'); 22 | 23 | ws['!cols'] = widths.map((it) => ({ wch: it })); 24 | 25 | writeFile(wb, 'jsontoexcel.xlsx'); 26 | }, [pres, widths]); 27 | 28 | return [pres, rcWidths, setPres, exportFile]; 29 | } 30 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export function useDebounce(value: T, delay?: number): T { 4 | const [debouncedValue, setDebouncedValue] = useState(value); 5 | 6 | useEffect(() => { 7 | const timer = setTimeout(() => setDebouncedValue(value), delay || 500); 8 | 9 | return () => { 10 | clearTimeout(timer); 11 | }; 12 | }, [value, delay]); 13 | 14 | return debouncedValue; 15 | } 16 | -------------------------------------------------------------------------------- /src/hooks/useLikeList.tsx: -------------------------------------------------------------------------------- 1 | import { useLocalStorageState } from 'ahooks'; 2 | import { createContext, useState } from 'react'; 3 | 4 | export interface Like { 5 | likeList: string[]; 6 | updateLikeList: (value: string[]) => void; 7 | } 8 | 9 | export const LikeContext = createContext(null as any); 10 | 11 | export const LikeContextProvider = ({ children }: any) => { 12 | const [localList = [], setLocalList] = useLocalStorageState( 13 | 'like_list', 14 | { defaultValue: [] } 15 | ); 16 | const [likeList, setValue] = useState(localList); 17 | const updateLikeList = (newValue: string[]) => { 18 | setValue(newValue); 19 | setLocalList(newValue); 20 | }; 21 | const contextValue = { 22 | likeList, 23 | updateLikeList, 24 | }; 25 | 26 | return ( 27 | {children} 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /src/hooks/useMobileView.ts: -------------------------------------------------------------------------------- 1 | import { useTheme, useMediaQuery } from '@mui/material'; 2 | 3 | export const useMobileView = () => { 4 | const theme = useTheme(); 5 | const isMobile = useMediaQuery(theme.breakpoints.down('sm')); 6 | return isMobile; 7 | }; 8 | -------------------------------------------------------------------------------- /src/hooks/useNavigationWithPramas.ts: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router'; 2 | 3 | export const useNavigateParams = () => { 4 | const router = useRouter(); 5 | 6 | return (path: string) => { 7 | router.push({ pathname: path, query: router.query }); 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /src/hooks/usePath.ts: -------------------------------------------------------------------------------- 1 | import { useRouter } from 'next/router'; 2 | 3 | export const usePath = (): { 4 | root: string; 5 | path: string; 6 | getResolvedPath: (path: string) => string; 7 | } => { 8 | const router = useRouter(); 9 | const currentPath = router.pathname; 10 | const paths = currentPath.split('/')?.filter((path) => path !== ''); 11 | const currentPathRoot = paths?.[0] ?? '/'; 12 | const getResolvedPath = (path: string): string => { 13 | let resolvedPath = location.pathname; 14 | if (path) { 15 | paths.push(path); 16 | paths.unshift(''); 17 | resolvedPath = paths.join('/'); 18 | } 19 | return resolvedPath; 20 | }; 21 | 22 | return { root: currentPathRoot, path: currentPath, getResolvedPath }; 23 | }; 24 | -------------------------------------------------------------------------------- /src/hooks/useWasm.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import __wbg_init, { aggregate } from '../../public/pkg/cidr_aggregator.js'; 3 | 4 | interface WasmModule { 5 | aggregate: ( 6 | cidrs: string, 7 | reverse: boolean, 8 | exclude_reserved: boolean 9 | ) => any; 10 | } 11 | 12 | export const useWasm = () => { 13 | const [wasm, setWasm] = useState(null); 14 | 15 | useEffect(() => { 16 | async function loadWasm() { 17 | try { 18 | const wasmModuleUrl = new URL( 19 | '../../public/pkg/cidr_aggregator_bg.wasm', 20 | import.meta.url 21 | ); 22 | const wasmModule = await fetch(wasmModuleUrl).then((response) => 23 | response.arrayBuffer() 24 | ); 25 | await __wbg_init(wasmModule); 26 | setWasm({ 27 | aggregate: (cidrs, reverse, exclude_reserved) => 28 | aggregate(cidrs, reverse, exclude_reserved), 29 | }); 30 | } catch (err) { 31 | console.error('Failed to load WebAssembly module:', err); 32 | } 33 | } 34 | 35 | loadWasm(); 36 | }, []); 37 | 38 | return wasm; 39 | }; 40 | -------------------------------------------------------------------------------- /src/icon/ArrowDown.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const ArrowDown = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/icon/Check.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const Check = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/icon/CheckCircleFilled.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const CheckCircleFilled = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/icon/CheckCircleOutlined.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const CheckCircleOutlined = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/icon/Copy.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const Copy = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 8 | 9 | 10 | 20 | 21 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/icon/DashedArrow.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | import dashed_arrow from '@/asset/svgs/onboarding_arrow.svg'; 4 | 5 | export const DashedArrow = (props: SvgIconProps) => { 6 | return ; 7 | }; 8 | -------------------------------------------------------------------------------- /src/icon/Delete.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const Delete = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 15 | 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/icon/Exclamation.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const Exclamation = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/icon/Home.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const Home = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/icon/ImageIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { styled, SxProps } from '@mui/material/styles'; 3 | import Image, { StaticImageData } from 'next/image'; 4 | 5 | export const IconWrapper = styled('div')(() => ({ 6 | display: 'inline-block', 7 | transition: 'all 0.2s linear', 8 | })); 9 | 10 | // export const Image = styled("img")(() => ({ 11 | // display: "block", 12 | // width: "100%", 13 | // verticalAlign: "middle", 14 | // // transform: "translateX(-50%)", 15 | // // objectFit: "contain", 16 | // })); 17 | 18 | export interface ImageIconProps { 19 | src: string | StaticImageData; 20 | className?: string; 21 | sx?: SxProps; 22 | id?: string; 23 | onClick?: (event: React.MouseEvent) => void; 24 | } 25 | 26 | export const ImageIcon: React.FC = (props) => { 27 | const { src, className, sx, id, onClick } = props; 28 | return ( 29 | 30 | icon 31 | 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/icon/Logo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const Logo = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 15 | 16 | 17 | 18 | 26 | 30 | 34 | 35 | ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/icon/LogoWhite.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const LogoWhite = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 15 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /src/icon/RightLined.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const RightLined = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/icon/Sort.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const Sort = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/icon/WeChat.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { SvgIcon, SvgIconProps } from '@mui/material'; 3 | 4 | export const WeChat = (props: SvgIconProps) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/icon/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Logo'; 2 | export * from './LogoWhite'; 3 | export * from './WeChat'; 4 | export * from './ArrowDown'; 5 | export * from './Exclamation'; 6 | export * from './Check'; 7 | export * from './CheckCircleOutlined'; 8 | export * from './CheckCircleFilled'; 9 | export * from './ImageIcon'; 10 | export * from './Home'; 11 | export * from './RightLined'; 12 | export * from './Sort'; 13 | export * from './Delete'; 14 | export * from './Copy'; 15 | export * from './DashedArrow'; 16 | -------------------------------------------------------------------------------- /src/layouts/SideBar/index.tsx: -------------------------------------------------------------------------------- 1 | import { grayText } from '@/constant/color'; 2 | import { AnchorContext } from '@/hooks/useAnchor'; 3 | import { allTags } from '@/utils/tags'; 4 | import { Button, Paper, Stack, Typography } from '@mui/material'; 5 | import Image from 'next/image'; 6 | import React, { useContext, useEffect, useState } from 'react'; 7 | import Link from 'next/link'; 8 | import { useLocalStorageState } from 'ahooks'; 9 | 10 | const SideBar: React.FC<{}> = () => { 11 | const { anchor } = useContext(AnchorContext); 12 | const [linkAnchor, setLinkAnchor] = useState(false); 13 | const [checkAnchor, setCheckAnchor] = useState(''); 14 | const [, setScrollTop] = useLocalStorageState('home_scrollTop', { 15 | defaultValue: 0, 16 | }); 17 | 18 | useEffect(() => { 19 | if (linkAnchor) setLinkAnchor(false); 20 | else setCheckAnchor(anchor); 21 | // eslint-disable-next-line react-hooks/exhaustive-deps 22 | }, [anchor]); 23 | return ( 24 | 35 | 36 | {allTags.map((item) => ( 37 | { 42 | setCheckAnchor(item.name); 43 | setLinkAnchor(true); 44 | setScrollTop(undefined); 45 | }} 46 | href={'/home/#' + item.name} 47 | style={{ alignSelf: 'stretch' }} 48 | className='custom-link' 49 | prefetch 50 | > 51 | 77 | 78 | ))} 79 | 80 | 81 | ); 82 | }; 83 | 84 | export default SideBar; 85 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Stack } from '@mui/material'; 4 | import Text from '@/components/Text'; 5 | import Button from '@/components/Button'; 6 | import Link from 'next/link'; 7 | 8 | export const NotFound = () => { 9 | return ( 10 | 18 | 404 NOT FOUND 19 | 28 | 29 | ); 30 | }; 31 | 32 | export default NotFound; 33 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/global.css'; 2 | import '@/styles/static/slick.css'; 3 | import '@/styles/static/effect-creative.min.css'; 4 | import '@/styles/static/navigation.min.css'; 5 | import '@/styles/static/swiper-customize.css'; 6 | import '@/styles/static/swiper.css'; 7 | import theme from '@/styles/theme'; 8 | import createEmotionCache from '@/utils/emotionCache'; 9 | import { CacheProvider } from '@emotion/react'; 10 | import { Stack, ThemeProvider } from '@mui/material'; 11 | import CssBaseline from '@mui/material/CssBaseline'; 12 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; 13 | 14 | import PropTypes from 'prop-types'; 15 | import Header from '@/layouts/Header'; 16 | import SideBar from '@/layouts/SideBar'; 17 | import { LikeContextProvider } from '@/hooks/useLikeList'; 18 | import { AnchorContextProvider } from '@/hooks/useAnchor'; 19 | import { usePath } from '@/hooks'; 20 | import { useMemo } from 'react'; 21 | import { allTools } from '@/utils/tools'; 22 | import Head from 'next/head'; 23 | import RiverHeader from '../components/Header'; 24 | import '@chaitin_rivers/excalidraw/index.css'; 25 | 26 | const clientSideEmotionCache = createEmotionCache(); 27 | 28 | const queryClient = new QueryClient({ 29 | defaultOptions: { queries: { retry: false, refetchOnWindowFocus: false } }, 30 | }); 31 | 32 | export default function App({ 33 | Component, 34 | emotionCache = clientSideEmotionCache, 35 | }: any) { 36 | const { path } = usePath(); 37 | const isProduction = process.env.NODE_ENV === 'production'; 38 | const currentItem = useMemo(() => { 39 | return allTools.find((item) => item.path === path); 40 | }, [path]); 41 | return ( 42 | <> 43 | 44 | 48 | 49 | {currentItem?.label 50 | ? currentItem?.label + ' - 百川在线工具箱' 51 | : '百川云常用工具'} 52 | 53 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | {isProduction ? : null} 68 | 75 |
76 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | ); 92 | } 93 | 94 | App.propTypes = { 95 | Component: PropTypes.elementType.isRequired, 96 | emotionCache: PropTypes.object, 97 | }; 98 | -------------------------------------------------------------------------------- /src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GetServerSidePropsContext } from 'next'; 3 | import Document, { 4 | Html, 5 | Head, 6 | Main, 7 | NextScript, 8 | DocumentProps, 9 | DocumentContext, 10 | } from 'next/document'; 11 | import createEmotionServer from '@emotion/server/create-instance'; 12 | import createEmotionCache from '@/utils/emotionCache'; 13 | 14 | interface MyDocumentProps extends DocumentProps { 15 | emotionStyleTags: JSX.Element[]; 16 | } 17 | 18 | export default function MyDocument({ emotionStyleTags }: MyDocumentProps) { 19 | return ( 20 | 21 | 22 |