From 147cc3e0faf7f7c04316c99a35fff960b4a75b8d Mon Sep 17 00:00:00 2001 From: videogame hacker Date: Sat, 10 Jul 2021 16:57:08 +0100 Subject: [PATCH] Initial commit - Split from Lesbos theme --- .editorconfig | 12 + .gitignore | 1 + Cargo.lock | 1168 ++++++++++++++++++++++++++++++++ Cargo.toml | 18 + LICENSE.md | 38 ++ README.md | 18 + src/injector.rs | 20 + src/main.rs | 133 ++++ src/open_url.rs | 36 + src/panel/index.html | 51 ++ src/panel/injector.template.js | 114 ++++ src/panel/panel.css | 87 +++ src/panel/panel.js | 47 ++ src/themes.rs | 48 ++ styles/.gitignore | 2 + 15 files changed, 1793 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 src/injector.rs create mode 100644 src/main.rs create mode 100644 src/open_url.rs create mode 100644 src/panel/index.html create mode 100644 src/panel/injector.template.js create mode 100644 src/panel/panel.css create mode 100644 src/panel/panel.js create mode 100644 src/themes.rs create mode 100644 styles/.gitignore diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bcb5235 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = true + +[*.rs] +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..30804e6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1168 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" +dependencies = [ + "libc", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "discord-css-injector" +version = "0.1.0" +dependencies = [ + "include_dir", + "minifier", + "once_cell", + "serde", + "serde_json", + "tokio", + "warp", + "winapi", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" + +[[package]] +name = "futures-io" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" + +[[package]] +name = "futures-sink" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" + +[[package]] +name = "futures-task" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" + +[[package]] +name = "futures-util" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" +dependencies = [ + "autocfg", + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "h2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "headers" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b7591fb62902706ae8e7aaff416b1b0fa2c0fd0878b46dc13baa3712d8a855" +dependencies = [ + "base64", + "bitflags", + "bytes", + "headers-core", + "http", + "mime", + "sha-1", + "time", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" + +[[package]] +name = "httpdate" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" + +[[package]] +name = "hyper" +version = "0.14.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7728a72c4c7d72665fde02204bcbd93b247721025b222ef78606f14513e0fd03" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "include_dir" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31a924bd335356c7622dff9ee33d06920afcf7f762a1a991236645e08c8a484b" +dependencies = [ + "glob", + "include_dir_impl", + "proc-macro-hack", +] + +[[package]] +name = "include_dir_impl" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afae3917f781921d7c7813992ccadff7e816f7e6ecb4b70a9ec3e740d51da3d6" +dependencies = [ + "anyhow", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "input_buffer" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413" +dependencies = [ + "bytes", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" + +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "macro-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e72f7deb758fea9ea7d290aebfa788763d0bffae12caa6406a25baaf8fa68a8" + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minifier" +version = "0.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5594542d20834f2b974f5e5fb8e0cf1c67a2119dcadc29ef5d93a081fb30cc08" +dependencies = [ + "macro-utils", +] + +[[package]] +name = "mio" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "multipart" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050aeedc89243f5347c3e237e3e13dc76fbe4ae3742a57b94dc14f69acf76d4" +dependencies = [ + "buf_redux", + "httparse", + "log", + "mime", + "mime_guess", + "quick-error", + "rand 0.7.3", + "safemem", + "tempfile", + "twoway", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", + "rand_hc 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.3", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + +[[package]] +name = "redox_syscall" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "socket2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand 0.8.4", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c8b05dc14c75ea83d63dd391100353789f5f24b8b3866542a5e85c8be8e985" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1a5f475f1b9d077ea1017ecbc60890fda8e54942d680ca0b1d2b47cfa2d861b" +dependencies = [ + "futures-util", + "log", + "pin-project", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "tungstenite" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ada8297e8d70872fa9a551d93250a9f407beb9f37ef86494eb20012a2ff7c24" +dependencies = [ + "base64", + "byteorder", + "bytes", + "http", + "httparse", + "input_buffer", + "log", + "rand 0.8.4", + "sha-1", + "url", + "utf-8", +] + +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "warp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332d47745e9a0c38636dbd454729b147d16bd1ed08ae67b3ab281c4506771054" +dependencies = [ + "bytes", + "futures", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "multipart", + "percent-encoding", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tokio-util", + "tower-service", + "tracing", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..98254d2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "discord-css-injector" +authors = ["videogame hacker "] +version = "0.1.0" +edition = "2018" + +[dependencies] +include_dir = "0.6.1" +minifier = "0.0.41" +once_cell = "1.8.0" +serde = { version = "1.0.126", features = ["derive"] } +serde_json = "1.0.64" +tokio = { version = "1.8.0", features = ["full"] } +warp = "0.3.1" + +# Why is opening a browser on Windows so annoying??? +[target.'cfg(target_os = "windows")'.dependencies] +winapi = { version = "0.3.9", features = ["shellapi"] } diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d6bdf3b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,38 @@ +The Charlotte Public License version 0.1 + +Copyright 2021, Charlotte Som (the "Author" henceforth). + +This license gives everyone permission to examine, modify, and use this software +and the associated documentation (the "Inator"), without patent obstacles, while protecting +the Author and any contributors (the "Composers") from liability. + +Each Composer permits you to examine, modify, utilize, and distribute the Inator +where it would otherwise infringe upon that Composer's copyright or any patent claims that +they hold. + +No contributor may revoke this license, but the Author may choose to release the Inator +(including the contributed works of any other Composer) under a different license. + +You may not use the Inator to accrue revenue without explicit permission from the Author. + +You may not use the Inator to do Malevolence. If you are +notified that you have committed a Malevolence instrumented by the Inator, your license is +terminated unless you take all practical steps to comply within a reasonable timeframe. + +The definition of Malevolence is at the discretion of the Author. It may include, but is not +limited to: + +- The promotion of bigotry, including: sexism, transphobia, homophobia, ableism, or the +perpetuation of racial oppression. +- Causing a detriment to public health. +- Instigating political, economic, or corporeal violence. +- Entrenching an empire. +- Where applicable, use of the Inator without the informed consent of a second party who may +object to its use. + +The Inator is provided without any warranty, "as-is". No Composer is liable for any damages +related to the Inator. + +In order to receive this license, you must agree to the terms set out in this document. +This license, authorial attribution, and copyright notice must be distributed with +any copies or large portions of the Inator. diff --git a/README.md b/README.md new file mode 100644 index 0000000..094fda7 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# discord-css-injector + +This is a theme injector that doesn't require patching the Discord client. +As a bonus, this means that you can easily use themes in Discord's web application, +but the downside is that you have to apply your theme manually every time you start Discord. + +## Usage + +```shell +$ cargo run +[...] +Listening on: http://127.0.0.1:12345/ +``` + +1. A browser should open at the given URL +2. Choose the theme you want to apply +3. Hit 'Copy JS' +4. Paste into Discord's DevTools console (you can hit Ctrl + Shift + I to open this) diff --git a/src/injector.rs b/src/injector.rs new file mode 100644 index 0000000..fcccd62 --- /dev/null +++ b/src/injector.rs @@ -0,0 +1,20 @@ +use std::net::SocketAddr; + +use crate::themes::Theme; + +const INJECTOR_TEMPLATE: &str = include_str!("./panel/injector.template.js"); + +pub fn render_injector(current_addr: &SocketAddr, theme: &Theme) -> String { + let stylesheet_urls: Vec = theme + .files + .iter() + .map(|f| format!("http://{}/styles/{}/{}", current_addr, &theme.slug, f)) + .collect(); + + let stylesheet_urls = + serde_json::to_string(&stylesheet_urls).expect("Couldn't encode stylesheet URLs"); + + let injector_src = INJECTOR_TEMPLATE.replace("/* STYLESHEET_URLS */null", &stylesheet_urls); + + minifier::js::minify(&injector_src) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1dad3a7 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,133 @@ +// Incantation to not allocate a Windows console when compiled in release mode +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +use std::{collections::HashMap, net::SocketAddr, time::Duration}; + +use include_dir::{include_dir, Dir}; +use once_cell::sync::{Lazy, OnceCell}; +use warp::{ + http::HeaderValue, + hyper::HeaderMap, + reply::{json, Response}, + Filter, Rejection, Reply, +}; + +mod injector; +mod open_url; +mod themes; + +use crate::themes::{read_theme_from_dir, Theme}; + +static CURRENT_ADDRESS: OnceCell = OnceCell::new(); +static THEMES: Lazy> = Lazy::new(|| { + let mut themes = HashMap::new(); + + for entry in std::fs::read_dir("styles").unwrap() { + if entry.is_err() { + continue; + } + + let path = entry.unwrap().path(); + if path.is_dir() { + if let Some(theme) = read_theme_from_dir(path) { + themes.insert(theme.slug.clone(), theme); + } + } + } + + themes +}); + +fn injector(theme_name: String) -> String { + if let Some(theme) = THEMES.get(&theme_name) { + let current_addr = CURRENT_ADDRESS.get().expect("Couldn't get current address"); + injector::render_injector(current_addr, theme) + } else { + "// Unknown theme, sorry!".to_string() + } +} + +fn themes() -> impl Reply { + let themes: &HashMap<_, _> = &THEMES; + json(themes) +} + +fn shutdown() -> String { + let _ = std::thread::spawn(|| { + std::thread::sleep(Duration::from_millis(500)); + std::process::exit(0) + }); + + "Goodbye!".to_string() +} + +const PANEL_DIR: Dir = include_dir!("./src/panel"); + +async fn serve_panel_file(path: warp::path::Tail) -> Result { + let mut file_name = path.as_str().to_string(); + if ("/".to_string() + &file_name).ends_with("/") { + file_name.push_str("index.html"); + } + + if let Some(file) = PANEL_DIR.get_file(&file_name) { + let (mut head, body) = Response::new(file.contents().into()).into_parts(); + + let extension = file_name.rfind('.').map(|idx| &file_name[idx..]); + let content_type = match extension { + Some(".html") => "text/html; charset=utf-8", + Some(".css") => "text/css; charset=utf-8", + Some(".js") => "application/javascript; charset=utf-8", + Some(".txt") => "text/plain; charset=utf-8", + _ => "application/octet-stream", + }; + + head.headers + .insert("Content-Type", HeaderValue::from_str(content_type).unwrap()); + + return Ok(Response::from_parts(head, body)); + } + + Err(warp::reject::not_found()) +} + +#[tokio::main] +async fn main() { + let styles_route = { + let mut static_headers = HeaderMap::new(); + for (h, v) in [ + ("Access-Control-Allow-Origin", "*"), + ("Pragma", "no-cache"), + ("Cache-Control", "no-cache"), + ] { + static_headers.insert(h, HeaderValue::from_static(v)); + } + + warp::path("styles") + .and(warp::fs::dir("styles")) + .with(warp::reply::with::headers(static_headers)) + }; + + let themes_list_route = warp::path("themes.json").map(themes); + let injector_route = warp::path!("theme" / String / "injector.js").map(injector); + let panel_route = warp::get() + .and(warp::path::tail()) + .and_then(serve_panel_file); + let shutdown_route = warp::post().and(warp::path("shutdown")).map(shutdown); + + let routes = styles_route + .or(injector_route) + .or(themes_list_route) + .or(panel_route) + .or(shutdown_route); + + let (addr, listener) = warp::serve(routes).bind_ephemeral(([127, 0, 0, 1], 0)); + println!("Listening on: http://{}/", &addr); + + open_url::open(&format!("http://{}/", addr)); + + CURRENT_ADDRESS + .set(addr) + .expect("Could not set current address"); + + listener.await; +} diff --git a/src/open_url.rs b/src/open_url.rs new file mode 100644 index 0000000..3db5ba9 --- /dev/null +++ b/src/open_url.rs @@ -0,0 +1,36 @@ +#[cfg(target_os = "windows")] +pub fn open(url: &str) { + extern crate winapi; + + use std::ffi::CString; + use std::ptr; + use winapi::um::shellapi::ShellExecuteA; + + unsafe { + let open_str = CString::new("open").unwrap(); + let url_str = CString::new(url.to_string().replace("\n", "%0A")).unwrap(); + + ShellExecuteA( + ptr::null_mut(), + open_str.as_ptr(), + url_str.as_ptr(), + ptr::null(), + ptr::null(), + winapi::um::winuser::SW_SHOWNORMAL, + ); + } +} + +#[cfg(target_os = "macos")] +pub fn open(url: &str) { + let _ = std::process::Command::new("open") + .arg(url.to_string()) + .output(); +} + +#[cfg(target_os = "linux")] +pub fn open(url: &str) { + let _ = std::process::Command::new("xdg-open") + .arg(url.to_string()) + .output(); +} diff --git a/src/panel/index.html b/src/panel/index.html new file mode 100644 index 0000000..ac40e0b --- /dev/null +++ b/src/panel/index.html @@ -0,0 +1,51 @@ + + + + + + discord themes - local injector + + + + +
+
+

discord themes

+ without patching your client :) +
+ +
+

Themes

+ +

choose a theme, hit the 'Copy JS' button, and paste it into the Discord console:

+ +
    +
    + +
    +
    +

    Exit

    +

    To exit the theming provider, you can hit this button.

    + + +
    +
    +
    + +
    +

    + hey, uh, discord say that pasting untrusted things into the console + can give malicious actors access to your account. + + this is entirely true. be careful! +

    + +

    i pinkie swear that I'm not trying to hack you, though

    + +

    a project by charlotte som.

    +
    + + + + + diff --git a/src/panel/injector.template.js b/src/panel/injector.template.js new file mode 100644 index 0000000..74f64f6 --- /dev/null +++ b/src/panel/injector.template.js @@ -0,0 +1,114 @@ +{ + const STYLESHEET_URLS = /* STYLESHEET_URLS */null; + + const denormalisedRegex = /(.*)-[A-Za-z0-9\-\_]{6}$/; + const setupNormalization = () => { + const addNormalizedClassNames = (element) => { + const normalizedClasses = []; + + if (!element.classList) + return; + + for (const className of element.classList) { + const matches = className.match(denormalisedRegex); + if (matches) { + if (matches[0].startsWith("hljs-")) + continue; + + const normalized = matches[1]; + normalizedClasses.push(normalized); + } + } + + normalizedClasses.forEach(it => element.classList.add(it)); + }; + + for (const element of document.querySelectorAll("*")) + addNormalizedClassNames(element); + + const obs = new MutationObserver((mutations, observer) => { + for (const mutation of mutations) { + if (mutation.type === "childList") { + for (const node of mutation.addedNodes) { + addNormalizedClassNames(node); + + for (const subNode of node.querySelectorAll("*")) + addNormalizedClassNames(subNode); + } + } + + if (mutation.type === "attributes") { + // If we don't check this, we get into some infinite loop shenanigans + let alreadyNormalized = false; + for (const className of mutation.target.classList) { + if (!className.match(denormalisedRegex)) { + alreadyNormalized = true; + break; + } + } + + if (!alreadyNormalized) + addNormalizedClassNames(mutation.target); + } + } + }); + + obs.observe( + document.documentElement, + { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ["class"] + } + ); + }; + + const injectSingleCSS = async (url) => { + const text = await fetch(url).then(r => r.text()); + + const style = document.createElement("style"); + style.innerText = text; + + document.head.appendChild(style); + + return style; + }; + + const main = async () => { + if (window.stylesheetsToInject) { + window.stylesheetsToInject = STYLESHEET_URLS; + window.reloadCSS(); + return; + } + + setupNormalization(); + + let tags = []; + const applyCSS = async (urls) => { + const promises = []; + for (const url of urls) + promises.push(injectSingleCSS(url)); + + tags = []; + for (const t of (await Promise.all(promises))) + tags.push(t); + }; + + window.stylesheetsToInject = STYLESHEET_URLS; + await applyCSS(window.stylesheetsToInject); + window.reloadCSS = async () => { + for (const tag of tags) { + tag.remove(); + } + + await applyCSS(window.stylesheetsToInject); + }; + }; + + if (document.readyState === "complete") { + main(); + } else { + document.addEventListener("DOMContentLoaded", main); + } +} diff --git a/src/panel/panel.css b/src/panel/panel.css new file mode 100644 index 0000000..25984cb --- /dev/null +++ b/src/panel/panel.css @@ -0,0 +1,87 @@ +:root { + --bg: #292841 !important; + --accent: #b4a4f8; + --accent-dark: #8971f4; + --success: #ffd1dc; + --fg: #fbfbfb !important; +} + +html, body { + font-family: sans-serif; + background-color: var(--bg); + color: var(--fg); +} + +body { + margin-top: 0; + margin-bottom: 0; + padding-top: 0; + padding-bottom: 0; + + min-height: 100vh; + display: flex; + flex-direction: column; +} + +a { + color: var(--accent); +} + +main { + flex: 1; +} + +main, footer { + margin: 0 auto; + max-width: 60ch; +} + +header { + padding-bottom: 1em; + margin-bottom: 1em; + border-bottom: 1px solid var(--fg); +} + +footer { + padding-top: 1em; + margin-top: 1em; + border-top: 1px solid var(--fg); +} + +button, input[type=submit] { + background-color: var(--accent); + border: var(--accent-dark); + border-radius: 6px; + color: var(--fg); + padding: 0.25em 0.5em; + display: inline-block; + text-align: center; + white-space: nowrap; + vertical-align: middle; + + user-select: none; + border: 1px solid rgba(0,0,0,0); + font-size: 1rem; + line-height: 1.5; + + transition: color .15s ease-in-out, background-color .15s ease-in-out; +} + +button.copied { + background-color: var(--success); + color: var(--bg); +} + +li { + display: flex; + flex-direction: row; + align-items: center; +} + +#shutdown-form { + margin-top: 4em; +} + +#shutdown-form > input[type=submit] { + background-color: red; +} diff --git a/src/panel/panel.js b/src/panel/panel.js new file mode 100644 index 0000000..30cc981 --- /dev/null +++ b/src/panel/panel.js @@ -0,0 +1,47 @@ +const main = async () => { + const themesContainer = document.querySelector("#themes"); + const themes = await fetch("/themes.json").then(r => r.json()); + + for (const slug in themes) { + const theme = themes[slug]; + + // TODO + const listItem = document.createElement("li"); + + const themeName = document.createElement("strong"); + themeName.textContent = theme.name; + + const themeDescription = document.createElement("em"); + themeDescription.textContent = theme.description; + + const copyButton = document.createElement("button"); + copyButton.textContent = "Copy JS"; + copyButton.addEventListener("click", e => { + e.preventDefault(); + + (async () => { + const injectorJS = await fetch(`/theme/${slug}/injector.js`).then(r => r.text()); + await navigator.clipboard.writeText(injectorJS) + + copyButton.textContent = "Copied!"; + copyButton.classList.add("copied"); + setTimeout(() => { + copyButton.textContent = "Copy JS"; + copyButton.classList.remove("copied"); + }, 2500); + })(); + }); + + listItem.appendChild(themeName); + listItem.appendChild(themeDescription); + listItem.appendChild(copyButton); + + themesContainer.appendChild(listItem); + } +}; + +if (document.readyState === "complete") { + main(); +} else { + document.addEventListener("DOMContentLoaded", main); +} diff --git a/src/themes.rs b/src/themes.rs new file mode 100644 index 0000000..0690dfd --- /dev/null +++ b/src/themes.rs @@ -0,0 +1,48 @@ +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct Theme { + pub name: String, + pub description: String, + + #[serde(skip)] + pub slug: String, + #[serde(skip)] + pub files: Vec, +} + +pub fn read_theme_from_dir(dir: PathBuf) -> Option { + if let Some((slug, mut theme)) = dir + .file_name() + .map(|dir_name| dir_name.to_string_lossy().to_string()) + .and_then(|slug| { + std::fs::read_to_string(dir.join("theme.json")) + .map(|theme_json| (slug, theme_json)) + .ok() + }) + .and_then(|(slug, theme_json)| { + serde_json::from_str::(&theme_json) + .ok() + .map(|theme| (slug, theme)) + }) + { + theme.slug = slug.clone(); + + let files: Vec<_> = std::fs::read_dir(dir) + .unwrap() + .into_iter() + .filter(|r| r.is_ok()) + .map(|r| r.unwrap()) + .map(|e| e.file_name().to_string_lossy().to_string()) + .filter(|n| n.ends_with(".css")) + .collect(); + + theme.files = files; + + return Some(theme); + } + + None +} diff --git a/styles/.gitignore b/styles/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/styles/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore