aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock108
-rw-r--r--Cargo.toml3
-rw-r--r--README.md7
-rw-r--r--build.rs19
-rw-r--r--data/style.scss734
-rw-r--r--src/args.rs26
-rw-r--r--src/auth.rs5
-rw-r--r--src/file_upload.rs39
-rw-r--r--src/listing.rs21
-rw-r--r--src/main.rs43
-rw-r--r--src/renderer.rs650
-rw-r--r--src/themes.rs261
12 files changed, 1042 insertions, 874 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6eb96b0..1286438 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -494,6 +494,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
+name = "ahash"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
+
+[[package]]
name = "aho-corasick"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -663,6 +669,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
[[package]]
+name = "beef"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43"
+
+[[package]]
name = "bit-set"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -852,6 +864,12 @@ dependencies = [
]
[[package]]
+name = "codemap"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24"
+
+[[package]]
name = "const_fn"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1229,6 +1247,26 @@ dependencies = [
]
[[package]]
+name = "grass"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de2cf11276bb4d38b76d9666f9d87423522d57a519aefb9df744c1f6cf574e4f"
+dependencies = [
+ "beef",
+ "clap",
+ "codemap",
+ "indexmap",
+ "lasso",
+ "num-bigint",
+ "num-rational",
+ "num-traits",
+ "once_cell",
+ "peekmore",
+ "phf",
+ "rand 0.7.3",
+]
+
+[[package]]
name = "h2"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1249,6 +1287,16 @@ dependencies = [
[[package]]
name = "hashbrown"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
+dependencies = [
+ "ahash",
+ "autocfg 1.0.1",
+]
+
+[[package]]
+name = "hashbrown"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
@@ -1411,7 +1459,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
dependencies = [
"autocfg 1.0.1",
- "hashbrown",
+ "hashbrown 0.9.0",
]
[[package]]
@@ -1482,6 +1530,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
[[package]]
+name = "lasso"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf1a626ea51398f5acf36666c8046ff4bfd048aab88e92db676d2a6eac8805d0"
+dependencies = [
+ "hashbrown 0.8.2",
+]
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1658,6 +1715,7 @@ dependencies = [
"chrono-humanize",
"failure",
"futures",
+ "grass",
"hex",
"libflate",
"log",
@@ -1788,6 +1846,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
+name = "num-bigint"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7f3fc75e3697059fb1bc465e3d8cca6cf92f56854f201158b3f9c77d5a3cfa0"
+dependencies = [
+ "autocfg 1.0.1",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
name = "num-integer"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1798,6 +1867,18 @@ dependencies = [
]
[[package]]
+name = "num-rational"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138"
+dependencies = [
+ "autocfg 1.0.1",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
name = "num-traits"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1870,6 +1951,15 @@ dependencies = [
]
[[package]]
+name = "peekmore"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e029e89e27328ecf6a2e0820b5767ce6deb10872f2f18fb9e98d2e8ab7aef29f"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1881,7 +1971,9 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
+ "phf_macros",
"phf_shared",
+ "proc-macro-hack",
]
[[package]]
@@ -1905,6 +1997,20 @@ dependencies = [
]
[[package]]
+name = "phf_macros"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 1beb435..8bd5b92 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -55,3 +55,6 @@ rstest = "0.6"
regex = "1.3.9"
pretty_assertions = "0.6"
url = "2.1"
+
+[build-dependencies]
+grass = "0.10.3"
diff --git a/README.md b/README.md
index 1be86ec..7bd7463 100644
--- a/README.md
+++ b/README.md
@@ -106,8 +106,11 @@ Sometimes this is just a more practical and quick way than doing things properly
username:sha512:hash (e.g. joe:123,
joe:sha256:a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3)
-c, --color-scheme <color-scheme>
- Default color scheme [default: Squirrel] [possible values: Archlinux, Zenburn,
- Monokai, Squirrel]
+ Default color scheme [default: squirrel] [possible values: squirrel, archlinux, zenburn, monokai]
+
+ -d, --color-scheme-dark <color-scheme-dark>
+ Default color scheme [default: archlinux] [possible values: squirrel, archlinux, zenburn, monokai]
+
--index <index_file>
The name of a directory index file to serve, like "index.html"
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..8c354d0
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,19 @@
+use std::env;
+use std::fs;
+use std::path::Path;
+
+fn main() {
+ let out_dir = env::var_os("OUT_DIR").expect("OUT_DIR env var missing");
+ let dest_path = Path::new(&out_dir).join("style.css");
+ fs::write(
+ &dest_path,
+ grass::from_string(
+ include_str!("data/style.scss").to_string(),
+ &grass::Options::default(),
+ )
+ .expect("scss failed to compile"),
+ )
+ .expect("failed to write css file");
+
+ println!("cargo:rerun-if-changed=data/style.scss");
+}
diff --git a/data/style.scss b/data/style.scss
new file mode 100644
index 0000000..95bc56f
--- /dev/null
+++ b/data/style.scss
@@ -0,0 +1,734 @@
+@use "sass:selector";
+
+// theme colors can be found at the bottom
+$themes: squirrel, archlinux, monokai, zenburn;
+
+// This returns a selector that matches body when no theme class is set,
+// in which case the default light/dark mode themes should be used.
+// The result of this function can be used with #{...} interpolation
+// in a selector list.
+@function body_not_themed() {
+ $s: unquote("body");
+ @each $t in $themes {
+ $s: selector.append($s, unquote(":not(.theme_#{$t})"));
+ }
+ @return $s;
+}
+
+
+
+html {
+ font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+ width: 100%;
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 300;
+ color: var(--text_color);
+ background: var(--background);
+ position: relative;
+ min-height: 100%;
+}
+
+.container {
+ padding: 1.5rem 5rem;
+}
+
+.title {
+ word-break: break-word;
+}
+
+a {
+ text-decoration: none;
+}
+
+a.root,
+a.root:visited,
+.root-chevron {
+ font-weight: bold;
+ color: var(--root_link_color);
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+a.directory,
+a.directory:visited {
+ font-weight: bold;
+ color: var(--directory_link_color);
+}
+
+a.file,
+a.file:visited,
+.error-back,
+.error-back:visited {
+ color: var(--file_link_color);
+}
+
+a.symlink,
+a.symlink:visited {
+ color: var(--symlink_link_color);
+}
+
+a.directory:hover {
+ color: var(--directory_link_color);
+}
+
+a.file:hover {
+ color: var(--file_link_color);
+}
+
+a.symlink:hover {
+ color: var(--symlink_link_color);
+}
+
+.symlink-symbol {
+ display: inline-block;
+ border: 1px solid var(--symlink_link_color);
+ margin-left: 0.5rem;
+ border-radius: 0.2rem;
+ padding: 0 0.1rem;
+}
+
+nav {
+ padding: 0 5rem;
+ display: flex;
+ justify-content: flex-end;
+}
+
+nav > div {
+ position: relative;
+ margin-left: 0.5rem;
+}
+
+nav p {
+ padding: 0.5rem 1rem;
+ width: 8rem;
+ text-align: center;
+ background: var(--switch_theme_background);
+ color: var(--change_theme_link_color);
+}
+
+nav p + * {
+ display: none;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 100%;
+ animation: show 0.5s ease;
+}
+
+@keyframes show {
+ from {
+ opacity: 0;
+ }
+
+ to {
+ opacity: 1;
+ }
+}
+
+nav > div:hover p {
+ cursor: pointer;
+ color: var(--switch_theme_link_color);
+}
+
+nav > div:hover p + * {
+ display: block;
+ border-top: 1px solid var(--switch_theme_border);
+}
+
+nav .qrcode {
+ padding: 0.5rem;
+ background: var(--switch_theme_background);
+}
+
+nav .qrcode img {
+ display: block;
+}
+
+nav .theme {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+}
+
+nav .theme li {
+ width: 100%;
+ background: var(--switch_theme_background);
+}
+
+nav .theme li a {
+ display: block;
+ width: 100%;
+ padding: 0.5rem 0;
+ text-align: center;
+ color: var(--switch_theme_link_color);
+}
+
+nav .theme li a:visited {
+ color: var(--switch_theme_link_color);
+}
+
+nav .theme li a:hover {
+ text-decoration: underline;
+ color: var(--change_theme_link_color_hover);
+}
+
+%active_theme_link {
+ font-weight: bold;
+ color: var(--switch_theme_active);
+}
+
+@each $theme in $themes {
+ body.theme_#{$theme} nav .theme li.theme_#{$theme} a {
+ @extend %active_theme_link;
+ }
+}
+
+#{body_not_themed()} nav .theme li.theme_default a {
+ @extend %active_theme_link;
+}
+
+p {
+ margin: 0;
+ padding: 0;
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 1.5rem;
+}
+
+table {
+ margin-top: 2rem;
+ width: 100%;
+ border: 0;
+ table-layout: auto;
+ background: var(--table_background);
+}
+
+table thead tr th,
+table tbody tr td {
+ padding: 0.5625rem 0.625rem;
+ font-size: 0.875rem;
+ color: var(--table_text_color);
+ text-align: left;
+ line-height: 1.125rem;
+}
+
+table thead tr th {
+ padding: 0.5rem 0.625rem 0.625rem;
+ font-weight: bold;
+}
+
+table thead th.size {
+ width: 6em;
+}
+
+table thead th.date {
+ width: 15em;
+}
+
+table tbody tr:nth-child(odd) {
+ background: var(--odd_row_background);
+}
+
+table tbody tr:nth-child(even) {
+ background: var(--even_row_background);
+}
+
+table thead {
+ background: var(--table_header_background);
+}
+
+table tbody tr:hover {
+ background: var(--active_row_color);
+}
+
+td.size-cell {
+ text-align: right;
+}
+
+td.date-cell {
+ display: flex;
+ justify-content: space-between;
+}
+
+.at {
+ color: var(--at_color);
+}
+
+.history {
+ color: var(--date_text_color);
+}
+
+.file-entry {
+ display: flex;
+ justify-content: space-between;
+}
+
+span.size {
+ border-radius: 1rem;
+ background: var(--size_background_color);
+ padding: 0 0.25rem;
+ font-size: 0.7rem;
+ color: var(--size_text_color);
+}
+
+.mobile-info {
+ display: none;
+}
+
+th a,
+th a:visited,
+.chevron {
+ color: var(--table_header_text_color);
+}
+
+.chevron,
+.root-chevron {
+ margin-right: 0.5rem;
+ font-size: 1.2em;
+ font-weight: bold;
+}
+
+th span.active a,
+th span.active span {
+ color: var(--table_header_active_color);
+}
+
+.back {
+ position: fixed;
+ width: 3.8rem;
+ height: 3.8rem;
+ align-items: center;
+ justify-content: center;
+ bottom: 3rem;
+ right: 3.75rem;
+ background: var(--back_button_background);
+ border-radius: 100%;
+ box-shadow: 0 0 8px -4px #888888;
+ color: var(--back_button_link_color);
+ display: none;
+}
+
+.back:visited {
+ color: var(--back_button_link_color);
+}
+
+.back:hover {
+ color: var(--back_button_link_color_hover);
+ font-weight: bold;
+ text-decoration: none;
+ background: var(--back_button_background_hover);
+}
+
+.toolbar {
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+}
+
+.download {
+ margin-top: 1rem;
+ padding: 0.125rem;
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ flex-wrap: wrap;
+}
+
+.download a,
+.download a:visited {
+ color: var(--download_button_link_color);
+}
+
+.download a {
+ background: var(--download_button_background);
+ padding: 0.5rem;
+ border-radius: 0.2rem;
+}
+
+.download a:hover {
+ background: var(--download_button_background_hover);
+ color: var(--download_button_link_color_hover);
+}
+
+.download a:not(:last-of-type) {
+ margin-right: 1rem;
+}
+
+.upload {
+ margin-top: 1rem;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.upload p {
+ font-size: 0.8rem;
+ margin-bottom: 1rem;
+ color: var(--upload_text_color);
+}
+
+.upload form {
+ padding: 1rem;
+ border: 1px solid var(--upload_form_border_color);
+ background: var(--upload_form_background);
+}
+
+.upload button {
+ background: var(--upload_button_background);
+ padding: 0.5rem;
+ border-radius: 0.2rem;
+ color: var(--upload_button_text_color);
+ border: none;
+}
+
+.upload div {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+}
+
+.drag-form {
+ display: none;
+ background: var(--drag_background);
+ position: absolute;
+ border: 0.5rem dashed var(--drag_border_color);
+ width: calc(100% - 1rem);
+ height: calc(100% - 1rem);
+ text-align: center;
+ z-index: 2;
+}
+
+.drag-title {
+ position: fixed;
+ color: var(--drag_text_color);
+ top: 50%;
+ width: 100%;
+ text-align: center;
+}
+
+.error {
+ margin: 2rem;
+}
+
+.error p {
+ margin: 1rem 0;
+ font-size: 0.9rem;
+ word-break: break-all;
+}
+
+.error p:first-of-type {
+ font-size: 1.25rem;
+ color: var(--error_color);
+ margin-bottom: 2rem;
+}
+
+.error p:nth-of-type(2) {
+ font-weight: bold;
+}
+
+.error-nav {
+ margin-top: 4rem;
+}
+
+@media (max-width: 760px) {
+ nav {
+ padding: 0 2.5rem;
+ }
+
+ .container {
+ padding: 1.5rem 2.5rem;
+ }
+
+ h1 {
+ font-size: 1.4em;
+ }
+
+ td:not(:nth-child(1)),
+ th:not(:nth-child(1)) {
+ display: none;
+ }
+
+ .mobile-info {
+ display: block;
+ }
+
+ table tbody tr td {
+ padding-top: 0;
+ padding-bottom: 0;
+ }
+
+ a.directory {
+ display: block;
+ padding: 0.5625rem 0;
+ }
+
+ .file-entry {
+ align-items: center;
+ }
+
+ a.root,
+ a.file,
+ a.symlink {
+ display: inline-block;
+ flex: 1;
+ padding: 0.5625rem 0;
+ }
+
+ a.symlink {
+ width: 100%;
+ }
+
+ .back {
+ display: flex;
+ }
+
+ .back {
+ right: 1.5rem;
+ }
+}
+
+@media (max-width: 600px) {
+ h1 {
+ font-size: 1.375em;
+ }
+}
+
+@media (max-width: 400px) {
+ nav {
+ padding: 0 0.5rem;
+ }
+
+ .container {
+ padding: 0.5rem;
+ }
+
+ h1 {
+ font-size: 1.375em;
+ }
+
+ .back {
+ right: 1.5rem;
+ }
+}
+
+
+
+@mixin theme_squirrel {
+ --background: #ffffff;
+ --text_color: #323232;
+ --directory_link_color: #d02474;
+ --file_link_color: #0086b3;
+ --symlink_link_color: #ed6a43;
+ --table_background: #ffffff;
+ --table_text_color: #323232;
+ --table_header_background: #323232;
+ --table_header_text_color: #f5f5f5;
+ --table_header_active_color: #ffffff;
+ --active_row_color: #f6f8fa;
+ --odd_row_background: #fbfbfb;
+ --even_row_background: #f2f2f2;
+ --root_link_color: #323232;
+ --download_button_background: #d02474;
+ --download_button_background_hover: #f52d8a;
+ --download_button_link_color: #ffffff;
+ --download_button_link_color_hover: #ffffff;
+ --back_button_background: #d02474;
+ --back_button_background_hover: #d02474;
+ --back_button_link_color: #ffffff;
+ --back_button_link_color_hover: #ffffff;
+ --date_text_color: #797979;
+ --at_color: #797979;
+ --switch_theme_background: #323232;
+ --switch_theme_link_color: #f5f5f5;
+ --switch_theme_active: #d02474;
+ --switch_theme_border: #49483e;
+ --change_theme_link_color: #f5f5f5;
+ --change_theme_link_color_hover: #f5f5f5;
+ --upload_text_color: #323232;
+ --upload_form_border_color: #d2d2d2;
+ --upload_form_background: #f2f2f2;
+ --upload_button_background: #d02474;
+ --upload_button_text_color: #ffffff;
+ --drag_background: #3333338f;
+ --drag_border_color: #ffffff;
+ --drag_text_color: #ffffff;
+ --size_background_color: #323232;
+ --size_text_color: #ffffff;
+ --error_color: #d02424;
+}
+
+@mixin theme_archlinux {
+ --background: #383c4a;
+ --text_color: #fefefe;
+ --directory_link_color: #03a9f4;
+ --file_link_color: #ea95ff;
+ --symlink_link_color: #ff9800;
+ --table_background: #353946;
+ --table_text_color: #eeeeee;
+ --table_header_background: #5294e2;
+ --table_header_text_color: #eeeeee;
+ --table_header_active_color: #ffffff;
+ --active_row_color: #5194e259;
+ --odd_row_background: #404552;
+ --even_row_background: #4b5162;
+ --root_link_color: #abb2bb;
+ --download_button_background: #ea95ff;
+ --download_button_background_hover: #eea7ff;
+ --download_button_link_color: #ffffff;
+ --download_button_link_color_hover: #ffffff;
+ --back_button_background: #ea95ff;
+ --back_button_background_hover: #ea95ff;
+ --back_button_link_color: #ffffff;
+ --back_button_link_color_hover: #ffffff;
+ --date_text_color: #9ebbdc;
+ --at_color: #9ebbdc;
+ --switch_theme_background: #4b5162;
+ --switch_theme_link_color: #fefefe;
+ --switch_theme_active: #ea95ff;
+ --switch_theme_border: #6a728a;
+ --change_theme_link_color: #fefefe;
+ --change_theme_link_color_hover: #fefefe;
+ --upload_text_color: #fefefe;
+ --upload_form_border_color: #353946;
+ --upload_form_background: #4b5162;
+ --upload_button_background: #ea95ff;
+ --upload_button_text_color: #ffffff;
+ --drag_background: #3333338f;
+ --drag_border_color: #fefefe;
+ --drag_text_color: #fefefe;
+ --size_background_color: #5294e2;
+ --size_text_color: #fefefe;
+ --error_color: #e44b4b;
+}
+
+@mixin theme_zenburn {
+ --background: #3f3f3f;
+ --text_color: #efefef;
+ --directory_link_color: #f0dfaf;
+ --file_link_color: #87d6d5;
+ --symlink_link_color: #ffccee;
+ --table_background: #4a4949;
+ --table_text_color: #efefef;
+ --table_header_background: #7f9f7f;
+ --table_header_text_color: #efefef;
+ --table_header_active_color: #efef8f;
+ --active_row_color: #7e9f7f9c;
+ --odd_row_background: #777777;
+ --even_row_background: #5a5a5a;
+ --root_link_color: #dca3a3;
+ --download_button_background: #cc9393;
+ --download_button_background_hover: #dca3a3;
+ --download_button_link_color: #efefef;
+ --download_button_link_color_hover: #efefef;
+ --back_button_background: #cc9393;
+ --back_button_background_hover: #cc9393;
+ --back_button_link_color: #efefef;
+ --back_button_link_color_hover: #efefef;
+ --date_text_color: #cfbfaf;
+ --at_color: #cfbfaf;
+ --switch_theme_background: #4a4949;
+ --switch_theme_link_color: #efefef;
+ --switch_theme_active: #efef8f;
+ --switch_theme_border: #5a5a5a;
+ --change_theme_link_color: #efefef;
+ --change_theme_link_color_hover: #efefef;
+ --upload_text_color: #efefef;
+ --upload_form_border_color: #4a4949;
+ --upload_form_background: #777777;
+ --upload_button_background: #cc9393;
+ --upload_button_text_color: #efefef;
+ --drag_background: #3333338f;
+ --drag_border_color: #efefef;
+ --drag_text_color: #efefef;
+ --size_background_color: #7f9f7f;
+ --size_text_color: #efefef;
+ --error_color: #d06565;
+}
+
+@mixin theme_monokai {
+ --background: #272822;
+ --text_color: #f8f8f2;
+ --directory_link_color: #f92672;
+ --file_link_color: #a6e22e;
+ --symlink_link_color: #fd971f;
+ --table_background: #3b3a32;
+ --table_text_color: #f8f8f0;
+ --table_header_background: #75715e;
+ --table_header_text_color: #f8f8f2;
+ --table_header_active_color: #e6db74;
+ --active_row_color: #ae81fe3d;
+ --odd_row_background: #3e3d32;
+ --even_row_background: #49483e;
+ --root_link_color: #66d9ef;
+ --download_button_background: #ae81ff;
+ --download_button_background_hover: #c6a6ff;
+ --download_button_link_color: #f8f8f0;
+ --download_button_link_color_hover: #f8f8f0;
+ --back_button_background: #ae81ff;
+ --back_button_background_hover: #ae81ff;
+ --back_button_link_color: #f8f8f0;
+ --back_button_link_color_hover: #f8f8f0;
+ --date_text_color: #66d9ef;
+ --at_color: #66d9ef;
+ --switch_theme_background: #3b3a32;
+ --switch_theme_link_color: #f8f8f2;
+ --switch_theme_active: #a6e22e;
+ --switch_theme_border: #49483e;
+ --change_theme_link_color: #f8f8f2;
+ --change_theme_link_color_hover: #f8f8f2;
+ --upload_text_color: #f8f8f2;
+ --upload_form_border_color: #3b3a32;
+ --upload_form_background: #49483e;
+ --upload_button_background: #ae81ff;
+ --upload_button_text_color: #f8f8f0;
+ --drag_background: #3333338f;
+ --drag_border_color: #f8f8f2;
+ --drag_text_color: #f8f8f2;
+ --size_background_color: #75715e;
+ --size_text_color: #f8f8f2;
+ --error_color: #d02929;
+}
+
+
+
+// For each of the themes, define a placeholder selector containing
+// the themes variables. Then add selectors for body when the theme:
+// - has explicitly been activated by .theme_*
+// - is set as default theme by .default_theme_* and no other theme is active
+// to the placeholder selector list by means of @extend.
+@each $theme in $themes {
+ %theme_#{$theme} { @include theme_#{$theme}; }
+
+ body.theme_#{$theme} { @extend %theme_#{$theme}; }
+
+ #{body_not_themed()}.default_theme_#{$theme} {
+ @extend %theme_#{$theme};
+ }
+}
+
+// Do the same thing again for the dark mode default.
+// Since the media query doesn't affect specificity, all dark mode
+// defaults need to come after all light mode defaults to override
+// them when dark mode is enabled.
+@each $theme in $themes {
+ @media (prefers-color-scheme: dark) {
+ %theme_dark_#{$theme} { @include theme_#{$theme}; }
+ }
+
+ // this extension will still end up inside the media query,
+ // because that is where %theme_dark_* was defined
+ #{body_not_themed()}.default_theme_dark_#{$theme} {
+ @extend %theme_dark_#{$theme};
+ }
+}
+
+
diff --git a/src/args.rs b/src/args.rs
index 25a3503..dc160d0 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -5,7 +5,7 @@ use structopt::StructOpt;
use crate::auth;
use crate::errors::ContextualError;
-use crate::themes;
+use crate::renderer;
/// Possible characters for random routes
const ROUTE_ALPHABET: [char; 16] = [
@@ -71,11 +71,21 @@ struct CLIArgs {
#[structopt(
short = "c",
long = "color-scheme",
- default_value = "Squirrel",
- possible_values = &themes::ColorScheme::variants(),
+ default_value = "squirrel",
+ possible_values = &renderer::THEME_SLUGS,
case_insensitive = true,
)]
- color_scheme: themes::ColorScheme,
+ color_scheme: String,
+
+ /// Default color scheme
+ #[structopt(
+ short = "d",
+ long = "color-scheme-dark",
+ default_value = "archlinux",
+ possible_values = &renderer::THEME_SLUGS,
+ case_insensitive = true,
+ )]
+ color_scheme_dark: String,
/// Enable QR code display
#[structopt(short = "q", long = "qrcode")]
@@ -175,11 +185,13 @@ pub fn parse_args() -> crate::MiniserveConfig {
None
};
- // Generate some random route for the favicon so that it is very unlikely to conflict with a
- // real file.
+ // Generate some random routes for the favicon and css so that they are very unlikely to conflict with
+ // real files.
let favicon_route = nanoid::nanoid!(10, &ROUTE_ALPHABET);
+ let css_route = nanoid::nanoid!(10, &ROUTE_ALPHABET);
let default_color_scheme = args.color_scheme;
+ let default_color_scheme_dark = args.color_scheme_dark;
let path_explicitly_chosen = args.path.is_some();
@@ -198,7 +210,9 @@ pub fn parse_args() -> crate::MiniserveConfig {
no_symlinks: args.no_symlinks,
random_route,
favicon_route,
+ css_route,
default_color_scheme,
+ default_color_scheme_dark,
index: args.index,
overwrite_files: args.overwrite_files,
show_qrcode: args.qrcode,
diff --git a/src/auth.rs b/src/auth.rs
index e3eec89..a2dfe60 100644
--- a/src/auth.rs
+++ b/src/auth.rs
@@ -120,11 +120,12 @@ fn build_unauthorized_response(
&return_path,
None,
None,
- state.default_color_scheme,
- state.default_color_scheme,
false,
false,
&state.favicon_route,
+ &state.css_route,
+ &state.default_color_scheme,
+ &state.default_color_scheme_dark,
)
.into_string()
}
diff --git a/src/file_upload.rs b/src/file_upload.rs
index 394afbc..14aadfc 100644
--- a/src/file_upload.rs
+++ b/src/file_upload.rs
@@ -12,7 +12,6 @@ use std::{
use crate::errors::{self, ContextualError};
use crate::listing::{self, SortingMethod, SortingOrder};
use crate::renderer;
-use crate::themes::ColorScheme;
/// Create future to save file.
fn save_file(
@@ -109,9 +108,11 @@ fn handle_multipart(
pub fn upload_file(
req: HttpRequest,
payload: actix_web::web::Payload,
- default_color_scheme: ColorScheme,
uses_random_route: bool,
favicon_route: String,
+ css_route: String,
+ default_color_scheme: &str,
+ default_color_scheme_dark: &str,
) -> Pin<Box<dyn Future<Output = Result<HttpResponse, actix_web::Error>>>> {
let conf = req.app_data::<crate::MiniserveConfig>().unwrap();
let return_path = if let Some(header) = req.headers().get(header::REFERER) {
@@ -121,7 +122,6 @@ pub fn upload_file(
};
let query_params = listing::extract_query_parameters(&req);
- let color_scheme = query_params.theme.unwrap_or(default_color_scheme);
let upload_path = match query_params.path.clone() {
Some(path) => match path.strip_prefix(Component::RootDir) {
Ok(stripped_path) => stripped_path.to_owned(),
@@ -137,10 +137,11 @@ pub fn upload_file(
&return_path,
query_params.sort,
query_params.order,
- color_scheme,
- default_color_scheme,
uses_random_route,
&favicon_route,
+ &css_route,
+ default_color_scheme,
+ default_color_scheme_dark,
));
}
};
@@ -158,10 +159,11 @@ pub fn upload_file(
&return_path,
query_params.sort,
query_params.order,
- color_scheme,
- default_color_scheme,
uses_random_route,
&favicon_route,
+ &css_route,
+ default_color_scheme,
+ default_color_scheme_dark,
));
}
};
@@ -179,14 +181,18 @@ pub fn upload_file(
&return_path,
query_params.sort,
query_params.order,
- color_scheme,
- default_color_scheme,
uses_random_route,
&favicon_route,
+ &css_route,
+ default_color_scheme,
+ default_color_scheme_dark,
));
}
};
let overwrite_files = conf.overwrite_files;
+ let default_color_scheme = conf.default_color_scheme.clone();
+ let default_color_scheme_dark = conf.default_color_scheme_dark.clone();
+
Box::pin(
actix_multipart::Multipart::new(req.headers(), payload)
.map_err(ContextualError::MultipartError)
@@ -205,10 +211,11 @@ pub fn upload_file(
&return_path,
query_params.sort,
query_params.order,
- color_scheme,
- default_color_scheme,
uses_random_route,
&favicon_route,
+ &css_route,
+ &default_color_scheme,
+ &default_color_scheme_dark,
),
}),
)
@@ -222,10 +229,11 @@ fn create_error_response(
return_path: &str,
sorting_method: Option<SortingMethod>,
sorting_order: Option<SortingOrder>,
- color_scheme: ColorScheme,
- default_color_scheme: ColorScheme,
uses_random_route: bool,
favicon_route: &str,
+ css_route: &str,
+ default_color_scheme: &str,
+ default_color_scheme_dark: &str,
) -> future::Ready<Result<HttpResponse, actix_web::Error>> {
errors::log_error_chain(description.to_string());
future::ok(
@@ -238,11 +246,12 @@ fn create_error_response(
return_path,
sorting_method,
sorting_order,
- color_scheme,
- default_color_scheme,
true,
!uses_random_route,
&favicon_route,
+ &css_route,
+ default_color_scheme,
+ default_color_scheme_dark,
)
.into_string(),
),
diff --git a/src/listing.rs b/src/listing.rs
index d14a188..755b026 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -15,7 +15,6 @@ use strum_macros::{Display, EnumString};
use crate::archive::CompressionMethod;
use crate::errors::{self, ContextualError};
use crate::renderer;
-use crate::themes::ColorScheme;
const FRAGMENT: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
@@ -25,7 +24,6 @@ pub struct QueryParameters {
pub path: Option<PathBuf>,
pub sort: Option<SortingMethod>,
pub order: Option<SortingOrder>,
- pub theme: Option<ColorScheme>,
qrcode: Option<String>,
download: Option<CompressionMethod>,
}
@@ -153,7 +151,9 @@ pub fn directory_listing(
file_upload: bool,
random_route: Option<String>,
favicon_route: String,
- default_color_scheme: ColorScheme,
+ css_route: String,
+ default_color_scheme: &str,
+ default_color_scheme_dark: &str,
show_qrcode: bool,
upload_route: String,
tar_enabled: bool,
@@ -323,8 +323,6 @@ pub fn directory_listing(
}
}
- let color_scheme = query_params.theme.unwrap_or(default_color_scheme);
-
if let Some(compression_method) = query_params.download {
if !compression_method.is_enabled(tar_enabled, zip_enabled) {
return Ok(ServiceResponse::new(
@@ -338,11 +336,12 @@ pub fn directory_listing(
"/",
None,
None,
- color_scheme,
- default_color_scheme,
false,
false,
&favicon_route,
+ &css_route,
+ default_color_scheme,
+ default_color_scheme_dark,
)
.into_string(),
),
@@ -393,17 +392,17 @@ pub fn directory_listing(
.content_type("text/html; charset=utf-8")
.body(
renderer::page(
- serve_path,
entries,
is_root,
query_params.sort,
query_params.order,
- default_color_scheme,
- color_scheme,
show_qrcode,
file_upload,
&upload_route,
&favicon_route,
+ &css_route,
+ default_color_scheme,
+ default_color_scheme_dark,
&encoded_dir,
breadcrumbs,
tar_enabled,
@@ -421,7 +420,6 @@ pub fn extract_query_parameters(req: &HttpRequest) -> QueryParameters {
sort: query.sort,
order: query.order,
download: query.download,
- theme: query.theme,
qrcode: query.qrcode.to_owned(),
path: query.path.clone(),
},
@@ -432,7 +430,6 @@ pub fn extract_query_parameters(req: &HttpRequest) -> QueryParameters {
sort: None,
order: None,
download: None,
- theme: None,
qrcode: None,
path: None,
}
diff --git a/src/main.rs b/src/main.rs
index 45187ca..c70bd3b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,7 +22,6 @@ mod file_upload;
mod listing;
mod pipe;
mod renderer;
-mod themes;
use crate::errors::ContextualError;
@@ -56,8 +55,14 @@ pub struct MiniserveConfig {
/// Randomly generated favicon route
pub favicon_route: String,
+ /// Randomly generated css route
+ pub css_route: String,
+
/// Default color scheme
- pub default_color_scheme: themes::ColorScheme,
+ pub default_color_scheme: String,
+
+ /// Default dark mode color scheme
+ pub default_color_scheme_dark: String,
/// The name of a directory index file to serve, like "index.html"
///
@@ -252,6 +257,7 @@ async fn run() -> Result<(), ContextualError> {
&format!("/{}", inside_config.favicon_route),
web::get().to(favicon),
)
+ .route(&format!("/{}", inside_config.css_route), web::get().to(css))
.configure(|c| configure_app(c, &inside_config))
.default_service(web::get().to(error_404))
})
@@ -284,7 +290,9 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) {
let no_symlinks = conf.no_symlinks;
let random_route = conf.random_route.clone();
let favicon_route = conf.favicon_route.clone();
- let default_color_scheme = conf.default_color_scheme;
+ let css_route = conf.css_route.clone();
+ let default_color_scheme = conf.default_color_scheme.clone();
+ let default_color_scheme_dark = conf.default_color_scheme_dark.clone();
let show_qrcode = conf.show_qrcode;
let file_upload = conf.file_upload;
let tar_enabled = conf.tar_enabled;
@@ -314,7 +322,9 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) {
file_upload,
random_route.clone(),
favicon_route.clone(),
- default_color_scheme,
+ css_route.clone(),
+ &default_color_scheme,
+ &default_color_scheme_dark,
show_qrcode,
u_r.clone(),
tar_enabled,
@@ -328,18 +338,24 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) {
};
let favicon_route = conf.favicon_route.clone();
+ let css_route = conf.css_route.clone();
+
+ let default_color_scheme = conf.default_color_scheme.clone();
+ let default_color_scheme_dark = conf.default_color_scheme_dark.clone();
+
if let Some(serve_path) = serve_path {
if conf.file_upload {
- let default_color_scheme = conf.default_color_scheme;
// Allow file upload
app.service(
web::resource(&upload_route).route(web::post().to(move |req, payload| {
file_upload::upload_file(
req,
payload,
- default_color_scheme,
uses_random_route,
favicon_route.clone(),
+ css_route.clone(),
+ &default_color_scheme,
+ &default_color_scheme_dark,
)
})),
)
@@ -358,11 +374,10 @@ fn configure_app(app: &mut web::ServiceConfig, conf: &MiniserveConfig) {
async fn error_404(req: HttpRequest) -> HttpResponse {
let err_404 = ContextualError::RouteNotFoundError(req.path().to_string());
let conf = req.app_data::<MiniserveConfig>().unwrap();
- let default_color_scheme = conf.default_color_scheme;
let uses_random_route = conf.random_route.is_some();
let favicon_route = conf.favicon_route.clone();
+ let css_route = conf.css_route.clone();
let query_params = listing::extract_query_parameters(&req);
- let color_scheme = query_params.theme.unwrap_or(default_color_scheme);
errors::log_error_chain(err_404.to_string());
@@ -373,11 +388,12 @@ async fn error_404(req: HttpRequest) -> HttpResponse {
"/",
query_params.sort,
query_params.order,
- color_scheme,
- default_color_scheme,
false,
!uses_random_route,
&favicon_route,
+ &css_route,
+ &conf.default_color_scheme,
+ &conf.default_color_scheme_dark,
)
.into_string(),
)
@@ -389,3 +405,10 @@ async fn favicon() -> impl Responder {
.set(ContentType(mime::IMAGE_SVG))
.message_body(logo.into())
}
+
+async fn css() -> impl Responder {
+ let css = include_str!(concat!(env!("OUT_DIR"), "/style.css"));
+ web::HttpResponse::Ok()
+ .set(ContentType(mime::TEXT_CSS))
+ .message_body(css.into())
+}
diff --git a/src/renderer.rs b/src/renderer.rs
index 43cb222..5a727d0 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -7,35 +7,27 @@ use strum::IntoEnumIterator;
use crate::archive::CompressionMethod;
use crate::listing::{Breadcrumb, Entry, SortingMethod, SortingOrder};
-use crate::themes::ColorScheme;
/// Renders the file listing
#[allow(clippy::too_many_arguments)]
pub fn page(
- serve_path: &str,
entries: Vec<Entry>,
is_root: bool,
sort_method: Option<SortingMethod>,
sort_order: Option<SortingOrder>,
- default_color_scheme: ColorScheme,
- color_scheme: ColorScheme,
show_qrcode: bool,
file_upload: bool,
upload_route: &str,
favicon_route: &str,
+ css_route: &str,
+ default_color_scheme: &str,
+ default_color_scheme_dark: &str,
encoded_dir: &str,
breadcrumbs: Vec<Breadcrumb>,
tar_enabled: bool,
zip_enabled: bool,
) -> Markup {
- let upload_action = build_upload_action(
- upload_route,
- encoded_dir,
- sort_method,
- sort_order,
- color_scheme,
- default_color_scheme,
- );
+ let upload_action = build_upload_action(upload_route, encoded_dir, sort_method, sort_order);
let title_path = breadcrumbs
.iter()
@@ -46,8 +38,36 @@ pub fn page(
html! {
(DOCTYPE)
html {
- (page_header(&title_path, color_scheme, file_upload, favicon_route))
- body#drop-container {
+ (page_header(&title_path, file_upload, favicon_route, css_route))
+
+ body#drop-container
+ .(format!("default_theme_{}", default_color_scheme))
+ .(format!("default_theme_dark_{}", default_color_scheme_dark)) {
+
+ (PreEscaped(r#"
+ <script>
+ // read theme from local storage and apply it to body
+ const body = document.body;
+ var theme = localStorage.getItem('theme');
+
+ if (theme != null && theme != 'default') {
+ body.classList.add('theme_' + theme);
+ }
+
+ // updates the color scheme by replacing the appropriate class
+ // on body and saving the new theme to local storage
+ function updateColorScheme(name) {
+ body.classList.remove.apply(body.classList, Array.from(body.classList).filter(v=>v.startsWith("theme_")));
+
+ if (name != "default") {
+ body.classList.add('theme_' + name);
+ }
+
+ localStorage.setItem('theme', name);
+ }
+ </script>
+ "#))
+
@if file_upload {
div.drag-form {
div.drag-title {
@@ -55,7 +75,7 @@ pub fn page(
}
}
}
- (color_scheme_selector(sort_method, sort_order, color_scheme, default_color_scheme, serve_path, show_qrcode))
+ (color_scheme_selector(show_qrcode))
div.container {
span#top { }
h1.title {
@@ -64,7 +84,7 @@ pub fn page(
// wrapped in span so the text doesn't shift slightly when it turns into a link
span { (el.name) }
} @else {
- a.directory href=(parametrized_link(&el.link, sort_method, sort_order, color_scheme, default_color_scheme)) {
+ a.directory href=(parametrized_link(&el.link, sort_method, sort_order)) {
(el.name)
}
}
@@ -76,7 +96,7 @@ pub fn page(
div.download {
@for compression_method in CompressionMethod::iter() {
@if compression_method.is_enabled(tar_enabled, zip_enabled) {
- (archive_button(compression_method, sort_method, sort_order, color_scheme, default_color_scheme))
+ (archive_button(compression_method, sort_method, sort_order))
}
}
}
@@ -95,23 +115,23 @@ pub fn page(
}
table {
thead {
- th.name { (build_link("name", "Name", sort_method, sort_order, color_scheme, default_color_scheme)) }
- th.size { (build_link("size", "Size", sort_method, sort_order, color_scheme, default_color_scheme)) }
- th.date { (build_link("date", "Last modification", sort_method, sort_order, color_scheme, default_color_scheme)) }
+ th.name { (build_link("name", "Name", sort_method, sort_order)) }
+ th.size { (build_link("size", "Size", sort_method, sort_order)) }
+ th.date { (build_link("date", "Last modification", sort_method, sort_order)) }
}
tbody {
@if !is_root {
tr {
td colspan="3" {
span.root-chevron { (chevron_left()) }
- a.root href=(parametrized_link("../", sort_method, sort_order, color_scheme, default_color_scheme)) {
+ a.root href=(parametrized_link("../", sort_method, sort_order)) {
"Parent directory"
}
}
}
}
@for entry in entries {
- (entry_row(entry, sort_method, sort_order, color_scheme, default_color_scheme))
+ (entry_row(entry, sort_method, sort_order))
}
}
}
@@ -130,8 +150,6 @@ fn build_upload_action(
encoded_dir: &str,
sort_method: Option<SortingMethod>,
sort_order: Option<SortingOrder>,
- color_scheme: ColorScheme,
- default_color_scheme: ColorScheme,
) -> String {
let mut upload_action = format!("{}?path={}", upload_route, encoded_dir);
if let Some(sorting_method) = sort_method {
@@ -140,22 +158,22 @@ fn build_upload_action(
if let Some(sorting_order) = sort_order {
upload_action = format!("{}&order={}", upload_action, &sorting_order);
}
- if color_scheme != default_color_scheme {
- upload_action = format!("{}&theme={}", upload_action, color_scheme.to_slug());
- }
upload_action
}
+const THEME_PICKER_CHOICES: &[(&str, &str)] = &[
+ ("Default (light/dark)", "default"),
+ ("Squirrel (light)", "squirrel"),
+ ("Archlinux (dark)", "archlinux"),
+ ("Zenburn (dark)", "zenburn"),
+ ("Monokai (dark)", "monokai"),
+];
+
+pub const THEME_SLUGS: &[&str] = &["squirrel", "archlinux", "zenburn", "monokai"];
+
/// Partial: color scheme selector
-fn color_scheme_selector(
- sort_method: Option<SortingMethod>,
- sort_order: Option<SortingOrder>,
- active_color_scheme: ColorScheme,
- default_color_scheme: ColorScheme,
- serve_path: &str,
- show_qrcode: bool,
-) -> Markup {
+fn color_scheme_selector(show_qrcode: bool) -> Markup {
html! {
nav {
@if show_qrcode {
@@ -173,15 +191,9 @@ fn color_scheme_selector(
"Change theme..."
}
ul.theme {
- @for color_scheme in ColorScheme::iter() {
- @if active_color_scheme == color_scheme {
- li.active {
- (color_scheme_link(sort_method, sort_order, color_scheme, default_color_scheme, serve_path))
- }
- } @else {
- li {
- (color_scheme_link(sort_method, sort_order, color_scheme, default_color_scheme, serve_path))
- }
+ @for color_scheme in THEME_PICKER_CHOICES {
+ li.(format!("theme_{}", color_scheme.1)) {
+ (color_scheme_link(color_scheme))
}
}
}
@@ -190,32 +202,13 @@ fn color_scheme_selector(
}
}
-/// Partial: color scheme link
-fn color_scheme_link(
- sort_method: Option<SortingMethod>,
- sort_order: Option<SortingOrder>,
- color_scheme: ColorScheme,
- default_color_scheme: ColorScheme,
- serve_path: &str,
-) -> Markup {
- let link = parametrized_link(
- serve_path,
- sort_method,
- sort_order,
- color_scheme,
- default_color_scheme,
- );
- let title = format!("Switch to {} theme", color_scheme);
+// /// Partial: color scheme link
+fn color_scheme_link(color_scheme: &(&str, &str)) -> Markup {
+ let title = format!("Switch to {} theme", color_scheme.0);
html! {
- a href=(link) title=(title) {
- (color_scheme)
- " "
- @if color_scheme.is_dark() {
- "(dark)"
- } @else {
- "(light)"
- }
+ a href=(format!("javascript:updateColorScheme(\"{}\")", color_scheme.1)) title=(title) {
+ (color_scheme.0)
}
}
}
@@ -225,25 +218,16 @@ fn archive_button(
compress_method: CompressionMethod,
sort_method: Option<SortingMethod>,
sort_order: Option<SortingOrder>,
- color_scheme: ColorScheme,
- default_color_scheme: ColorScheme,
) -> Markup {
- let link =
- if sort_method.is_none() && sort_order.is_none() && color_scheme == default_color_scheme {
- format!("?download={}", compress_method)
- } else {
- format!(
- "{}&download={}",
- parametrized_link(
- "",
- sort_method,
- sort_order,
- color_scheme,
- default_color_scheme
- ),
- compress_method
- )
- };
+ let link = if sort_method.is_none() && sort_order.is_none() {
+ format!("?download={}", compress_method)
+ } else {
+ format!(
+ "{}&download={}",
+ parametrized_link("", sort_method, sort_order,),
+ compress_method
+ )
+ };
let text = format!("Download .{}", compress_method.extension());
@@ -268,8 +252,6 @@ fn parametrized_link(
link: &str,
sort_method: Option<SortingMethod>,
sort_order: Option<SortingOrder>,
- color_scheme: ColorScheme,
- default_color_scheme: ColorScheme,
) -> String {
if let Some(method) = sort_method {
if let Some(order) = sort_order {
@@ -280,22 +262,10 @@ fn parametrized_link(
order
);
- if color_scheme != default_color_scheme {
- return format!("{}&theme={}", parametrized_link, color_scheme.to_slug());
- }
-
return parametrized_link;
}
}
- if color_scheme != default_color_scheme {
- return format!(
- "{}?theme={}",
- make_link_with_trailing_slash(&link),
- color_scheme.to_slug()
- );
- }
-
make_link_with_trailing_slash(&link)
}
@@ -305,8 +275,6 @@ fn build_link(
title: &str,
sort_method: Option<SortingMethod>,
sort_order: Option<SortingOrder>,
- color_scheme: ColorScheme,
- default_color_scheme: ColorScheme,
) -> Markup {
let mut link = format!("?sort={}&order=asc", name);
let mut help = format!("Sort by {} in ascending order", name);
@@ -326,10 +294,6 @@ fn build_link(
}
};
- if color_scheme != default_color_scheme {
- link = format!("{}&theme={}", &link, color_scheme.to_slug());
- }
-
html! {
span class=(class) {
span.chevron { (chevron) }
@@ -343,15 +307,13 @@ fn entry_row(
entry: Entry,
sort_method: Option<SortingMethod>,
sort_order: Option<SortingOrder>,
- color_scheme: ColorScheme,
- default_color_scheme: ColorScheme,
) -> Markup {
html! {
tr {
td {
p {
@if entry.is_dir() {
- a.directory href=(parametrized_link(&entry.link, sort_method, sort_order, color_scheme, default_color_scheme)) {
+ a.directory href=(parametrized_link(&entry.link, sort_method, sort_order)) {
(entry.name) "/"
}
} @else if entry.is_file() {
@@ -366,7 +328,7 @@ fn entry_row(
}
}
} @else if entry.is_symlink() {
- a.symlink href=(parametrized_link(&entry.link, sort_method, sort_order, color_scheme, default_color_scheme)) {
+ a.symlink href=(parametrized_link(&entry.link, sort_method, sort_order)) {
(entry.name) span.symlink-symbol { "⇢" }
}
}
@@ -395,443 +357,6 @@ fn entry_row(
}
}
-/// Partial: CSS
-fn css(color_scheme: ColorScheme) -> Markup {
- let theme = color_scheme.get_theme();
-
- let css = format!("
- html {{
- font-smoothing: antialiased;
- text-rendering: optimizeLegibility;
- width: 100%;
- height: 100%;
- }}
- body {{
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto,\"Helvetica Neue\", Helvetica, Arial, sans-serif;
- font-weight: 300;
- color: {text_color};
- background: {background};
- position: relative;
- min-height: 100%;
- }}
- .container {{
- padding: 1.5rem 5rem;
- }}
- .title {{
- word-break: break-word;
- }}
- a {{
- text-decoration: none;
- }}
- a.root, a.root:visited, .root-chevron {{
- font-weight: bold;
- color: {root_link_color};
- }}
- a:hover {{
- text-decoration: underline;
- }}
- a.directory, a.directory:visited {{
- font-weight: bold;
- color: {directory_link_color};
- }}
- a.file, a.file:visited, .error-back, .error-back:visited {{
- color: {file_link_color};
- }}
- a.symlink, a.symlink:visited {{
- color: {symlink_link_color};
- }}
- a.directory:hover {{
- color: {directory_link_color};
- }}
- a.file:hover {{
- color: {file_link_color};
- }}
- a.symlink:hover {{
- color: {symlink_link_color};
- }}
- .symlink-symbol {{
- display: inline-block;
- border: 1px solid {symlink_link_color};
- margin-left: 0.5rem;
- border-radius: .2rem;
- padding: 0 0.1rem;
- }}
- nav {{
- padding: 0 5rem;
- display: flex;
- justify-content: flex-end;
- }}
- nav > div {{
- position: relative;
- margin-left: 0.5rem;
- }}
- nav p {{
- padding: 0.5rem 1rem;
- width: 8rem;
- text-align: center;
- background: {switch_theme_background};
- color: {change_theme_link_color};
- }}
- nav p + * {{
- display: none;
- position: absolute;
- left: 0;
- right: 0;
- top: 100%;
- animation: show 0.5s ease;
- }}
- @keyframes show {{
- from {{
- opacity: 0;
- }}
- to {{
- opacity: 1;
- }}
- }}
- nav > div::hover p {{
- cursor: pointer;
- color: {switch_theme_link_color};
- }}
- nav > div:hover p + * {{
- display: block;
- border-top: 1px solid {switch_theme_border};
- }}
- nav .qrcode {{
- padding: 0.5rem;
- background: {switch_theme_background};
- }}
- nav .qrcode img {{
- display: block;
- }}
- nav .theme {{
- margin: 0;
- padding: 0;
- list-style-type: none;
- }}
- nav .theme li {{
- width: 100%;
- background: {switch_theme_background};
- }}
- nav .theme li a {{
- display: block;
- width: 100%;
- padding: 0.5rem 0;
- text-align: center;
- color: {switch_theme_link_color};
- }}
- nav .theme li a:visited {{
- color: {switch_theme_link_color};
- }}
- nav .theme li a::hover {{
- text-decoration: underline;
- color: {change_theme_link_color_hover};
- }}
- nav .theme li.active a {{
- font-weight: bold;
- color: {switch_theme_active};
- }}
- p {{
- margin: 0;
- padding: 0;
- }}
- h1 {{
- margin-top: 0;
- font-size: 1.5rem;
- }}
- table {{
- margin-top: 2rem;
- width: 100%;
- border: 0;
- table-layout: auto;
- background: {table_background};
- }}
- table thead tr th,
- table tbody tr td {{
- padding: 0.5625rem 0.625rem;
- font-size: 0.875rem;
- color: {table_text_color};
- text-align: left;
- line-height: 1.125rem;
- }}
- table thead tr th {{
- padding: 0.5rem 0.625rem 0.625rem;
- font-weight: bold;
- }}
- table thead th.size {{
- width: 6em;
- }}
- table thead th.date {{
- width: 15em;
- }}
- table tbody tr:nth-child(odd) {{
- background: {odd_row_background};
- }}
- table tbody tr:nth-child(even) {{
- background: {even_row_background};
- }}
- table thead {{
- background: {table_header_background};
- }}
- table tbody tr:hover {{
- background: {active_row_color};
- }}
- td.size-cell {{
- text-align: right;
- }}
- td.date-cell {{
- display: flex;
- justify-content: space-between;
- }}
- .at {{
- color: {at_color};
- }}
- .history {{
- color: {date_text_color};
- }}
- .file-entry {{
- display: flex;
- justify-content: space-between;
- }}
- span.size {{
- border-radius: 1rem;
- background: {size_background_color};
- padding: 0 0.25rem;
- font-size: 0.7rem;
- color: {size_text_color}
- }}
- .mobile-info {{
- display: none;
- }}
- th a, th a:visited, .chevron {{
- color: {table_header_text_color};
- }}
- .chevron, .root-chevron {{
- margin-right: .5rem;
- font-size: 1.2em;
- font-weight: bold;
- }}
- th span.active a, th span.active span {{
- color: {table_header_active_color};
- }}
- .back {{
- position: fixed;
- width: 3.8rem;
- height: 3.8rem;
- align-items: center;
- justify-content: center;
- bottom: 3rem;
- right: 3.75rem;
- background: {back_button_background};
- border-radius: 100%;
- box-shadow: 0 0 8px -4px #888888;
- color: {back_button_link_color};
- display: none;
- }}
- .back:visited {{
- color: {back_button_link_color};
- }}
- .back:hover {{
- color: {back_button_link_color_hover};
- font-weight: bold;
- text-decoration: none;
- background: {back_button_background_hover};
- }}
- .toolbar {{
- display: flex;
- justify-content: space-between;
- flex-wrap: wrap;
- }}
- .download {{
- margin-top: 1rem;
- padding: 0.125rem;
- display: flex;
- flex-direction: row;
- align-items: flex-start;
- flex-wrap: wrap;
- }}
- .download a, .download a:visited {{
- color: {download_button_link_color};
- }}
- .download a {{
- background: {download_button_background};
- padding: 0.5rem;
- border-radius: 0.2rem;
- }}
- .download a:hover {{
- background: {download_button_background_hover};
- color: {download_button_link_color_hover};
- }}
- .download a:not(:last-of-type) {{
- margin-right: 1rem;
- }}
- .upload {{
- margin-top: 1rem;
- display: flex;
- justify-content: flex-end;
- }}
- .upload p {{
- font-size: 0.8rem;
- margin-bottom: 1rem;
- color: {upload_text_color};
- }}
- .upload form {{
- padding: 1rem;
- border: 1px solid {upload_form_border_color};
- background: {upload_form_background};
- }}
- .upload button {{
- background: {upload_button_background};
- padding: 0.5rem;
- border-radius: 0.2rem;
- color: {upload_button_text_color};
- border: none;
- }}
- .upload div {{
- display: flex;
- align-items: baseline;
- justify-content: space-between;
- }}
- .drag-form {{
- display: none;
- background: {drag_background};
- position: absolute;
- border: 0.5rem dashed {drag_border_color};
- width: calc(100% - 1rem);
- height: calc(100% - 1rem);
- text-align: center;
- z-index: 2;
- }}
- .drag-title {{
- position: fixed;
- color: {drag_text_color};
- top: 50%;
- width: 100%;
- text-align: center;
- }}
- .error {{
- margin: 2rem;
- }}
- .error p {{
- margin: 1rem 0;
- font-size: 0.9rem;
- word-break: break-all;
- }}
- .error p:first-of-type {{
- font-size: 1.25rem;
- color: {error_color};
- margin-bottom: 2rem;
- }}
- .error p:nth-of-type(2) {{
- font-weight: bold;
- }}
- .error-nav {{
- margin-top: 4rem;
- }}
- @media (max-width: 760px) {{
- nav {{
- padding: 0 2.5rem;
- }}
- .container {{
- padding: 1.5rem 2.5rem;
- }}
- h1 {{
- font-size: 1.4em;
- }}
- td:not(:nth-child(1)), th:not(:nth-child(1)){{
- display: none;
- }}
- .mobile-info {{
- display: block;
- }}
- table tbody tr td {{
- padding-top: 0;
- padding-bottom: 0;
- }}
- a.directory {{
- display: block;
- padding: 0.5625rem 0;
- }}
- .file-entry {{
- align-items: center;
- }}
- a.root, a.file, a.symlink {{
- display: inline-block;
- flex: 1;
- padding: 0.5625rem 0;
- }}
- a.symlink {{
- width: 100%;
- }}
- .back {{
- display: flex;
- }}
- .back {{
- right: 1.5rem;
- }}
- }}
- @media (max-width: 600px) {{
- h1 {{
- font-size: 1.375em;
- }}
- }}
- @media (max-width: 400px) {{
- nav {{
- padding: 0 0.5rem;
- }}
- .container {{
- padding: 0.5rem;
- }}
- h1 {{
- font-size: 1.375em;
- }}
- .back {{
- right: 1.5rem;
- }}
- }}", background = theme.background,
- text_color = theme.text_color,
- directory_link_color = theme.directory_link_color,
- file_link_color = theme.file_link_color,
- symlink_link_color = theme.symlink_link_color,
- table_background = theme.table_background,
- table_text_color = theme.table_text_color,
- table_header_background = theme.table_header_background,
- table_header_text_color = theme.table_header_text_color,
- table_header_active_color = theme.table_header_active_color,
- active_row_color = theme.active_row_color,
- odd_row_background = theme.odd_row_background,
- even_row_background = theme.even_row_background,
- root_link_color = theme.root_link_color,
- download_button_background = theme.download_button_background,
- download_button_background_hover = theme.download_button_background_hover,
- download_button_link_color = theme.download_button_link_color,
- download_button_link_color_hover = theme.download_button_link_color_hover,
- back_button_background = theme.back_button_background,
- back_button_background_hover = theme.back_button_background_hover,
- back_button_link_color = theme.back_button_link_color,
- back_button_link_color_hover = theme.back_button_link_color_hover,
- date_text_color = theme.date_text_color,
- at_color = theme.at_color,
- switch_theme_background = theme.switch_theme_background,
- switch_theme_link_color = theme.switch_theme_link_color,
- switch_theme_active = theme.switch_theme_active,
- switch_theme_border = theme.switch_theme_border,
- change_theme_link_color = theme.change_theme_link_color,
- change_theme_link_color_hover = theme.change_theme_link_color_hover,
- upload_text_color = theme.upload_text_color,
- upload_form_border_color = theme.upload_form_border_color,
- upload_form_background = theme.upload_form_background,
- upload_button_background = theme.upload_button_background,
- upload_button_text_color = theme.upload_button_text_color,
- drag_background = theme.drag_background,
- drag_border_color = theme.drag_border_color,
- drag_text_color = theme.drag_text_color,
- size_background_color = theme.size_background_color,
- size_text_color = theme.size_text_color,
- error_color = theme.error_color);
- PreEscaped(css)
-}
-
/// Partial: up arrow
fn arrow_up() -> Markup {
PreEscaped("⇪".to_string())
@@ -853,21 +378,18 @@ fn chevron_down() -> Markup {
}
/// Partial: page header
-fn page_header(
- title: &str,
- color_scheme: ColorScheme,
- file_upload: bool,
- favicon_route: &str,
-) -> Markup {
+fn page_header(title: &str, file_upload: bool, favicon_route: &str, css_route: &str) -> Markup {
html! {
head {
meta charset="utf-8";
meta http-equiv="X-UA-Compatible" content="IE=edge";
meta name="viewport" content="width=device-width, initial-scale=1";
+
link rel="icon" type="image/svg+xml" href={ "/" (favicon_route) };
+ link rel="stylesheet" href={ "/" (css_route) };
+
title { (title) }
- style { (css(color_scheme)) }
@if file_upload {
(PreEscaped(r#"
<script>
@@ -937,29 +459,27 @@ pub fn render_error(
return_address: &str,
sort_method: Option<SortingMethod>,
sort_order: Option<SortingOrder>,
- color_scheme: ColorScheme,
- default_color_scheme: ColorScheme,
has_referer: bool,
display_back_link: bool,
favicon_route: &str,
+ css_route: &str,
+ default_color_scheme: &str,
+ default_color_scheme_dark: &str,
) -> Markup {
let link = if has_referer {
return_address.to_string()
} else {
- parametrized_link(
- return_address,
- sort_method,
- sort_order,
- color_scheme,
- default_color_scheme,
- )
+ parametrized_link(return_address, sort_method, sort_order)
};
html! {
(DOCTYPE)
html {
- (page_header(&error_code.to_string(), color_scheme, false, favicon_route))
- body {
+ (page_header(&error_code.to_string(), false, favicon_route, css_route))
+
+ body.(format!("default_theme_{}", default_color_scheme))
+ .(format!("default_theme_dark_{}", default_color_scheme_dark)) {
+
div.error {
p { (error_code.to_string()) }
@for error in error_description.lines() {
diff --git a/src/themes.rs b/src/themes.rs
deleted file mode 100644
index 12ae7e3..0000000
--- a/src/themes.rs
+++ /dev/null
@@ -1,261 +0,0 @@
-use serde::Deserialize;
-use structopt::clap::arg_enum;
-use strum_macros::EnumIter;
-
-/// Describes a theme
-pub struct Theme {
- pub background: &'static str,
- pub text_color: &'static str,
- pub directory_link_color: &'static str,
- pub file_link_color: &'static str,
- pub symlink_link_color: &'static str,
- pub table_background: &'static str,
- pub table_text_color: &'static str,
- pub table_header_background: &'static str,
- pub table_header_text_color: &'static str,
- pub table_header_active_color: &'static str,
- pub active_row_color: &'static str,
- pub odd_row_background: &'static str,
- pub even_row_background: &'static str,
- pub root_link_color: &'static str,
- pub download_button_background: &'static str,
- pub download_button_background_hover: &'static str,
- pub download_button_link_color: &'static str,
- pub download_button_link_color_hover: &'static str,
- pub back_button_background: &'static str,
- pub back_button_background_hover: &'static str,
- pub back_button_link_color: &'static str,
- pub back_button_link_color_hover: &'static str,
- pub date_text_color: &'static str,
- pub at_color: &'static str,
- pub switch_theme_background: &'static str,
- pub switch_theme_link_color: &'static str,
- pub switch_theme_active: &'static str,
- pub switch_theme_border: &'static str,
- pub change_theme_link_color: &'static str,
- pub change_theme_link_color_hover: &'static str,
- pub upload_text_color: &'static str,
- pub upload_form_border_color: &'static str,
- pub upload_form_background: &'static str,
- pub upload_button_background: &'static str,
- pub upload_button_text_color: &'static str,
- pub drag_background: &'static str,
- pub drag_border_color: &'static str,
- pub drag_text_color: &'static str,
- pub size_background_color: &'static str,
- pub size_text_color: &'static str,
- pub error_color: &'static str,
-}
-
-arg_enum! {
- #[derive(PartialEq, Deserialize, Clone, EnumIter, Copy)]
- #[serde(rename_all = "lowercase")]
- pub enum ColorScheme {
- Archlinux,
- Zenburn,
- Monokai,
- Squirrel,
- }
-}
-
-impl ColorScheme {
- /// Returns the URL-compatible name of a color scheme
- /// This must correspond to the name of the variant, in lowercase
- /// See https://github.com/svenstaro/miniserve/pull/55 for explanations
- pub fn to_slug(self) -> &'static str {
- match self {
- ColorScheme::Archlinux => "archlinux",
- ColorScheme::Zenburn => "zenburn",
- ColorScheme::Monokai => "monokai",
- ColorScheme::Squirrel => "squirrel",
- }
- }
-
- /// Returns wether a color scheme is dark
- pub fn is_dark(self) -> bool {
- match self {
- ColorScheme::Archlinux => true,
- ColorScheme::Zenburn => true,
- ColorScheme::Monokai => true,
- ColorScheme::Squirrel => false,
- }
- }
-
- /// Retrieves the color palette associated to a color scheme
- pub fn get_theme(self) -> Theme {
- match self {
- ColorScheme::Archlinux => Theme {
- background: "#383c4a",
- text_color: "#fefefe",
- directory_link_color: "#03a9f4",
- file_link_color: "#ea95ff",
- symlink_link_color: "#ff9800",
- table_background: "#353946",
- table_text_color: "#eeeeee",
- table_header_background: "#5294e2",
- table_header_text_color: "#eeeeee",
- table_header_active_color: "#ffffff",
- active_row_color: "#5194e259",
- odd_row_background: "#404552",
- even_row_background: "#4b5162",
- root_link_color: "#abb2bb",
- download_button_background: "#ea95ff",
- download_button_background_hover: "#eea7ff",
- download_button_link_color: "#ffffff",
- download_button_link_color_hover: "#ffffff",
- back_button_background: "#ea95ff",
- back_button_background_hover: "#ea95ff",
- back_button_link_color: "#ffffff",
- back_button_link_color_hover: "#ffffff",
- date_text_color: "#9ebbdc",
- at_color: "#9ebbdc",
- switch_theme_background: "#4b5162",
- switch_theme_link_color: "#fefefe",
- switch_theme_active: "#ea95ff",
- switch_theme_border: "#6a728a",
- change_theme_link_color: "#fefefe",
- change_theme_link_color_hover: "#fefefe",
- upload_text_color: "#fefefe",
- upload_form_border_color: "#353946",
- upload_form_background: "#4b5162",
- upload_button_background: "#ea95ff",
- upload_button_text_color: "#ffffff",
- drag_background: "#3333338f",
- drag_border_color: "#fefefe",
- drag_text_color: "#fefefe",
- size_background_color: "#5294e2",
- size_text_color: "#fefefe",
- error_color: "#e44b4b",
- },
- ColorScheme::Zenburn => Theme {
- background: "#3f3f3f",
- text_color: "#efefef",
- directory_link_color: "#f0dfaf",
- file_link_color: "#87D6D5",
- symlink_link_color: "#FFCCEE",
- table_background: "#4a4949",
- table_text_color: "#efefef",
- table_header_background: "#7f9f7f",
- table_header_text_color: "#efefef",
- table_header_active_color: "#efef8f",
- active_row_color: "#7e9f7f9c",
- odd_row_background: "#777777",
- even_row_background: "#5a5a5a",
- root_link_color: "#dca3a3",
- download_button_background: "#cc9393",
- download_button_background_hover: "#dca3a3",
- download_button_link_color: "#efefef",
- download_button_link_color_hover: "#efefef",
- back_button_background: "#cc9393",
- back_button_background_hover: "#cc9393",
- back_button_link_color: "#efefef",
- back_button_link_color_hover: "#efefef",
- date_text_color: "#cfbfaf",
- at_color: "#cfbfaf",
- switch_theme_background: "#4a4949",
- switch_theme_link_color: "#efefef",
- switch_theme_active: "#efef8f",
- switch_theme_border: "#5a5a5a",
- change_theme_link_color: "#efefef",
- change_theme_link_color_hover: "#efefef",
- upload_text_color: "#efefef",
- upload_form_border_color: "#4a4949",
- upload_form_background: "#777777",
- upload_button_background: "#cc9393",
- upload_button_text_color: "#efefef",
- drag_background: "#3333338f",
- drag_border_color: "#efefef",
- drag_text_color: "#efefef",
- size_background_color: "#7f9f7f",
- size_text_color: "#efefef",
- error_color: "#d06565",
- },
- ColorScheme::Monokai => Theme {
- background: "#272822",
- text_color: "#F8F8F2",
- directory_link_color: "#F92672",
- file_link_color: "#A6E22E",
- symlink_link_color: "#FD971F",
- table_background: "#3B3A32",
- table_text_color: "#F8F8F0",
- table_header_background: "#75715E",
- table_header_text_color: "#F8F8F2",
- table_header_active_color: "#E6DB74",
- active_row_color: "#ae81fe3d",
- odd_row_background: "#3E3D32",
- even_row_background: "#49483E",
- root_link_color: "#66D9EF",
- download_button_background: "#AE81FF",
- download_button_background_hover: "#c6a6ff",
- download_button_link_color: "#F8F8F0",
- download_button_link_color_hover: "#F8F8F0",
- back_button_background: "#AE81FF",
- back_button_background_hover: "#AE81FF",
- back_button_link_color: "#F8F8F0",
- back_button_link_color_hover: "#F8F8F0",
- date_text_color: "#66D9EF",
- at_color: "#66D9EF",
- switch_theme_background: "#3B3A32",
- switch_theme_link_color: "#F8F8F2",
- switch_theme_active: "#A6E22E",
- switch_theme_border: "#49483E",
- change_theme_link_color: "#F8F8F2",
- change_theme_link_color_hover: "#F8F8F2",
- upload_text_color: "#F8F8F2",
- upload_form_border_color: "#3B3A32",
- upload_form_background: "#49483E",
- upload_button_background: "#AE81FF",
- upload_button_text_color: "#F8F8F0",
- drag_background: "#3333338f",
- drag_border_color: "#F8F8F2",
- drag_text_color: "#F8F8F2",
- size_background_color: "#75715E",
- size_text_color: "#F8F8F2",
- error_color: "#d02929",
- },
- ColorScheme::Squirrel => Theme {
- background: "#FFFFFF",
- text_color: "#323232",
- directory_link_color: "#d02474",
- file_link_color: "#0086B3",
- symlink_link_color: "#ED6A43",
- table_background: "#ffffff",
- table_text_color: "#323232",
- table_header_background: "#323232",
- table_header_text_color: "#F5F5F5",
- table_header_active_color: "#FFFFFF",
- active_row_color: "#f6f8fa",
- odd_row_background: "#fbfbfb",
- even_row_background: "#f2f2f2",
- root_link_color: "#323232",
- download_button_background: "#d02474",
- download_button_background_hover: "#f52d8a",
- download_button_link_color: "#FFFFFF",
- download_button_link_color_hover: "#FFFFFF",
- back_button_background: "#d02474",
- back_button_background_hover: "#d02474",
- back_button_link_color: "#FFFFFF",
- back_button_link_color_hover: "#FFFFFF",
- date_text_color: "#797979",
- at_color: "#797979",
- switch_theme_background: "#323232",
- switch_theme_link_color: "#F5F5F5",
- switch_theme_active: "#d02474",
- switch_theme_border: "#49483E",
- change_theme_link_color: "#F5F5F5",
- change_theme_link_color_hover: "#F5F5F5",
- upload_text_color: "#323232",
- upload_form_border_color: "#d2d2d2",
- upload_form_background: "#f2f2f2",
- upload_button_background: "#d02474",
- upload_button_text_color: "#FFFFFF",
- drag_background: "#3333338f",
- drag_border_color: "#ffffff",
- drag_text_color: "#ffffff",
- size_background_color: "#323232",
- size_text_color: "#FFFFFF",
- error_color: "#d02424",
- },
- }
- }
-}