diff --git a/package.json b/package.json index c0206bd..1506356 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,8 @@ "sass": "^1.49.7", "typescript": "^4.5.5", "vite": "^2.8.6", - "vite-plugin-monaco-editor": "^1.0.10" + "vite-plugin-monaco-editor": "^1.0.10", + "vite-plugin-svgr": "^1.1.0" }, "prettier": { "tabWidth": 2, diff --git a/src/assets/styles/layout.scss b/src/assets/styles/layout.scss index 3bf456f..25cb110 100644 --- a/src/assets/styles/layout.scss +++ b/src/assets/styles/layout.scss @@ -18,7 +18,7 @@ .the-logo { position: relative; - flex: 0 1 180px; + flex: 0 1 168px; width: 100%; max-width: 168px; max-height: 168px; @@ -27,8 +27,11 @@ text-align: center; box-sizing: border-box; - img { + img, + svg { width: 100%; + height: 100%; + pointer-events: none; } .the-newbtn { diff --git a/src/components/layout/use-custom-theme.ts b/src/components/layout/use-custom-theme.ts new file mode 100644 index 0000000..fc72a5e --- /dev/null +++ b/src/components/layout/use-custom-theme.ts @@ -0,0 +1,49 @@ +import useSWR from "swr"; +import { useMemo } from "react"; +import { createTheme } from "@mui/material"; +import { getVergeConfig } from "../../services/cmds"; + +/** + * wip: custome theme + */ +export default function useCustomTheme() { + const { data } = useSWR("getVergeConfig", getVergeConfig); + const mode = data?.theme_mode ?? "light"; + + const theme = useMemo(() => { + // const background = mode === "light" ? "#f5f5f5" : "#000"; + const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5"; + + const rootEle = document.documentElement; + rootEle.style.background = "transparent"; + rootEle.style.setProperty("--selection-color", selectColor); + + const theme = createTheme({ + breakpoints: { + values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 }, + }, + palette: { + mode, + primary: { main: "#5b5c9d" }, + text: { primary: "#637381", secondary: "#909399" }, + }, + }); + + const { palette } = theme; + + setTimeout(() => { + const dom = document.querySelector("#Gradient2"); + if (dom) { + dom.innerHTML = ` + + + + `; + } + }, 0); + + return theme; + }, [mode]); + + return { theme }; +} diff --git a/src/components/layout/use-traffic-graph.ts b/src/components/layout/use-traffic-graph.ts index ad89783..c723f17 100644 --- a/src/components/layout/use-traffic-graph.ts +++ b/src/components/layout/use-traffic-graph.ts @@ -1,19 +1,17 @@ -import { useRef } from "react"; +import { useEffect, useRef } from "react"; +import { useTheme } from "@mui/material"; const minPoint = 10; const maxPoint = 36; -const refLineAlpha = 0.5; +const refLineAlpha = 1; const refLineWidth = 2; -const refLineColor = "#ccc"; const upLineAlpha = 0.6; const upLineWidth = 4; -const upLineColor = "#9c27b0"; const downLineAlpha = 1; const downLineWidth = 4; -const downLineColor = "#5b5c9d"; /** * draw the traffic graph @@ -24,11 +22,23 @@ export default function useTrafficGraph() { const styleRef = useRef(true); const canvasRef = useRef(null!); + const { palette } = useTheme(); + const paletteRef = useRef(palette); + + useEffect(() => { + paletteRef.current = palette; + }, [palette]); + const drawGraph = () => { const canvas = canvasRef.current!; if (!canvas) return; + const { primary, secondary, divider } = paletteRef.current; + const refLineColor = divider || "rgba(0, 0, 0, 0.12)"; + const upLineColor = secondary.main || "#9c27b0"; + const downLineColor = primary.main || "#5b5c9d"; + const context = canvas.getContext("2d")!; const width = canvas.width; const height = canvas.height; diff --git a/src/main.tsx b/src/main.tsx index 849f898..aaf0b37 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,4 +1,5 @@ /// +/// import "./assets/styles/index.scss"; import React from "react"; diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx index e892526..98cf4d7 100644 --- a/src/pages/_layout.tsx +++ b/src/pages/_layout.tsx @@ -2,20 +2,21 @@ import dayjs from "dayjs"; import i18next from "i18next"; import relativeTime from "dayjs/plugin/relativeTime"; import useSWR, { SWRConfig, useSWRConfig } from "swr"; -import { useEffect, useMemo } from "react"; +import { useEffect } from "react"; import { useTranslation } from "react-i18next"; import { Route, Routes } from "react-router-dom"; -import { alpha, createTheme, List, Paper, ThemeProvider } from "@mui/material"; +import { alpha, List, Paper, ThemeProvider } from "@mui/material"; import { listen } from "@tauri-apps/api/event"; import { appWindow } from "@tauri-apps/api/window"; import { routers } from "./_routers"; import { getAxios } from "../services/api"; import { getVergeConfig } from "../services/cmds"; -import LogoSvg from "../assets/image/logo.svg"; +import { ReactComponent as LogoSvg } from "../assets/image/logo.svg"; import LayoutItem from "../components/layout/layout-item"; import LayoutControl from "../components/layout/layout-control"; import LayoutTraffic from "../components/layout/layout-traffic"; import UpdateButton from "../components/layout/update-button"; +import useCustomTheme from "../components/layout/use-custom-theme"; import getSystem from "../utils/get-system"; import "dayjs/locale/zh-cn"; @@ -26,10 +27,11 @@ const OS = getSystem(); const Layout = () => { const { t } = useTranslation(); const { mutate } = useSWRConfig(); - const { data } = useSWR("getVergeConfig", getVergeConfig); - const blur = !!data?.theme_blur; - const mode = data?.theme_mode ?? "light"; + const { theme } = useCustomTheme(); + + const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig); + const { theme_blur, language } = vergeConfig || {}; useEffect(() => { window.addEventListener("keydown", (e) => { @@ -48,37 +50,11 @@ const Layout = () => { }, []); useEffect(() => { - if (data?.language) { - dayjs.locale(data.language === "zh" ? "zh-cn" : data.language); - i18next.changeLanguage(data.language); + if (language) { + dayjs.locale(language === "zh" ? "zh-cn" : language); + i18next.changeLanguage(language); } - }, [data?.language]); - - const theme = useMemo(() => { - // const background = mode === "light" ? "#f5f5f5" : "#000"; - const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5"; - - const rootEle = document.documentElement; - rootEle.style.background = "transparent"; - rootEle.style.setProperty("--selection-color", selectColor); - - return createTheme({ - breakpoints: { - values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 }, - }, - palette: { - mode, - primary: { main: "#5b5c9d" }, - text: { primary: "#637381", secondary: "#909399" }, - }, - }); - }, [mode]); - - const onDragging = (e: any) => { - if (e?.target?.dataset?.windrag) { - appWindow.startDragging(); - } - }; + }, [language]); return ( @@ -87,20 +63,22 @@ const Layout = () => { square elevation={0} className={`${OS} layout`} - onPointerDown={onDragging} + onPointerDown={(e: any) => { + if (e.target?.dataset?.windrag) appWindow.startDragging(); + }} onContextMenu={(e) => { // only prevent it on Windows if (OS === "windows") e.preventDefault(); }} sx={[ - (theme) => ({ - bgcolor: alpha(theme.palette.background.paper, blur ? 0.85 : 1), + ({ palette }) => ({ + bgcolor: alpha(palette.background.paper, theme_blur ? 0.85 : 1), }), ]} >
- +
diff --git a/vite.config.ts b/vite.config.ts index 97daae4..c85e941 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,11 +1,12 @@ import { defineConfig } from "vite"; +import svgr from "vite-plugin-svgr"; import react from "@vitejs/plugin-react"; import monaco from "vite-plugin-monaco-editor"; // https://vitejs.dev/config/ export default defineConfig({ root: "src", - plugins: [react(), monaco()], + plugins: [svgr(), react(), monaco()], build: { outDir: "../dist", emptyOutDir: true, diff --git a/yarn.lock b/yarn.lock index fd8c120..983252f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38,7 +38,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== -"@babel/core@^7.16.12": +"@babel/core@^7.15.5", "@babel/core@^7.16.12": version "7.17.8" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.8.tgz#3dac27c190ebc3a4381110d46c80e77efe172e1a" integrity sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ== @@ -259,7 +259,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.16.7", "@babel/types@^7.17.0": +"@babel/types@^7.15.6", "@babel/types@^7.16.7", "@babel/types@^7.17.0": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== @@ -572,6 +572,87 @@ estree-walker "^2.0.1" picomatch "^2.2.2" +"@svgr/babel-plugin-add-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz#bd6d1ff32a31b82b601e73672a789cc41e84fe18" + integrity sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA== + +"@svgr/babel-plugin-remove-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz#58654908beebfa069681a83332544b17e5237e89" + integrity sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw== + +"@svgr/babel-plugin-remove-jsx-empty-expression@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz#d06dd6e8a8f603f92f9979bb9990a1f85a4f57ba" + integrity sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz#0b85837577b02c31c09c758a12932820f5245cee" + integrity sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ== + +"@svgr/babel-plugin-svg-dynamic-title@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz#28236ec26f7ab9d486a487d36ae52d58ba15676f" + integrity sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg== + +"@svgr/babel-plugin-svg-em-dimensions@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz#40267c5dea1b43c4f83a0eb6169e08b43d8bafce" + integrity sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA== + +"@svgr/babel-plugin-transform-react-native-svg@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz#eb688d0a5f539e34d268d8a516e81f5d7fede7c9" + integrity sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ== + +"@svgr/babel-plugin-transform-svg-component@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.2.0.tgz#7ba61d9fc1fb42b0ba1a04e4630019fa7e993c4f" + integrity sha512-bhYIpsORb++wpsp91fymbFkf09Z/YEKR0DnFjxvN+8JHeCUD2unnh18jIMKnDJTWtvpTaGYPXELVe4OOzFI0xg== + +"@svgr/babel-preset@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.2.0.tgz#1d3ad8c7664253a4be8e4a0f0e6872f30d8af627" + integrity sha512-4WQNY0J71JIaL03DRn0vLiz87JXx0b9dYm2aA8XHlQJQoixMl4r/soYHm8dsaJZ3jWtkCiOYy48dp9izvXhDkQ== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^6.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.0.0" + "@svgr/babel-plugin-svg-dynamic-title" "^6.0.0" + "@svgr/babel-plugin-svg-em-dimensions" "^6.0.0" + "@svgr/babel-plugin-transform-react-native-svg" "^6.0.0" + "@svgr/babel-plugin-transform-svg-component" "^6.2.0" + +"@svgr/core@^6.2.0": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.2.1.tgz#195de807a9f27f9e0e0d678e01084b05c54fdf61" + integrity sha512-NWufjGI2WUyrg46mKuySfviEJ6IxHUOm/8a3Ph38VCWSp+83HBraCQrpEM3F3dB6LBs5x8OElS8h3C0oOJaJAA== + dependencies: + "@svgr/plugin-jsx" "^6.2.1" + camelcase "^6.2.0" + cosmiconfig "^7.0.1" + +"@svgr/hast-util-to-babel-ast@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.2.1.tgz#ae065567b74cbe745afae617053adf9a764bea25" + integrity sha512-pt7MMkQFDlWJVy9ULJ1h+hZBDGFfSCwlBNW1HkLnVi7jUhyEXUaGYWi1x6bM2IXuAR9l265khBT4Av4lPmaNLQ== + dependencies: + "@babel/types" "^7.15.6" + entities "^3.0.1" + +"@svgr/plugin-jsx@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.2.1.tgz#5668f1d2aa18c2f1bb7a1fc9f682d3f9aed263bd" + integrity sha512-u+MpjTsLaKo6r3pHeeSVsh9hmGRag2L7VzApWIaS8imNguqoUwDq/u6U/NDmYs/KAsrmtBjOEaAAPbwNGXXp1g== + dependencies: + "@babel/core" "^7.15.5" + "@svgr/babel-preset" "^6.2.0" + "@svgr/hast-util-to-babel-ast" "^6.2.1" + svg-parser "^2.0.2" + "@tauri-apps/api@^1.0.0-rc.3": version "1.0.0-rc.3" resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-1.0.0-rc.3.tgz#e80ddbe832bf003537d94801d508a7f7e25ac48b" @@ -865,6 +946,11 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + caniuse-lite@^1.0.30001317: version "1.0.30001320" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz#8397391bec389b8ccce328636499b7284ee13285" @@ -954,6 +1040,17 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" +cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + cross-spawn@^7.0.0: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1010,6 +1107,11 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" +entities@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1341,7 +1443,7 @@ immutable@^4.0.0: resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23" integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw== -import-fresh@^3.1.0: +import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -1884,6 +1986,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svg-parser@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + swr@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/swr/-/swr-1.2.2.tgz#6cae09928d30593a7980d80f85823e57468fac5d" @@ -1936,6 +2043,13 @@ vite-plugin-monaco-editor@^1.0.10: resolved "https://registry.yarnpkg.com/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.0.10.tgz#cd370f68d4121bced6f902c6284649cc8eca4170" integrity sha512-7yTAFIE0SefjCmfnjrvXOl53wkxeSASc/ZIcB5tZeEK3vAmHhveV8y3f90Vp8b+PYdbUipjqf91mbFbSENkpcw== +vite-plugin-svgr@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-1.1.0.tgz#75b0e1eb55acadb8ab2666b736e9d1428887326f" + integrity sha512-fuwfRpUoMBX3wr47JVt4b4xpMYujONDGyDwZFE4JuohVTJovJ4VCNeAb3//mzdRxdzeHEfJuedpoHTvwPsqisA== + dependencies: + "@svgr/core" "^6.2.0" + vite@^2.8.6: version "2.8.6" resolved "https://registry.yarnpkg.com/vite/-/vite-2.8.6.tgz#32d50e23c99ca31b26b8ccdc78b1d72d4d7323d3" @@ -1983,7 +2097,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -yaml@^1.7.2: +yaml@^1.10.0, yaml@^1.7.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==