[enhancement]: custom css
This commit is contained in:
parent
004c9a8d06
commit
6125901023
10 changed files with 332 additions and 325 deletions
358
package-lock.json
generated
358
package-lock.json
generated
|
@ -33,6 +33,7 @@
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"cmdk": "^0.2.0",
|
"cmdk": "^0.2.0",
|
||||||
"dayjs": "^1.11.6",
|
"dayjs": "^1.11.6",
|
||||||
|
"dompurify": "^3.1.6",
|
||||||
"electron-debug": "^3.2.0",
|
"electron-debug": "^3.2.0",
|
||||||
"electron-localshortcut": "^3.2.1",
|
"electron-localshortcut": "^3.2.1",
|
||||||
"electron-log": "^5.1.1",
|
"electron-log": "^5.1.1",
|
||||||
|
@ -67,7 +68,6 @@
|
||||||
"react-virtualized-auto-sizer": "^1.0.17",
|
"react-virtualized-auto-sizer": "^1.0.17",
|
||||||
"react-window": "^1.8.9",
|
"react-window": "^1.8.9",
|
||||||
"react-window-infinite-loader": "^1.0.9",
|
"react-window-infinite-loader": "^1.0.9",
|
||||||
"sanitize-html": "^2.13.0",
|
|
||||||
"semver": "^7.5.4",
|
"semver": "^7.5.4",
|
||||||
"styled-components": "^6.0.8",
|
"styled-components": "^6.0.8",
|
||||||
"swiper": "^9.3.1",
|
"swiper": "^9.3.1",
|
||||||
|
@ -81,6 +81,7 @@
|
||||||
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
|
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^13.0.0",
|
"@testing-library/react": "^13.0.0",
|
||||||
|
"@types/dompurify": "^3.0.5",
|
||||||
"@types/electron-localshortcut": "^3.1.0",
|
"@types/electron-localshortcut": "^3.1.0",
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.4.1",
|
||||||
"@types/lodash": "^4.14.188",
|
"@types/lodash": "^4.14.188",
|
||||||
|
@ -92,7 +93,6 @@
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||||
"@types/react-window": "^1.8.5",
|
"@types/react-window": "^1.8.5",
|
||||||
"@types/react-window-infinite-loader": "^1.0.6",
|
"@types/react-window-infinite-loader": "^1.0.6",
|
||||||
"@types/sanitize-html": "^2.11.0",
|
|
||||||
"@types/styled-components": "^5.1.26",
|
"@types/styled-components": "^5.1.26",
|
||||||
"@types/terser-webpack-plugin": "^5.0.4",
|
"@types/terser-webpack-plugin": "^5.0.4",
|
||||||
"@types/webpack-bundle-analyzer": "^4.4.1",
|
"@types/webpack-bundle-analyzer": "^4.4.1",
|
||||||
|
@ -5097,6 +5097,16 @@
|
||||||
"@types/ms": "*"
|
"@types/ms": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/dompurify": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/trusted-types": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/electron-localshortcut": {
|
"node_modules/@types/electron-localshortcut": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/electron-localshortcut/-/electron-localshortcut-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/electron-localshortcut/-/electron-localshortcut-3.1.0.tgz",
|
||||||
|
@ -5430,89 +5440,6 @@
|
||||||
"integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==",
|
"integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/sanitize-html": {
|
|
||||||
"version": "2.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz",
|
|
||||||
"integrity": "sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"htmlparser2": "^8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/sanitize-html/node_modules/dom-serializer": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.2",
|
|
||||||
"entities": "^4.2.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/sanitize-html/node_modules/domhandler": {
|
|
||||||
"version": "5.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
|
||||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/sanitize-html/node_modules/domutils": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"dom-serializer": "^2.0.0",
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/sanitize-html/node_modules/entities": {
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
|
||||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/sanitize-html/node_modules/htmlparser2": {
|
|
||||||
"version": "8.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
|
||||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/fb55"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3",
|
|
||||||
"domutils": "^3.0.1",
|
|
||||||
"entities": "^4.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/scheduler": {
|
"node_modules/@types/scheduler": {
|
||||||
"version": "0.16.2",
|
"version": "0.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
|
@ -5601,6 +5528,13 @@
|
||||||
"@types/jest": "*"
|
"@types/jest": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/trusted-types": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/verror": {
|
"node_modules/@types/verror": {
|
||||||
"version": "1.10.10",
|
"version": "1.10.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz",
|
||||||
|
@ -9707,6 +9641,7 @@
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||||
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
@ -9750,6 +9685,12 @@
|
||||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dompurify": {
|
||||||
|
"version": "3.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz",
|
||||||
|
"integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==",
|
||||||
|
"license": "(MPL-2.0 OR Apache-2.0)"
|
||||||
|
},
|
||||||
"node_modules/domutils": {
|
"node_modules/domutils": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||||
|
@ -17746,11 +17687,6 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parse-srcset": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
|
|
||||||
},
|
|
||||||
"node_modules/parse5": {
|
"node_modules/parse5": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||||
|
@ -20043,96 +19979,6 @@
|
||||||
"truncate-utf8-bytes": "^1.0.0"
|
"truncate-utf8-bytes": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sanitize-html": {
|
|
||||||
"version": "2.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.0.tgz",
|
|
||||||
"integrity": "sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==",
|
|
||||||
"dependencies": {
|
|
||||||
"deepmerge": "^4.2.2",
|
|
||||||
"escape-string-regexp": "^4.0.0",
|
|
||||||
"htmlparser2": "^8.0.0",
|
|
||||||
"is-plain-object": "^5.0.0",
|
|
||||||
"parse-srcset": "^1.0.2",
|
|
||||||
"postcss": "^8.3.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sanitize-html/node_modules/dom-serializer": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.2",
|
|
||||||
"entities": "^4.2.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sanitize-html/node_modules/domhandler": {
|
|
||||||
"version": "5.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
|
||||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sanitize-html/node_modules/domutils": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
|
||||||
"dependencies": {
|
|
||||||
"dom-serializer": "^2.0.0",
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sanitize-html/node_modules/entities": {
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
|
||||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sanitize-html/node_modules/htmlparser2": {
|
|
||||||
"version": "8.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
|
||||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
|
||||||
"funding": [
|
|
||||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/fb55"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3",
|
|
||||||
"domutils": "^3.0.1",
|
|
||||||
"entities": "^4.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sanitize-html/node_modules/is-plain-object": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.50.0",
|
"version": "1.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.50.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.50.0.tgz",
|
||||||
|
@ -27525,6 +27371,15 @@
|
||||||
"@types/ms": "*"
|
"@types/ms": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/dompurify": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/trusted-types": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/electron-localshortcut": {
|
"@types/electron-localshortcut": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/electron-localshortcut/-/electron-localshortcut-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/electron-localshortcut/-/electron-localshortcut-3.1.0.tgz",
|
||||||
|
@ -27856,66 +27711,6 @@
|
||||||
"integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==",
|
"integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/sanitize-html": {
|
|
||||||
"version": "2.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz",
|
|
||||||
"integrity": "sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"htmlparser2": "^8.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"dom-serializer": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.2",
|
|
||||||
"entities": "^4.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domhandler": {
|
|
||||||
"version": "5.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
|
||||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"domelementtype": "^2.3.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domutils": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"dom-serializer": "^2.0.0",
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"entities": {
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
|
||||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"htmlparser2": {
|
|
||||||
"version": "8.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
|
||||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3",
|
|
||||||
"domutils": "^3.0.1",
|
|
||||||
"entities": "^4.4.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/scheduler": {
|
"@types/scheduler": {
|
||||||
"version": "0.16.2",
|
"version": "0.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
|
@ -28002,6 +27797,12 @@
|
||||||
"@types/jest": "*"
|
"@types/jest": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/trusted-types": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/verror": {
|
"@types/verror": {
|
||||||
"version": "1.10.10",
|
"version": "1.10.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz",
|
||||||
|
@ -31011,7 +30812,8 @@
|
||||||
"domelementtype": {
|
"domelementtype": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||||
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
|
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"domexception": {
|
"domexception": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
|
@ -31039,6 +30841,11 @@
|
||||||
"domelementtype": "^2.2.0"
|
"domelementtype": "^2.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dompurify": {
|
||||||
|
"version": "3.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz",
|
||||||
|
"integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ=="
|
||||||
|
},
|
||||||
"domutils": {
|
"domutils": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
|
||||||
|
@ -36736,11 +36543,6 @@
|
||||||
"lines-and-columns": "^1.1.6"
|
"lines-and-columns": "^1.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"parse-srcset": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
|
|
||||||
},
|
|
||||||
"parse5": {
|
"parse5": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||||
|
@ -38318,70 +38120,6 @@
|
||||||
"truncate-utf8-bytes": "^1.0.0"
|
"truncate-utf8-bytes": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sanitize-html": {
|
|
||||||
"version": "2.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.13.0.tgz",
|
|
||||||
"integrity": "sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==",
|
|
||||||
"requires": {
|
|
||||||
"deepmerge": "^4.2.2",
|
|
||||||
"escape-string-regexp": "^4.0.0",
|
|
||||||
"htmlparser2": "^8.0.0",
|
|
||||||
"is-plain-object": "^5.0.0",
|
|
||||||
"parse-srcset": "^1.0.2",
|
|
||||||
"postcss": "^8.3.11"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"dom-serializer": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
|
||||||
"requires": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.2",
|
|
||||||
"entities": "^4.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domhandler": {
|
|
||||||
"version": "5.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
|
||||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
|
||||||
"requires": {
|
|
||||||
"domelementtype": "^2.3.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domutils": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
|
||||||
"requires": {
|
|
||||||
"dom-serializer": "^2.0.0",
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"entities": {
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
|
||||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
|
|
||||||
},
|
|
||||||
"htmlparser2": {
|
|
||||||
"version": "8.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
|
||||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
|
||||||
"requires": {
|
|
||||||
"domelementtype": "^2.3.0",
|
|
||||||
"domhandler": "^5.0.3",
|
|
||||||
"domutils": "^3.0.1",
|
|
||||||
"entities": "^4.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"is-plain-object": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sass": {
|
"sass": {
|
||||||
"version": "1.50.0",
|
"version": "1.50.0",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.50.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.50.0.tgz",
|
||||||
|
|
|
@ -205,6 +205,7 @@
|
||||||
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
|
"@teamsupercell/typings-for-css-modules-loader": "^2.5.1",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^13.0.0",
|
"@testing-library/react": "^13.0.0",
|
||||||
|
"@types/dompurify": "^3.0.5",
|
||||||
"@types/electron-localshortcut": "^3.1.0",
|
"@types/electron-localshortcut": "^3.1.0",
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.4.1",
|
||||||
"@types/lodash": "^4.14.188",
|
"@types/lodash": "^4.14.188",
|
||||||
|
@ -216,7 +217,6 @@
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||||
"@types/react-window": "^1.8.5",
|
"@types/react-window": "^1.8.5",
|
||||||
"@types/react-window-infinite-loader": "^1.0.6",
|
"@types/react-window-infinite-loader": "^1.0.6",
|
||||||
"@types/sanitize-html": "^2.11.0",
|
|
||||||
"@types/styled-components": "^5.1.26",
|
"@types/styled-components": "^5.1.26",
|
||||||
"@types/terser-webpack-plugin": "^5.0.4",
|
"@types/terser-webpack-plugin": "^5.0.4",
|
||||||
"@types/webpack-bundle-analyzer": "^4.4.1",
|
"@types/webpack-bundle-analyzer": "^4.4.1",
|
||||||
|
@ -314,6 +314,7 @@
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"cmdk": "^0.2.0",
|
"cmdk": "^0.2.0",
|
||||||
"dayjs": "^1.11.6",
|
"dayjs": "^1.11.6",
|
||||||
|
"dompurify": "^3.1.6",
|
||||||
"electron-debug": "^3.2.0",
|
"electron-debug": "^3.2.0",
|
||||||
"electron-localshortcut": "^3.2.1",
|
"electron-localshortcut": "^3.2.1",
|
||||||
"electron-log": "^5.1.1",
|
"electron-log": "^5.1.1",
|
||||||
|
@ -348,7 +349,6 @@
|
||||||
"react-virtualized-auto-sizer": "^1.0.17",
|
"react-virtualized-auto-sizer": "^1.0.17",
|
||||||
"react-window": "^1.8.9",
|
"react-window": "^1.8.9",
|
||||||
"react-window-infinite-loader": "^1.0.9",
|
"react-window-infinite-loader": "^1.0.9",
|
||||||
"sanitize-html": "^2.13.0",
|
|
||||||
"semver": "^7.5.4",
|
"semver": "^7.5.4",
|
||||||
"styled-components": "^6.0.8",
|
"styled-components": "^6.0.8",
|
||||||
"swiper": "^9.3.1",
|
"swiper": "^9.3.1",
|
||||||
|
|
|
@ -384,6 +384,7 @@
|
||||||
"title": "$t(entity.playlist_other)"
|
"title": "$t(entity.playlist_other)"
|
||||||
},
|
},
|
||||||
"setting": {
|
"setting": {
|
||||||
|
"advanced": "advanced",
|
||||||
"generalTab": "general",
|
"generalTab": "general",
|
||||||
"hotkeysTab": "hotkeys",
|
"hotkeysTab": "hotkeys",
|
||||||
"playbackTab": "playback",
|
"playbackTab": "playback",
|
||||||
|
@ -467,6 +468,11 @@
|
||||||
"crossfadeDuration_description": "sets the duration of the crossfade effect",
|
"crossfadeDuration_description": "sets the duration of the crossfade effect",
|
||||||
"crossfadeStyle": "crossfade style",
|
"crossfadeStyle": "crossfade style",
|
||||||
"crossfadeStyle_description": "select the crossfade style to use for the audio player",
|
"crossfadeStyle_description": "select the crossfade style to use for the audio player",
|
||||||
|
"customCssEnable": "enable custom css",
|
||||||
|
"customCssEnable_description": "allow for writing custom css.",
|
||||||
|
"customCssNotice": "Warning: while there is some sanitization (disallowing url() and content:), using custom CSS can still pose risks by changing the interface.",
|
||||||
|
"customCss": "custom css",
|
||||||
|
"customCss_description": "custom css content. Note: content and remote urls are disallowed properties. A preview of your content is shown below. Additional fields you didn't set are present due to sanitization.",
|
||||||
"customFontPath": "custom font path",
|
"customFontPath": "custom font path",
|
||||||
"customFontPath_description": "sets the path to the custom font to use for the application",
|
"customFontPath_description": "sets the path to the custom font to use for the application",
|
||||||
"disableAutomaticUpdates": "disable automatic updates",
|
"disableAutomaticUpdates": "disable automatic updates",
|
||||||
|
|
|
@ -20,13 +20,14 @@ import { ContextMenuProvider } from '/@/renderer/features/context-menu';
|
||||||
import { useHandlePlayQueueAdd } from '/@/renderer/features/player/hooks/use-handle-playqueue-add';
|
import { useHandlePlayQueueAdd } from '/@/renderer/features/player/hooks/use-handle-playqueue-add';
|
||||||
import { PlayQueueHandlerContext } from '/@/renderer/features/player';
|
import { PlayQueueHandlerContext } from '/@/renderer/features/player';
|
||||||
import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-settings';
|
import { getMpvProperties } from '/@/renderer/features/settings/components/playback/mpv-settings';
|
||||||
import { PlayerState, usePlayerStore, useQueueControls } from '/@/renderer/store';
|
import { PlayerState, useCssSettings, usePlayerStore, useQueueControls } from '/@/renderer/store';
|
||||||
import { FontType, PlaybackType, PlayerStatus } from '/@/renderer/types';
|
import { FontType, PlaybackType, PlayerStatus } from '/@/renderer/types';
|
||||||
import '@ag-grid-community/styles/ag-grid.css';
|
import '@ag-grid-community/styles/ag-grid.css';
|
||||||
import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc';
|
import { useDiscordRpc } from '/@/renderer/features/discord-rpc/use-discord-rpc';
|
||||||
import i18n from '/@/i18n/i18n';
|
import i18n from '/@/i18n/i18n';
|
||||||
import { useServerVersion } from '/@/renderer/hooks/use-server-version';
|
import { useServerVersion } from '/@/renderer/hooks/use-server-version';
|
||||||
import { updateSong } from '/@/renderer/features/player/update-remote-song';
|
import { updateSong } from '/@/renderer/features/player/update-remote-song';
|
||||||
|
import { sanitizeCss } from '/@/renderer/utils/sanitize';
|
||||||
|
|
||||||
ModuleRegistry.registerModules([ClientSideRowModelModule, InfiniteRowModelModule]);
|
ModuleRegistry.registerModules([ClientSideRowModelModule, InfiniteRowModelModule]);
|
||||||
|
|
||||||
|
@ -43,12 +44,14 @@ export const App = () => {
|
||||||
const language = useSettingsStore((store) => store.general.language);
|
const language = useSettingsStore((store) => store.general.language);
|
||||||
const nativeImageAspect = useSettingsStore((store) => store.general.nativeAspectRatio);
|
const nativeImageAspect = useSettingsStore((store) => store.general.nativeAspectRatio);
|
||||||
const { builtIn, custom, system, type } = useSettingsStore((state) => state.font);
|
const { builtIn, custom, system, type } = useSettingsStore((state) => state.font);
|
||||||
|
const { enabled, content } = useCssSettings();
|
||||||
const { type: playbackType } = usePlaybackSettings();
|
const { type: playbackType } = usePlaybackSettings();
|
||||||
const { bindings } = useHotkeySettings();
|
const { bindings } = useHotkeySettings();
|
||||||
const handlePlayQueueAdd = useHandlePlayQueueAdd();
|
const handlePlayQueueAdd = useHandlePlayQueueAdd();
|
||||||
const { clearQueue, restoreQueue } = useQueueControls();
|
const { clearQueue, restoreQueue } = useQueueControls();
|
||||||
const remoteSettings = useRemoteSettings();
|
const remoteSettings = useRemoteSettings();
|
||||||
const textStyleRef = useRef<HTMLStyleElement>();
|
const textStyleRef = useRef<HTMLStyleElement>();
|
||||||
|
const cssRef = useRef<HTMLStyleElement>();
|
||||||
useDiscordRpc();
|
useDiscordRpc();
|
||||||
useServerVersion();
|
useServerVersion();
|
||||||
|
|
||||||
|
@ -87,6 +90,26 @@ export const App = () => {
|
||||||
}
|
}
|
||||||
}, [builtIn, custom, system, type]);
|
}, [builtIn, custom, system, type]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (enabled && content) {
|
||||||
|
// Yes, CSS is sanitized here as well. Prevent a suer from changing the
|
||||||
|
// localStorage to bypass sanitizing.
|
||||||
|
const sanitized = sanitizeCss(content);
|
||||||
|
if (!cssRef.current) {
|
||||||
|
cssRef.current = document.createElement('style');
|
||||||
|
document.body.appendChild(cssRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
cssRef.current.textContent = sanitized;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cssRef.current!.textContent = '';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, [content, enabled]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
root.style.setProperty('--primary-color', accent);
|
root.style.setProperty('--primary-color', accent);
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Stack } from '@mantine/core';
|
||||||
|
import { StylesSettings } from '/@/renderer/features/settings/components/advanced/styles-settings';
|
||||||
|
|
||||||
|
export const AdvancedTab = () => {
|
||||||
|
return (
|
||||||
|
<Stack spacing="md">
|
||||||
|
<StylesSettings />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,126 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Button, ConfirmModal, Switch, Text, Textarea } from '/@/renderer/components';
|
||||||
|
import { sanitizeCss } from '/@/renderer/utils/sanitize';
|
||||||
|
import { Code } from '@mantine/core';
|
||||||
|
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
|
||||||
|
import { closeAllModals, openModal } from '@mantine/modals';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useCssSettings, useSettingsStoreActions } from '/@/renderer/store';
|
||||||
|
|
||||||
|
export const StylesSettings = () => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { enabled, content } = useCssSettings();
|
||||||
|
const [css, setCss] = useState(content);
|
||||||
|
|
||||||
|
const { setSettings } = useSettingsStoreActions();
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
setSettings({
|
||||||
|
css: {
|
||||||
|
content: css,
|
||||||
|
enabled,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResetToDefault = () => {
|
||||||
|
setSettings({
|
||||||
|
css: {
|
||||||
|
content,
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
closeAllModals();
|
||||||
|
};
|
||||||
|
|
||||||
|
const openConfirmModal = () => {
|
||||||
|
openModal({
|
||||||
|
children: (
|
||||||
|
<ConfirmModal onConfirm={handleResetToDefault}>
|
||||||
|
<Text color="red !important">
|
||||||
|
{t('setting.customCssNotice', { postProcess: 'sentenceCase' })}
|
||||||
|
</Text>
|
||||||
|
</ConfirmModal>
|
||||||
|
),
|
||||||
|
title: t('setting.customCssEnable', { postProcess: 'sentenceCase' }),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SettingsOptions
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
checked={enabled}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (!e.currentTarget.checked) {
|
||||||
|
setSettings({
|
||||||
|
css: {
|
||||||
|
content,
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
openConfirmModal();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
description={t('setting.customCssEnable', {
|
||||||
|
context: 'description',
|
||||||
|
postProcess: 'sentenceCase',
|
||||||
|
})}
|
||||||
|
title={t('setting.customCssEnable', { postProcess: 'sentenceCase' })}
|
||||||
|
/>
|
||||||
|
{enabled && (
|
||||||
|
<>
|
||||||
|
<SettingsOptions
|
||||||
|
control={
|
||||||
|
<>
|
||||||
|
{open && (
|
||||||
|
<Button
|
||||||
|
compact
|
||||||
|
// disabled={isSaveButtonDisabled}
|
||||||
|
variant="filled"
|
||||||
|
onClick={handleSave}
|
||||||
|
>
|
||||||
|
{t('common.save', { postProcess: 'titleCase' })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
compact
|
||||||
|
variant="filled"
|
||||||
|
onClick={() => setOpen(!open)}
|
||||||
|
>
|
||||||
|
{t(open ? 'common.close' : 'common.edit', {
|
||||||
|
postProcess: 'titleCase',
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
description={t('setting.customCss', {
|
||||||
|
context: 'description',
|
||||||
|
postProcess: 'sentenceCase',
|
||||||
|
})}
|
||||||
|
title={t('setting.customCss', { postProcess: 'sentenceCase' })}
|
||||||
|
/>
|
||||||
|
{open && (
|
||||||
|
<>
|
||||||
|
<Textarea
|
||||||
|
autosize
|
||||||
|
defaultValue={css}
|
||||||
|
onBlur={(e) =>
|
||||||
|
setCss(sanitizeCss(`<style>${e.currentTarget.value}`))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Text>{t('common.preview', { postProcess: 'sentenceCase' })}: </Text>
|
||||||
|
<Code block>{css}</Code>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -29,6 +29,12 @@ const HotkeysTab = lazy(() =>
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const AdvancedTab = lazy(() =>
|
||||||
|
import('/@/renderer/features/settings/components/advanced/advanced-tab').then((module) => ({
|
||||||
|
default: module.AdvancedTab,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
const TabContainer = styled.div`
|
const TabContainer = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -65,6 +71,9 @@ export const SettingsContent = () => {
|
||||||
{t('page.setting.windowTab', { postProcess: 'sentenceCase' })}
|
{t('page.setting.windowTab', { postProcess: 'sentenceCase' })}
|
||||||
</Tabs.Tab>
|
</Tabs.Tab>
|
||||||
)}
|
)}
|
||||||
|
<Tabs.Tab value="advanced">
|
||||||
|
{t('page.setting.advanced', { postProcess: 'sentenceCase' })}
|
||||||
|
</Tabs.Tab>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<Tabs.Panel value="general">
|
<Tabs.Panel value="general">
|
||||||
<GeneralTab />
|
<GeneralTab />
|
||||||
|
@ -80,6 +89,9 @@ export const SettingsContent = () => {
|
||||||
<ApplicationTab />
|
<ApplicationTab />
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
)}
|
)}
|
||||||
|
<Tabs.Panel value="advanced">
|
||||||
|
<AdvancedTab />
|
||||||
|
</Tabs.Panel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TabContainer>
|
</TabContainer>
|
||||||
);
|
);
|
||||||
|
|
|
@ -178,6 +178,10 @@ export enum GenreTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SettingsState {
|
export interface SettingsState {
|
||||||
|
css: {
|
||||||
|
content: string;
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
discord: {
|
discord: {
|
||||||
clientId: string;
|
clientId: string;
|
||||||
enableIdle: boolean;
|
enableIdle: boolean;
|
||||||
|
@ -308,6 +312,10 @@ const getPlatformDefaultWindowBarStyle = (): Platform => {
|
||||||
const platformDefaultWindowBarStyle: Platform = getPlatformDefaultWindowBarStyle();
|
const platformDefaultWindowBarStyle: Platform = getPlatformDefaultWindowBarStyle();
|
||||||
|
|
||||||
const initialState: SettingsState = {
|
const initialState: SettingsState = {
|
||||||
|
css: {
|
||||||
|
content: '',
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
discord: {
|
discord: {
|
||||||
clientId: '1165957668758900787',
|
clientId: '1165957668758900787',
|
||||||
enableIdle: false,
|
enableIdle: false,
|
||||||
|
@ -714,3 +722,5 @@ export const useRemoteSettings = () => useSettingsStore((state) => state.remote,
|
||||||
export const useFontSettings = () => useSettingsStore((state) => state.font, shallow);
|
export const useFontSettings = () => useSettingsStore((state) => state.font, shallow);
|
||||||
|
|
||||||
export const useDiscordSetttings = () => useSettingsStore((state) => state.discord, shallow);
|
export const useDiscordSetttings = () => useSettingsStore((state) => state.discord, shallow);
|
||||||
|
|
||||||
|
export const useCssSettings = () => useSettingsStore((state) => state.css, shallow);
|
||||||
|
|
|
@ -16,10 +16,11 @@ export const replaceURLWithHTMLLinks = (text: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const link = match[0];
|
const link = match[0];
|
||||||
|
const prefix = link.startsWith('http') ? '' : 'https://';
|
||||||
elements.push(
|
elements.push(
|
||||||
<a
|
<a
|
||||||
key={lastIndex}
|
key={lastIndex}
|
||||||
href={link}
|
href={prefix + link}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,16 +1,97 @@
|
||||||
import sanitizeHtml, { IOptions, simpleTransform } from 'sanitize-html';
|
import DomPurify, { Config } from 'dompurify';
|
||||||
|
|
||||||
const SANITIZE_OPTIONS: IOptions = {
|
const SANITIZE_OPTIONS: Config = {
|
||||||
allowedAttributes: {
|
ALLOWED_ATTR: ['href'],
|
||||||
a: ['href', 'rel', 'target'],
|
ALLOWED_TAGS: ['a', 'b', 'div', 'em', 'i', 'p', 'span', 'strong'],
|
||||||
},
|
// allow http://, https://, and // (mapped to https)
|
||||||
allowedSchemes: ['http', 'https', 'mailto'],
|
ALLOWED_URI_REGEXP: /^(http(s?):)?\/\/.+/i,
|
||||||
allowedTags: ['a', 'b', 'div', 'em', 'i', 'p', 'strong'],
|
|
||||||
transformTags: {
|
|
||||||
a: simpleTransform('a', { rel: 'noopener noreferrer', target: '_blank' }),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const regex = /(url\("?)(?!data:)/gim;
|
||||||
|
|
||||||
|
const addStyles = (output: string[], styles: CSSStyleDeclaration) => {
|
||||||
|
for (let prop = styles.length - 1; prop >= 0; prop -= 1) {
|
||||||
|
const key = styles[prop] as keyof CSSStyleDeclaration;
|
||||||
|
if (key !== 'content' && styles[key]) {
|
||||||
|
const value = styles[key];
|
||||||
|
const priority = styles.getPropertyPriority(key as string);
|
||||||
|
const priorityString = priority === 'important' ? ` !important` : '';
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (!value.match(regex)) {
|
||||||
|
output.push(`${key}:${value}${priorityString};`);
|
||||||
|
}
|
||||||
|
} else if (typeof value === 'number') {
|
||||||
|
output.push(`${key}:${value}${priorityString};`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addCssRules = (rules: CSSRuleList, output: string[]) => {
|
||||||
|
for (let index = rules.length - 1; index >= 0; index -= 1) {
|
||||||
|
const rule = rules[index];
|
||||||
|
if (rule.constructor.name === 'CSSStyleRule') {
|
||||||
|
const cssRule = rule as CSSStyleRule;
|
||||||
|
output.push(`${cssRule.selectorText} {`);
|
||||||
|
if (cssRule.style) {
|
||||||
|
addStyles(output, cssRule.style);
|
||||||
|
}
|
||||||
|
output.push('}');
|
||||||
|
} else if (rule.constructor.name === 'CSSMediaRule') {
|
||||||
|
const mediaRule = rule as CSSMediaRule;
|
||||||
|
output.push(`@media ${mediaRule.media.mediaText}{`);
|
||||||
|
addCssRules(mediaRule.cssRules, output);
|
||||||
|
output.push('}');
|
||||||
|
} else if (rule.constructor.name === 'CSSKeyframesRule') {
|
||||||
|
const keyFrameRule = rule as CSSKeyframesRule;
|
||||||
|
for (let i = keyFrameRule.cssRules.length - 1; i >= 0; i -= 1) {
|
||||||
|
const frame = keyFrameRule.cssRules[i];
|
||||||
|
if (frame.constructor.name === 'CSSKeyframeRule') {
|
||||||
|
const keyframeRule = frame as CSSKeyframeRule;
|
||||||
|
if (keyframeRule.keyText) {
|
||||||
|
output.push(`${keyframeRule.keyText}{`);
|
||||||
|
if (keyframeRule.style) {
|
||||||
|
addStyles(output, keyframeRule.style);
|
||||||
|
}
|
||||||
|
output.push('}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.push('}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DomPurify.addHook('afterSanitizeAttributes', (node: Element) => {
|
||||||
|
if (node.tagName === 'A') {
|
||||||
|
if (node.getAttribute('href')?.startsWith('//')) {
|
||||||
|
node.setAttribute('href', `https:${node.getAttribute('href')}`);
|
||||||
|
}
|
||||||
|
node.setAttribute('target', '_blank');
|
||||||
|
node.setAttribute('rel', 'noopener noreferrer');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
DomPurify.addHook('uponSanitizeElement', (node: Element) => {
|
||||||
|
if (node.tagName === 'STYLE') {
|
||||||
|
const rules = (node as HTMLStyleElement).sheet?.cssRules;
|
||||||
|
if (rules) {
|
||||||
|
const output: string[] = [];
|
||||||
|
addCssRules(rules, output);
|
||||||
|
node.textContent = output.join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export const sanitize = (text: string): string => {
|
export const sanitize = (text: string): string => {
|
||||||
return sanitizeHtml(text, SANITIZE_OPTIONS);
|
return DomPurify.sanitize(text, SANITIZE_OPTIONS);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sanitizeCss = (text: string): string => {
|
||||||
|
return DomPurify.sanitize(text, {
|
||||||
|
ALLOWED_ATTR: [],
|
||||||
|
ALLOWED_TAGS: ['style'],
|
||||||
|
RETURN_DOM: true,
|
||||||
|
WHOLE_DOCUMENT: true,
|
||||||
|
}).innerText;
|
||||||
};
|
};
|
||||||
|
|
Reference in a new issue