Upgrade chart.js
This commit is contained in:
parent
3458ef250d
commit
e8f927bfd3
24 changed files with 164 additions and 239 deletions
|
@ -37,6 +37,7 @@ rules:
|
||||||
# disable this temporarily since we have a lot of JS files
|
# disable this temporarily since we have a lot of JS files
|
||||||
# and typescript-eslint runs against JS files too
|
# and typescript-eslint runs against JS files too
|
||||||
'@typescript-eslint/explicit-module-boundary-types': off
|
'@typescript-eslint/explicit-module-boundary-types': off
|
||||||
|
'@typescript-eslint/ban-ts-ignore': 'off'
|
||||||
react-hooks/rules-of-hooks: error
|
react-hooks/rules-of-hooks: error
|
||||||
react-hooks/exhaustive-deps:
|
react-hooks/exhaustive-deps:
|
||||||
- warn
|
- warn
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
"@hsjs/react-cache": "0.0.0-alpha.aa94237",
|
"@hsjs/react-cache": "0.0.0-alpha.aa94237",
|
||||||
"@reach/tooltip": "0.17.0",
|
"@reach/tooltip": "0.17.0",
|
||||||
"@reach/visually-hidden": "0.17.0",
|
"@reach/visually-hidden": "0.17.0",
|
||||||
"chart.js": "2.9.4",
|
"chart.js": "3.7.1",
|
||||||
"clsx": "^1.1.0",
|
"clsx": "^1.1.0",
|
||||||
"core-js": "3.22.4",
|
"core-js": "3.22.4",
|
||||||
"date-fns": "2.28.0",
|
"date-fns": "2.28.0",
|
||||||
|
@ -79,11 +79,10 @@
|
||||||
"@types/react": "18.0.9",
|
"@types/react": "18.0.9",
|
||||||
"@types/react-dom": "18.0.3",
|
"@types/react-dom": "18.0.3",
|
||||||
"@types/react-modal": "3.13.1",
|
"@types/react-modal": "3.13.1",
|
||||||
"@types/react-tabs": "5.0.5",
|
|
||||||
"@types/react-window": "1.8.5",
|
"@types/react-window": "1.8.5",
|
||||||
"@typescript-eslint/eslint-plugin": "5.22.0",
|
"@typescript-eslint/eslint-plugin": "5.22.0",
|
||||||
"@typescript-eslint/parser": "5.22.0",
|
"@typescript-eslint/parser": "5.22.0",
|
||||||
"@vitejs/plugin-react-refresh": "1.3.6",
|
"@vitejs/plugin-react": "1.3.2",
|
||||||
"autoprefixer": "10.4.7",
|
"autoprefixer": "10.4.7",
|
||||||
"cssnano": "5.1.7",
|
"cssnano": "5.1.7",
|
||||||
"eslint": "8.15.0",
|
"eslint": "8.15.0",
|
||||||
|
|
|
@ -13,13 +13,12 @@ specifiers:
|
||||||
'@types/react': 18.0.9
|
'@types/react': 18.0.9
|
||||||
'@types/react-dom': 18.0.3
|
'@types/react-dom': 18.0.3
|
||||||
'@types/react-modal': 3.13.1
|
'@types/react-modal': 3.13.1
|
||||||
'@types/react-tabs': 5.0.5
|
|
||||||
'@types/react-window': 1.8.5
|
'@types/react-window': 1.8.5
|
||||||
'@typescript-eslint/eslint-plugin': 5.22.0
|
'@typescript-eslint/eslint-plugin': 5.22.0
|
||||||
'@typescript-eslint/parser': 5.22.0
|
'@typescript-eslint/parser': 5.22.0
|
||||||
'@vitejs/plugin-react-refresh': 1.3.6
|
'@vitejs/plugin-react': 1.3.2
|
||||||
autoprefixer: 10.4.7
|
autoprefixer: 10.4.7
|
||||||
chart.js: 2.9.4
|
chart.js: 3.7.1
|
||||||
clsx: ^1.1.0
|
clsx: ^1.1.0
|
||||||
core-js: 3.22.4
|
core-js: 3.22.4
|
||||||
cssnano: 5.1.7
|
cssnano: 5.1.7
|
||||||
|
@ -87,7 +86,7 @@ dependencies:
|
||||||
'@hsjs/react-cache': 0.0.0-alpha.aa94237_react@18.1.0
|
'@hsjs/react-cache': 0.0.0-alpha.aa94237_react@18.1.0
|
||||||
'@reach/tooltip': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
|
'@reach/tooltip': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
|
||||||
'@reach/visually-hidden': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
|
'@reach/visually-hidden': 0.17.0_ef5jwxihqo6n7gxfmzogljlgcm
|
||||||
chart.js: 2.9.4
|
chart.js: 3.7.1
|
||||||
clsx: 1.1.1
|
clsx: 1.1.1
|
||||||
core-js: 3.22.4
|
core-js: 3.22.4
|
||||||
date-fns: 2.28.0
|
date-fns: 2.28.0
|
||||||
|
@ -133,11 +132,10 @@ devDependencies:
|
||||||
'@types/react': 18.0.9
|
'@types/react': 18.0.9
|
||||||
'@types/react-dom': 18.0.3
|
'@types/react-dom': 18.0.3
|
||||||
'@types/react-modal': 3.13.1
|
'@types/react-modal': 3.13.1
|
||||||
'@types/react-tabs': 5.0.5_react@18.1.0
|
|
||||||
'@types/react-window': 1.8.5
|
'@types/react-window': 1.8.5
|
||||||
'@typescript-eslint/eslint-plugin': 5.22.0_tal4xlmvnofklupd3hwjtzfb4q
|
'@typescript-eslint/eslint-plugin': 5.22.0_tal4xlmvnofklupd3hwjtzfb4q
|
||||||
'@typescript-eslint/parser': 5.22.0_hcfsmds2fshutdssjqluwm76uu
|
'@typescript-eslint/parser': 5.22.0_hcfsmds2fshutdssjqluwm76uu
|
||||||
'@vitejs/plugin-react-refresh': 1.3.6
|
'@vitejs/plugin-react': 1.3.2
|
||||||
autoprefixer: 10.4.7_postcss@8.4.13
|
autoprefixer: 10.4.7_postcss@8.4.13
|
||||||
cssnano: 5.1.7_postcss@8.4.13
|
cssnano: 5.1.7_postcss@8.4.13
|
||||||
eslint: 8.15.0
|
eslint: 8.15.0
|
||||||
|
@ -1849,15 +1847,6 @@ packages:
|
||||||
'@types/react': 18.0.9
|
'@types/react': 18.0.9
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/react-tabs/5.0.5_react@18.1.0:
|
|
||||||
resolution: {integrity: sha512-3CZTmjR7nNrZnYbnxp/DtK5e82mhM22dN47aYObmYLcp9fC1XjIEF0mLzGKFl1fR6/R8X7DyGh9hO6lON6LVkQ==}
|
|
||||||
deprecated: This is a stub types definition. react-tabs provides its own type definitions, so you do not need this installed.
|
|
||||||
dependencies:
|
|
||||||
react-tabs: 5.1.0_react@18.1.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- react
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/react-window/1.8.5:
|
/@types/react-window/1.8.5:
|
||||||
resolution: {integrity: sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==}
|
resolution: {integrity: sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -2025,16 +2014,18 @@ packages:
|
||||||
eslint-visitor-keys: 3.3.0
|
eslint-visitor-keys: 3.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vitejs/plugin-react-refresh/1.3.6:
|
/@vitejs/plugin-react/1.3.2:
|
||||||
resolution: {integrity: sha512-iNR/UqhUOmFFxiezt0em9CgmiJBdWR+5jGxB2FihaoJfqGt76kiwaKoVOJVU5NYcDWMdN06LbyN2VIGIoYdsEA==}
|
resolution: {integrity: sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
deprecated: This package has been deprecated in favor of @vitejs/plugin-react
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.17.10
|
'@babel/core': 7.17.10
|
||||||
|
'@babel/plugin-transform-react-jsx': 7.17.3_@babel+core@7.17.10
|
||||||
|
'@babel/plugin-transform-react-jsx-development': 7.16.7_@babel+core@7.17.10
|
||||||
'@babel/plugin-transform-react-jsx-self': 7.16.7_@babel+core@7.17.10
|
'@babel/plugin-transform-react-jsx-self': 7.16.7_@babel+core@7.17.10
|
||||||
'@babel/plugin-transform-react-jsx-source': 7.16.7_@babel+core@7.17.10
|
'@babel/plugin-transform-react-jsx-source': 7.16.7_@babel+core@7.17.10
|
||||||
'@rollup/pluginutils': 4.2.1
|
'@rollup/pluginutils': 4.2.1
|
||||||
react-refresh: 0.10.0
|
react-refresh: 0.13.0
|
||||||
|
resolve: 1.22.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -2384,24 +2375,8 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/chart.js/2.9.4:
|
/chart.js/3.7.1:
|
||||||
resolution: {integrity: sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==}
|
resolution: {integrity: sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==}
|
||||||
dependencies:
|
|
||||||
chartjs-color: 2.4.1
|
|
||||||
moment: 2.29.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/chartjs-color-string/0.6.0:
|
|
||||||
resolution: {integrity: sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==}
|
|
||||||
dependencies:
|
|
||||||
color-name: 1.1.4
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/chartjs-color/2.4.1:
|
|
||||||
resolution: {integrity: sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==}
|
|
||||||
dependencies:
|
|
||||||
chartjs-color-string: 0.6.0
|
|
||||||
color-convert: 1.9.3
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/chokidar/3.5.3:
|
/chokidar/3.5.3:
|
||||||
|
@ -2422,11 +2397,13 @@ packages:
|
||||||
/clsx/1.1.1:
|
/clsx/1.1.1:
|
||||||
resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==}
|
resolution: {integrity: sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/color-convert/1.9.3:
|
/color-convert/1.9.3:
|
||||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.3
|
color-name: 1.1.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/color-convert/2.0.1:
|
/color-convert/2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
|
@ -2437,9 +2414,11 @@ packages:
|
||||||
|
|
||||||
/color-name/1.1.3:
|
/color-name/1.1.3:
|
||||||
resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=}
|
resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/color-name/1.1.4:
|
/color-name/1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/colord/2.9.2:
|
/colord/2.9.2:
|
||||||
resolution: {integrity: sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==}
|
resolution: {integrity: sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==}
|
||||||
|
@ -4131,10 +4110,6 @@ packages:
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/moment/2.29.3:
|
|
||||||
resolution: {integrity: sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ms/2.0.0:
|
/ms/2.0.0:
|
||||||
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
|
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -4837,8 +4812,8 @@ packages:
|
||||||
react-dom: 18.1.0_react@18.1.0
|
react-dom: 18.1.0_react@18.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-refresh/0.10.0:
|
/react-refresh/0.13.0:
|
||||||
resolution: {integrity: sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ==}
|
resolution: {integrity: sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
@ -4890,6 +4865,7 @@ packages:
|
||||||
clsx: 1.1.1
|
clsx: 1.1.1
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 18.1.0
|
react: 18.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-tiny-fab/4.0.4_react@18.1.0:
|
/react-tiny-fab/4.0.4_react@18.1.0:
|
||||||
resolution: {integrity: sha512-PxT6gEnIQR2vFfeIaa1Oq4PRX+cIEDbEfbS6PyevWCQngrKfqjMKPEcZOaaURaUclB9u3RilgjkaBUQFVlbWcg==}
|
resolution: {integrity: sha512-PxT6gEnIQR2vFfeIaa1Oq4PRX+cIEDbEfbS6PyevWCQngrKfqjMKPEcZOaaURaUclB9u3RilgjkaBUQFVlbWcg==}
|
||||||
|
|
|
@ -29,6 +29,7 @@ export type ConnectionItem = {
|
||||||
chains: string[];
|
chains: string[];
|
||||||
// e.g. 'Match', 'DomainKeyword'
|
// e.g. 'Match', 'DomainKeyword'
|
||||||
rule: string;
|
rule: string;
|
||||||
|
rulePayload?: string;
|
||||||
};
|
};
|
||||||
type ConnectionsData = {
|
type ConnectionsData = {
|
||||||
downloadTotal: number;
|
downloadTotal: number;
|
||||||
|
|
|
@ -1,31 +1,33 @@
|
||||||
|
import { ClashAPIConfig } from '$src/types';
|
||||||
|
|
||||||
import { buildWebSocketURL, getURLAndInit } from '../misc/request-helper';
|
import { buildWebSocketURL, getURLAndInit } from '../misc/request-helper';
|
||||||
|
|
||||||
const endpoint = '/traffic';
|
const endpoint = '/traffic';
|
||||||
const textDecoder = new TextDecoder('utf-8');
|
const textDecoder = new TextDecoder('utf-8');
|
||||||
|
|
||||||
const Size = 150;
|
const Size = 150;
|
||||||
|
|
||||||
const traffic = {
|
const traffic = {
|
||||||
labels: Array(Size),
|
labels: Array(Size).fill(0),
|
||||||
// labels: [],
|
|
||||||
up: Array(Size),
|
up: Array(Size),
|
||||||
down: Array(Size),
|
down: Array(Size),
|
||||||
|
|
||||||
size: Size,
|
size: Size,
|
||||||
subscribers: [],
|
subscribers: [],
|
||||||
appendData(o) {
|
appendData(o: { up: number; down: number }) {
|
||||||
|
this.up.shift();
|
||||||
|
this.down.shift();
|
||||||
|
this.labels.shift();
|
||||||
|
|
||||||
|
const l = Date.now();
|
||||||
this.up.push(o.up);
|
this.up.push(o.up);
|
||||||
this.down.push(o.down);
|
this.down.push(o.down);
|
||||||
const t = new Date();
|
|
||||||
const l = '' + t.getMinutes() + t.getSeconds();
|
|
||||||
this.labels.push(l);
|
this.labels.push(l);
|
||||||
if (this.up.length > this.size) this.up.shift();
|
|
||||||
if (this.down.length > this.size) this.down.shift();
|
|
||||||
if (this.labels.length > this.size) this.labels.shift();
|
|
||||||
|
|
||||||
this.subscribers.forEach((f) => f(o));
|
this.subscribers.forEach((f) => f(o));
|
||||||
},
|
},
|
||||||
|
|
||||||
subscribe(listener) {
|
subscribe(listener: (x:any) => void) {
|
||||||
this.subscribers.push(listener);
|
this.subscribers.push(listener);
|
||||||
return () => {
|
return () => {
|
||||||
const idx = this.subscribers.indexOf(listener);
|
const idx = this.subscribers.indexOf(listener);
|
||||||
|
@ -37,11 +39,11 @@ const traffic = {
|
||||||
let fetched = false;
|
let fetched = false;
|
||||||
let decoded = '';
|
let decoded = '';
|
||||||
|
|
||||||
function parseAndAppend(x) {
|
function parseAndAppend(x: string) {
|
||||||
traffic.appendData(JSON.parse(x));
|
traffic.appendData(JSON.parse(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
function pump(reader) {
|
function pump(reader: ReadableStreamDefaultReader) {
|
||||||
return reader.read().then(({ done, value }) => {
|
return reader.read().then(({ done, value }) => {
|
||||||
const str = textDecoder.decode(value, { stream: !done });
|
const str = textDecoder.decode(value, { stream: !done });
|
||||||
decoded += str;
|
decoded += str;
|
||||||
|
@ -73,8 +75,8 @@ function pump(reader) {
|
||||||
// other value CLOSED
|
// other value CLOSED
|
||||||
// similar to ws readyState but not the same
|
// similar to ws readyState but not the same
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
|
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState
|
||||||
let wsState;
|
let wsState: number;
|
||||||
function fetchData(apiConfig) {
|
function fetchData(apiConfig: ClashAPIConfig) {
|
||||||
if (fetched || wsState === 1) return traffic;
|
if (fetched || wsState === 1) return traffic;
|
||||||
wsState = 1;
|
wsState = 1;
|
||||||
const url = buildWebSocketURL(apiConfig, endpoint);
|
const url = buildWebSocketURL(apiConfig, endpoint);
|
||||||
|
@ -92,7 +94,7 @@ function fetchData(apiConfig) {
|
||||||
return traffic;
|
return traffic;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchDataWithFetch(apiConfig) {
|
function fetchDataWithFetch(apiConfig: ClashAPIConfig) {
|
||||||
if (fetched) return traffic;
|
if (fetched) return traffic;
|
||||||
fetched = true;
|
fetched = true;
|
||||||
const { url, init } = getURLAndInit(apiConfig);
|
const { url, init } = getURLAndInit(apiConfig);
|
||||||
|
|
10
src/app.tsx
10
src/app.tsx
|
@ -1,22 +1,22 @@
|
||||||
import 'modern-normalize/modern-normalize.css';
|
import 'modern-normalize/modern-normalize.css';
|
||||||
import './misc/i18n';
|
import './misc/i18n';
|
||||||
|
|
||||||
import React from 'react';
|
import * as React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import { createRoot } from 'react-dom/client';
|
||||||
import Modal from 'react-modal';
|
import Modal from 'react-modal';
|
||||||
|
|
||||||
import Root from './components/Root';
|
import Root from './components/Root';
|
||||||
import * as swRegistration from './swRegistration';
|
import * as swRegistration from './swRegistration';
|
||||||
|
|
||||||
const rootEl = document.getElementById('app');
|
const rootEl = document.getElementById('app');
|
||||||
|
const root = createRoot(rootEl);
|
||||||
|
|
||||||
Modal.setAppElement(rootEl);
|
Modal.setAppElement(rootEl);
|
||||||
|
|
||||||
ReactDOM.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<Root />
|
<Root />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>
|
||||||
rootEl
|
|
||||||
);
|
);
|
||||||
|
|
||||||
swRegistration.register();
|
swRegistration.register();
|
||||||
|
|
|
@ -88,7 +88,7 @@ function formatConnectionDataItem(
|
||||||
download,
|
download,
|
||||||
start: now - new Date(start).valueOf(),
|
start: now - new Date(start).valueOf(),
|
||||||
chains: chains.reverse().join(' / '),
|
chains: chains.reverse().join(' / '),
|
||||||
rule: (rulePayload == null | rulePayload === '') ? rule : (`${rule}(${rulePayload})`),
|
rule: !rulePayload ? rule : `${rule}(${rulePayload})`,
|
||||||
...metadata,
|
...metadata,
|
||||||
host: `${host2}:${destinationPort}`,
|
host: `${host2}:${destinationPort}`,
|
||||||
type: `${type}(${network})`,
|
type: `${type}(${network})`,
|
||||||
|
|
|
@ -3,9 +3,9 @@ import '@fontsource/roboto-mono/latin-400.css';
|
||||||
import '@fontsource/open-sans/latin-400.css';
|
import '@fontsource/open-sans/latin-400.css';
|
||||||
import '@fontsource/open-sans/latin-700.css';
|
import '@fontsource/open-sans/latin-700.css';
|
||||||
|
|
||||||
import React, { lazy, Suspense } from 'react';
|
import * as React from 'react';
|
||||||
import { QueryClientProvider } from 'react-query';
|
import { QueryClientProvider } from 'react-query';
|
||||||
import { PartialRouteObject } from 'react-router';
|
import { RouteObject } from 'react-router';
|
||||||
import { HashRouter as Router, useRoutes } from 'react-router-dom';
|
import { HashRouter as Router, useRoutes } from 'react-router-dom';
|
||||||
import { RecoilRoot } from 'recoil';
|
import { RecoilRoot } from 'recoil';
|
||||||
import { About } from 'src/components/about/About';
|
import { About } from 'src/components/about/About';
|
||||||
|
@ -24,6 +24,8 @@ import SideBar from './SideBar';
|
||||||
import StateProvider from './StateProvider';
|
import StateProvider from './StateProvider';
|
||||||
import StyleGuide from './StyleGuide';
|
import StyleGuide from './StyleGuide';
|
||||||
|
|
||||||
|
const { lazy, Suspense } = React;
|
||||||
|
|
||||||
const Connections = lazy(() => import('./Connections'));
|
const Connections = lazy(() => import('./Connections'));
|
||||||
const Config = lazy(() => import('./Config'));
|
const Config = lazy(() => import('./Config'));
|
||||||
const Logs = lazy(() => import('./Logs'));
|
const Logs = lazy(() => import('./Logs'));
|
||||||
|
@ -38,10 +40,8 @@ const routes = [
|
||||||
{ path: '/proxies', element: <Proxies /> },
|
{ path: '/proxies', element: <Proxies /> },
|
||||||
{ path: '/rules', element: <Rules /> },
|
{ path: '/rules', element: <Rules /> },
|
||||||
{ path: '/about', element: <About /> },
|
{ path: '/about', element: <About /> },
|
||||||
process.env.NODE_ENV === 'development'
|
process.env.NODE_ENV === 'development' ? { path: '/style', element: <StyleGuide /> } : false,
|
||||||
? { path: '/style', element: <StyleGuide /> }
|
].filter(Boolean) as RouteObject[];
|
||||||
: false,
|
|
||||||
].filter(Boolean) as PartialRouteObject[];
|
|
||||||
|
|
||||||
function RouteInnerApp() {
|
function RouteInnerApp() {
|
||||||
return useRoutes(routes);
|
return useRoutes(routes);
|
||||||
|
|
|
@ -20,7 +20,13 @@ const { memo } = React;
|
||||||
|
|
||||||
const paddingBottom = 30;
|
const paddingBottom = 30;
|
||||||
|
|
||||||
function itemKey(index: number, { rules, provider }) {
|
type ItemData = {
|
||||||
|
rules: any[];
|
||||||
|
provider: any;
|
||||||
|
apiConfig: ClashAPIConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
function itemKey(index: number, { rules, provider }: ItemData) {
|
||||||
const providerQty = provider.names.length;
|
const providerQty = provider.names.length;
|
||||||
|
|
||||||
if (index < providerQty) {
|
if (index < providerQty) {
|
||||||
|
@ -88,10 +94,8 @@ function Rules({ apiConfig }: RulesProps) {
|
||||||
<ContentHeader title={t('Rules')} />
|
<ContentHeader title={t('Rules')} />
|
||||||
<TextFilter placeholder="Filter" textAtom={ruleFilterText} />
|
<TextFilter placeholder="Filter" textAtom={ruleFilterText} />
|
||||||
</div>
|
</div>
|
||||||
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'number | MutableRefObject<any>' is not assig... Remove this comment to see the full error message */}
|
|
||||||
<div ref={refRulesContainer} style={{ paddingBottom }}>
|
<div ref={refRulesContainer} style={{ paddingBottom }}>
|
||||||
<VariableSizeList
|
<VariableSizeList
|
||||||
// @ts-expect-error ts-migrate(2362) FIXME: The left-hand side of an arithmetic operation must... Remove this comment to see the full error message
|
|
||||||
height={containerHeight - paddingBottom}
|
height={containerHeight - paddingBottom}
|
||||||
width="100%"
|
width="100%"
|
||||||
itemCount={rules.length + provider.names.length}
|
itemCount={rules.length + provider.names.length}
|
||||||
|
|
|
@ -6,16 +6,8 @@ import React from 'react';
|
||||||
// this is just workaround
|
// this is just workaround
|
||||||
immer.setAutoFreeze(false);
|
immer.setAutoFreeze(false);
|
||||||
|
|
||||||
const {
|
const { createContext, memo, useMemo, useRef, useEffect, useCallback, useContext, useState } =
|
||||||
createContext,
|
React;
|
||||||
memo,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useEffect,
|
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useState,
|
|
||||||
} = React;
|
|
||||||
|
|
||||||
export { immer };
|
export { immer };
|
||||||
|
|
||||||
|
@ -46,7 +38,7 @@ export default function Provider({ initialState, actions = {}, children }) {
|
||||||
}
|
}
|
||||||
}, [getState]);
|
}, [getState]);
|
||||||
const dispatch = useCallback(
|
const dispatch = useCallback(
|
||||||
(actionId, fn) => {
|
(actionId: string | ((a: any, b: any) => any), fn: (s: any) => void) => {
|
||||||
if (typeof actionId === 'function') return actionId(dispatch, getState);
|
if (typeof actionId === 'function') return actionId(dispatch, getState);
|
||||||
|
|
||||||
const stateNext = produce(getState(), fn);
|
const stateNext = produce(getState(), fn);
|
||||||
|
@ -61,26 +53,21 @@ export default function Provider({ initialState, actions = {}, children }) {
|
||||||
},
|
},
|
||||||
[getState]
|
[getState]
|
||||||
);
|
);
|
||||||
const boundActions = useMemo(() => bindActions(actions, dispatch), [
|
const boundActions = useMemo(() => bindActions(actions, dispatch), [actions, dispatch]);
|
||||||
actions,
|
|
||||||
dispatch,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StateContext.Provider value={state}>
|
<StateContext.Provider value={state}>
|
||||||
<DispatchContext.Provider value={dispatch}>
|
<DispatchContext.Provider value={dispatch}>
|
||||||
<ActionsContext.Provider value={boundActions}>
|
<ActionsContext.Provider value={boundActions}>{children}</ActionsContext.Provider>
|
||||||
{children}
|
|
||||||
</ActionsContext.Provider>
|
|
||||||
</DispatchContext.Provider>
|
</DispatchContext.Provider>
|
||||||
</StateContext.Provider>
|
</StateContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function connect(mapStateToProps) {
|
export function connect(mapStateToProps: any) {
|
||||||
return (Component) => {
|
return (Component: any) => {
|
||||||
const MemoComponent = memo(Component);
|
const MemoComponent = memo(Component);
|
||||||
function Connected(props) {
|
function Connected(props: any) {
|
||||||
const state = useContext(StateContext);
|
const state = useContext(StateContext);
|
||||||
const dispatch = useContext(DispatchContext);
|
const dispatch = useContext(DispatchContext);
|
||||||
const mapped = mapStateToProps(state, props);
|
const mapped = mapStateToProps(state, props);
|
||||||
|
@ -92,14 +79,13 @@ export function connect(mapStateToProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// steal from https://github.com/reduxjs/redux/blob/master/src/bindActionCreators.ts
|
// steal from https://github.com/reduxjs/redux/blob/master/src/bindActionCreators.ts
|
||||||
function bindAction(action, dispatch) {
|
function bindAction(action: any, dispatch: any) {
|
||||||
return function (...args) {
|
return function (...args: any[]) {
|
||||||
// @ts-expect-error ts-migrate(2683) FIXME: 'this' implicitly has type 'any' because it does n... Remove this comment to see the full error message
|
|
||||||
return dispatch(action.apply(this, args));
|
return dispatch(action.apply(this, args));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindActions(actions, dispatch) {
|
function bindActions(actions: any, dispatch: any) {
|
||||||
const boundActions = {};
|
const boundActions = {};
|
||||||
for (const key in actions) {
|
for (const key in actions) {
|
||||||
const action = actions[key];
|
const action = actions[key];
|
||||||
|
|
|
@ -21,9 +21,7 @@ const optionsRule = [
|
||||||
{ label: 'Direct', value: 'Direct' },
|
{ label: 'Direct', value: 'Direct' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const Pane = ({ children, style }) => (
|
const Pane = ({ children, style }) => <div style={{ ...paneStyle, ...style }}>{children}</div>;
|
||||||
<div style={{ ...paneStyle, ...style }}>{children}</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
function useToggle(initialState = false) {
|
function useToggle(initialState = false) {
|
||||||
const [onoff, setonoff] = React.useState(initialState);
|
const [onoff, setonoff] = React.useState(initialState);
|
||||||
|
@ -52,12 +50,7 @@ class StyleGuide extends PureComponent {
|
||||||
</Pane>
|
</Pane>
|
||||||
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||||
<Pane>
|
<Pane>
|
||||||
<ToggleSwitch
|
<ToggleSwitch name="test" options={optionsRule} value="Rule" onChange={noop} />
|
||||||
name="test"
|
|
||||||
options={optionsRule}
|
|
||||||
value="Rule"
|
|
||||||
onChange={noop}
|
|
||||||
/>
|
|
||||||
</Pane>
|
</Pane>
|
||||||
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'style' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||||
<Pane>
|
<Pane>
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import React from 'react';
|
import * as React from 'react';
|
||||||
import S from 'react-switch';
|
import ReactSwitch from 'react-switch';
|
||||||
|
|
||||||
|
import { State } from '$src/store/types';
|
||||||
|
|
||||||
import { getTheme } from '../store/app';
|
import { getTheme } from '../store/app';
|
||||||
import { connect } from './StateProvider';
|
import { connect } from './StateProvider';
|
||||||
|
|
||||||
// workaround https://github.com/vitejs/vite/issues/2139#issuecomment-802981228
|
// workaround https://github.com/vitejs/vite/issues/2139#issuecomment-802981228
|
||||||
const Switch = S.default ? S.default : S;
|
// @ts-ignore
|
||||||
|
const Switch = ReactSwitch.default ? ReactSwitch.default : ReactSwitch;
|
||||||
|
|
||||||
function SwitchThemed({ checked = false, onChange, theme, name }) {
|
function SwitchThemed({ checked = false, onChange, theme, name }) {
|
||||||
const offColor = theme === 'dark' ? '#393939' : '#e9e9e9';
|
const offColor = theme === 'dark' ? '#393939' : '#e9e9e9';
|
||||||
|
@ -29,6 +32,4 @@ function SwitchThemed({ checked = false, onChange, theme, name }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect((s) => ({
|
export default connect((s: State) => ({ theme: getTheme(s) }))(SwitchThemed);
|
||||||
theme: getTheme(s),
|
|
||||||
}))(SwitchThemed);
|
|
||||||
|
|
|
@ -16,8 +16,7 @@ function ToggleSwitch({ options, value, name, onChange }: Props) {
|
||||||
);
|
);
|
||||||
|
|
||||||
const getPortionPercentage = useCallback(
|
const getPortionPercentage = useCallback(
|
||||||
// @ts-expect-error ts-migrate(7030) FIXME: Not all code paths return a value.
|
(idx: number) => {
|
||||||
(idx) => {
|
|
||||||
const w = Math.floor(100 / options.length);
|
const w = Math.floor(100 / options.length);
|
||||||
if (idx === options.length - 1) {
|
if (idx === options.length - 1) {
|
||||||
return 100 - options.length * w + w;
|
return 100 - options.length * w + w;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { State } from '$src/store/types';
|
||||||
|
|
||||||
import { fetchData } from '../api/traffic';
|
import { fetchData } from '../api/traffic';
|
||||||
import useLineChart from '../hooks/useLineChart';
|
import useLineChart from '../hooks/useLineChart';
|
||||||
import {
|
import {
|
||||||
|
@ -19,7 +21,7 @@ const chartWrapperStyle = {
|
||||||
maxWidth: 1000,
|
maxWidth: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapState = (s) => ({
|
const mapState = (s: State) => ({
|
||||||
apiConfig: getClashAPIConfig(s),
|
apiConfig: getClashAPIConfig(s),
|
||||||
selectedChartStyleIndex: getSelectedChartStyleIndex(s),
|
selectedChartStyleIndex: getSelectedChartStyleIndex(s),
|
||||||
});
|
});
|
||||||
|
@ -27,7 +29,7 @@ const mapState = (s) => ({
|
||||||
export default connect(mapState)(TrafficChart);
|
export default connect(mapState)(TrafficChart);
|
||||||
|
|
||||||
function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
|
function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
|
||||||
const Chart = chartJSResource.read();
|
const ChartMod = chartJSResource.read();
|
||||||
const traffic = fetchData(apiConfig);
|
const traffic = fetchData(apiConfig);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const data = useMemo(
|
const data = useMemo(
|
||||||
|
@ -48,10 +50,10 @@ function TrafficChart({ apiConfig, selectedChartStyleIndex }) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
[traffic, selectedChartStyleIndex, t]
|
[ traffic, selectedChartStyleIndex, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
useLineChart(Chart, 'trafficChart', data, traffic);
|
useLineChart(ChartMod.Chart, 'trafficChart', data, traffic);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ position: string; maxWidth: number; }' is ... Remove this comment to see the full error message
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ position: string; maxWidth: number; }' is ... Remove this comment to see the full error message
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
import React, { useMemo } from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import useLineChart from '../hooks/useLineChart';
|
import useLineChart from '../hooks/useLineChart';
|
||||||
import {
|
import { chartJSResource, chartStyles, commonDataSetProps } from '../misc/chart';
|
||||||
chartJSResource,
|
|
||||||
chartStyles,
|
|
||||||
commonDataSetProps,
|
|
||||||
} from '../misc/chart';
|
|
||||||
|
|
||||||
const extraChartOptions = {
|
const { useMemo } = React;
|
||||||
legend: {
|
|
||||||
display: false,
|
const extraChartOptions: import('chart.js').ChartOptions<'line'> = {
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
xAxes: [{ display: false }],
|
x: { display: false, type: 'category' },
|
||||||
yAxes: [{ display: false }],
|
y: { display: false, type: 'linear' },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,7 +20,7 @@ const data2 = [184e3, 183e3, 196e3, 182e3, 190e3, 186e3, 182e3, 189e3];
|
||||||
const labels = data1;
|
const labels = data1;
|
||||||
|
|
||||||
export default function TrafficChart({ id }) {
|
export default function TrafficChart({ id }) {
|
||||||
const Chart = chartJSResource.read();
|
const ChartMod = chartJSResource.read();
|
||||||
|
|
||||||
const data = useMemo(
|
const data = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -44,7 +42,7 @@ export default function TrafficChart({ id }) {
|
||||||
);
|
);
|
||||||
|
|
||||||
const eleId = 'chart-' + id;
|
const eleId = 'chart-' + id;
|
||||||
useLineChart(Chart, eleId, data, null, extraChartOptions);
|
useLineChart(ChartMod.Chart, eleId, data, null, extraChartOptions);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: 100, padding: 5 }}>
|
<div style={{ width: 100, padding: 5 }}>
|
||||||
|
|
|
@ -1,28 +1,24 @@
|
||||||
|
import type { ChartConfiguration } from 'chart.js';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { commonChartOptions } from 'src/misc/chart';
|
import { commonChartOptions } from 'src/misc/chart';
|
||||||
|
|
||||||
const { useEffect } = React;
|
const { useEffect } = React;
|
||||||
const options = commonChartOptions;
|
|
||||||
|
|
||||||
export default function useLineChart(
|
export default function useLineChart(
|
||||||
Chart,
|
chart: typeof import('chart.js').Chart,
|
||||||
elementId,
|
elementId: string,
|
||||||
data,
|
data: ChartConfiguration['data'],
|
||||||
subscription,
|
subscription: any,
|
||||||
extraChartOptions = {}
|
extraChartOptions = {}
|
||||||
) {
|
) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const ctx = document.getElementById(elementId).getContext('2d');
|
const ctx = (document.getElementById(elementId) as HTMLCanvasElement).getContext('2d');
|
||||||
const c = new Chart(ctx, {
|
const options = { ...commonChartOptions, ...extraChartOptions };
|
||||||
type: 'line',
|
const c = new chart(ctx, { type: 'line', data, options });
|
||||||
data,
|
const unsubscribe = subscription && subscription.subscribe(() => c.update());
|
||||||
options: { ...options, ...extraChartOptions },
|
|
||||||
});
|
|
||||||
const unsubscribe =
|
|
||||||
subscription && subscription.subscribe(() => c.update());
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribe && unsubscribe();
|
unsubscribe && unsubscribe();
|
||||||
c.destroy();
|
c.destroy();
|
||||||
};
|
};
|
||||||
}, [Chart, elementId, data, subscription, extraChartOptions]);
|
}, [chart, elementId, data, subscription, extraChartOptions]);
|
||||||
}
|
}
|
||||||
|
|
23
src/misc/chart-lib.ts
Normal file
23
src/misc/chart-lib.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import {
|
||||||
|
CategoryScale,
|
||||||
|
Chart,
|
||||||
|
Filler,
|
||||||
|
Legend,
|
||||||
|
LinearScale,
|
||||||
|
LineController,
|
||||||
|
LineElement,
|
||||||
|
PointElement,
|
||||||
|
} from 'chart.js';
|
||||||
|
|
||||||
|
// see https://www.chartjs.org/docs/latest/getting-started/integration.html#bundlers-webpack-rollup-etc
|
||||||
|
Chart.register(
|
||||||
|
LineElement,
|
||||||
|
PointElement,
|
||||||
|
LineController,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
Filler,
|
||||||
|
Legend
|
||||||
|
);
|
||||||
|
|
||||||
|
export { Chart };
|
|
@ -1,71 +1,36 @@
|
||||||
import { unstable_createResource as createResource } from '@hsjs/react-cache';
|
import { unstable_createResource as createResource } from '@hsjs/react-cache';
|
||||||
|
|
||||||
import prettyBytes from './pretty-bytes';
|
import prettyBytes from './pretty-bytes';
|
||||||
|
|
||||||
export const chartJSResource = createResource(() => {
|
export const chartJSResource = createResource(() => {
|
||||||
return import(
|
return import('$src/misc/chart-lib');
|
||||||
/* webpackChunkName: "chartjs" */
|
|
||||||
/* webpackPrefetch: true */
|
|
||||||
/* webpackPreload: true */
|
|
||||||
'chart.js/dist/Chart.min.js'
|
|
||||||
).then((c) => c.default);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const commonDataSetProps = {
|
export const commonDataSetProps = { borderWidth: 1, pointRadius: 0, tension: 0.2, fill: true };
|
||||||
borderWidth: 1,
|
|
||||||
lineTension: 0,
|
|
||||||
pointRadius: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const commonChartOptions = {
|
export const commonChartOptions: import('chart.js').ChartOptions<'line'> = {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: true,
|
maintainAspectRatio: true,
|
||||||
title: {
|
plugins: {
|
||||||
display: false,
|
legend: { labels: { boxWidth: 20 } }
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: true,
|
|
||||||
position: 'top',
|
|
||||||
labels: {
|
|
||||||
fontColor: '#ccc',
|
|
||||||
boxWidth: 20,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
enabled: false,
|
|
||||||
mode: 'index',
|
|
||||||
intersect: false,
|
|
||||||
animationDuration: 100,
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
mode: 'nearest',
|
|
||||||
intersect: true,
|
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
xAxes: [
|
x: { display: false, type: 'category' },
|
||||||
{
|
y: {
|
||||||
display: false,
|
type: 'linear',
|
||||||
gridLines: {
|
display: true,
|
||||||
display: false,
|
grid: {
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
yAxes: [
|
|
||||||
{
|
|
||||||
display: true,
|
display: true,
|
||||||
gridLines: {
|
color: '#555',
|
||||||
display: true,
|
drawTicks: false,
|
||||||
color: '#555',
|
borderDash: [3, 6],
|
||||||
borderDash: [3, 6],
|
drawBorder: false,
|
||||||
drawBorder: false,
|
},
|
||||||
},
|
ticks: {
|
||||||
ticks: {
|
callback(value: number) {
|
||||||
callback(value) {
|
return prettyBytes(value) + '/s ';
|
||||||
return prettyBytes(value) + '/s ';
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
// const ProxySortingOptions =
|
|
|
@ -1,10 +0,0 @@
|
||||||
const dsn = 'https://7068a15928ae45cf884dd8398fe8649c@sentry.io/1359284';
|
|
||||||
let Sentry;
|
|
||||||
export async function getSentry() {
|
|
||||||
if (Sentry) return Sentry;
|
|
||||||
const s = await import('@sentry/browser');
|
|
||||||
s.init({ dsn });
|
|
||||||
// eslint-disable-next-line require-atomic-updates
|
|
||||||
Sentry = s;
|
|
||||||
return Sentry;
|
|
||||||
}
|
|
|
@ -22,7 +22,6 @@ export const getLogStreamingPaused = (s: State) => s.app.logStreamingPaused;
|
||||||
|
|
||||||
const saveStateDebounced = debounce(saveState, 600);
|
const saveStateDebounced = debounce(saveState, 600);
|
||||||
|
|
||||||
// @ts-expect-error ts-migrate(7030) FIXME: Not all code paths return a value.
|
|
||||||
function findClashAPIConfigIndex(getState: GetStateFn, { baseURL, secret }) {
|
function findClashAPIConfigIndex(getState: GetStateFn, { baseURL, secret }) {
|
||||||
const arr = getClashAPIConfigs(getState());
|
const arr = getClashAPIConfigs(getState());
|
||||||
for (let i = 0; i < arr.length; i++) {
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
|
|
@ -108,10 +108,8 @@ export type State = {
|
||||||
export type GetStateFn = () => State;
|
export type GetStateFn = () => State;
|
||||||
export interface DispatchFn {
|
export interface DispatchFn {
|
||||||
(msg: string, change: (s: State) => void): void;
|
(msg: string, change: (s: State) => void): void;
|
||||||
(
|
(action: (dispatch: DispatchFn, getState: GetStateFn) => Promise<void>): ReturnType<
|
||||||
action: (dispatch: DispatchFn, getState: GetStateFn) => Promise<void>
|
|
||||||
): ReturnType<typeof action>;
|
|
||||||
(action: (dispatch: DispatchFn, getState: GetStateFn) => void): ReturnType<
|
|
||||||
typeof action
|
typeof action
|
||||||
>;
|
>;
|
||||||
|
(action: (dispatch: DispatchFn, getState: GetStateFn) => void): ReturnType<typeof action>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
"$schema": "https://json.schemastore.org/tsconfig",
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"$src": ["src"],
|
||||||
|
"$src/*": ["src/*"]
|
||||||
|
},
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
|
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
@ -14,20 +18,7 @@
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react",
|
"jsx": "react"
|
||||||
"downlevelIteration": true,
|
|
||||||
"importHelpers": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"checkJs": false,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"noImplicitAny": false,
|
|
||||||
"noUnusedParameters": false,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"strictNullChecks": false,
|
|
||||||
"suppressImplicitAnyIndexErrors": true,
|
|
||||||
"types": ["jest"]
|
|
||||||
},
|
},
|
||||||
"include": ["./src"]
|
"include": ["./src"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import { VitePWA } from 'vite-plugin-pwa';
|
import { VitePWA } from 'vite-plugin-pwa';
|
||||||
import reactRefresh from '@vitejs/plugin-react-refresh';
|
import react from '@vitejs/plugin-react'
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as pkg from './package.json';
|
import * as pkg from './package.json';
|
||||||
|
|
||||||
|
@ -12,15 +12,22 @@ export default defineConfig(({ mode }) => ({
|
||||||
'process.env.PUBLIC_URL': JSON.stringify('./'),
|
'process.env.PUBLIC_URL': JSON.stringify('./'),
|
||||||
},
|
},
|
||||||
base: './',
|
base: './',
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
$src: path.resolve(__dirname, './src'),
|
||||||
|
src: path.resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
publicDir: 'assets',
|
publicDir: 'assets',
|
||||||
build: {
|
build: {
|
||||||
|
// sourcemap: true,
|
||||||
// the default value is 'dist'
|
// the default value is 'dist'
|
||||||
// which make more sense
|
// which make more sense
|
||||||
// but change this may break other people's tools
|
// but change this may break other people's tools
|
||||||
outDir: 'public',
|
outDir: 'public',
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
reactRefresh(),
|
react(),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
srcDir: 'src',
|
srcDir: 'src',
|
||||||
outDir: 'public',
|
outDir: 'public',
|
||||||
|
@ -29,9 +36,4 @@ export default defineConfig(({ mode }) => ({
|
||||||
base: './',
|
base: './',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
src: path.resolve(__dirname, './src'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
Loading…
Reference in a new issue