aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/archive.rs5
-rw-r--r--src/args.rs16
-rw-r--r--src/listing.rs35
-rw-r--r--src/main.rs13
-rw-r--r--src/renderer.rs541
-rw-r--r--src/themes.rs230
6 files changed, 686 insertions, 154 deletions
diff --git a/src/archive.rs b/src/archive.rs
index 206d252..b1119f0 100644
--- a/src/archive.rs
+++ b/src/archive.rs
@@ -25,6 +25,11 @@ impl CompressionMethod {
.to_string()
}
+ /// Lists compression methods
+ pub fn get_compression_methods() -> Vec<Self> {
+ vec![CompressionMethod::TarGz]
+ }
+
pub fn extension(&self) -> String {
match &self {
CompressionMethod::TarGz => "tar.gz",
diff --git a/src/args.rs b/src/args.rs
index 4f0dbf7..bcee0bc 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -3,6 +3,7 @@ use std::path::PathBuf;
use structopt::StructOpt;
use crate::auth;
+use crate::themes;
/// Possible characters for random routes
const ROUTE_ALPHABET: [char; 16] = [
@@ -47,6 +48,18 @@ struct CLIArgs {
/// Do not follow symbolic links
#[structopt(short = "P", long = "no-symlinks")]
no_symlinks: bool,
+
+ /// Default color scheme
+ #[structopt(
+ short = "c",
+ long = "color-scheme",
+ default_value = "Squirrel",
+ raw(
+ possible_values = "&themes::ColorScheme::variants()",
+ case_insensitive = "true",
+ )
+ )]
+ color_scheme: themes::ColorScheme,
}
/// Checks wether an interface is valid, i.e. it can be parsed into an IP address
@@ -89,6 +102,8 @@ pub fn parse_args() -> crate::MiniserveConfig {
None
};
+ let default_color_scheme = args.color_scheme;
+
let path_explicitly_chosen = args.path.is_some();
crate::MiniserveConfig {
@@ -100,5 +115,6 @@ pub fn parse_args() -> crate::MiniserveConfig {
path_explicitly_chosen,
no_symlinks: args.no_symlinks,
random_route,
+ default_color_scheme,
}
}
diff --git a/src/listing.rs b/src/listing.rs
index c4daf88..64eb575 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -11,6 +11,7 @@ use std::time::SystemTime;
use crate::archive;
use crate::errors;
use crate::renderer;
+use crate::themes;
/// Query parameters
#[derive(Debug, Deserialize)]
@@ -18,6 +19,7 @@ struct QueryParameters {
sort: Option<SortingMethod>,
order: Option<SortingOrder>,
download: Option<archive::CompressionMethod>,
+ theme: Option<themes::ColorScheme>,
}
/// Available sorting methods
@@ -77,6 +79,9 @@ pub enum EntryType {
/// Entry is a file
File,
+
+ /// Entry is a symlink
+ Symlink,
}
/// Entry
@@ -114,9 +119,20 @@ impl Entry {
}
}
+ /// Returns wether the entry is a directory
pub fn is_dir(&self) -> bool {
self.entry_type == EntryType::Directory
}
+
+ /// Returns wether the entry is a file
+ pub fn is_file(&self) -> bool {
+ self.entry_type == EntryType::File
+ }
+
+ /// Returns wether the entry is a symlink
+ pub fn is_symlink(&self) -> bool {
+ self.entry_type == EntryType::Symlink
+ }
}
pub fn file_handler(req: &HttpRequest<crate::MiniserveConfig>) -> Result<fs::NamedFile> {
@@ -131,6 +147,7 @@ pub fn directory_listing<S>(
req: &HttpRequest<S>,
skip_symlinks: bool,
random_route: Option<String>,
+ default_color_scheme: themes::ColorScheme,
) -> Result<HttpResponse, io::Error> {
let title = format!("Index of {}", req.path());
let base = Path::new(req.path());
@@ -138,15 +155,16 @@ pub fn directory_listing<S>(
let is_root = base.parent().is_none() || req.path() == random_route;
let page_parent = base.parent().map(|p| p.display().to_string());
- let (sort_method, sort_order, download) =
+ let (sort_method, sort_order, download, color_scheme) =
if let Ok(query) = Query::<QueryParameters>::extract(req) {
(
query.sort.clone(),
query.order.clone(),
query.download.clone(),
+ query.theme.clone(),
)
} else {
- (None, None, None)
+ (None, None, None, None)
};
let mut entries: Vec<Entry> = Vec::new();
@@ -174,7 +192,15 @@ pub fn directory_listing<S>(
Err(_) => None,
};
- if metadata.is_dir() {
+ if metadata.file_type().is_symlink() {
+ entries.push(Entry::new(
+ file_name,
+ EntryType::Symlink,
+ file_url,
+ None,
+ last_modification_date,
+ ));
+ } else if metadata.is_dir() {
entries.push(Entry::new(
file_name,
EntryType::Directory,
@@ -227,6 +253,8 @@ pub fn directory_listing<S>(
}
}
+ let color_scheme = color_scheme.unwrap_or(default_color_scheme);
+
if let Some(compression_method) = &download {
log::info!(
"Creating an archive ({extension}) of {path}...",
@@ -265,6 +293,7 @@ pub fn directory_listing<S>(
page_parent,
sort_method,
sort_order,
+ color_scheme,
)
.into_string(),
))
diff --git a/src/main.rs b/src/main.rs
index f662a73..79a5db2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,6 +15,7 @@ mod auth;
mod errors;
mod listing;
mod renderer;
+mod themes;
#[derive(Clone, Debug)]
/// Configuration of the Miniserve application
@@ -42,6 +43,9 @@ pub struct MiniserveConfig {
/// Enable random route generation
pub random_route: Option<String>,
+
+ /// Default color scheme
+ pub default_color_scheme: themes::ColorScheme,
}
fn main() {
@@ -178,6 +182,7 @@ fn configure_app(app: App<MiniserveConfig>) -> App<MiniserveConfig> {
let path = &app.state().path;
let no_symlinks = app.state().no_symlinks;
let random_route = app.state().random_route.clone();
+ let default_color_scheme = app.state().default_color_scheme.clone();
if path.is_file() {
None
} else {
@@ -186,7 +191,13 @@ fn configure_app(app: App<MiniserveConfig>) -> App<MiniserveConfig> {
.expect("Couldn't create path")
.show_files_listing()
.files_listing_renderer(move |dir, req| {
- listing::directory_listing(dir, req, no_symlinks, random_route.clone())
+ listing::directory_listing(
+ dir,
+ req,
+ no_symlinks,
+ random_route.clone(),
+ default_color_scheme.clone(),
+ )
}),
)
}
diff --git a/src/renderer.rs b/src/renderer.rs
index 66fc714..abb8730 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -5,6 +5,7 @@ use std::time::SystemTime;
use crate::archive;
use crate::listing;
+use crate::themes;
/// Renders the file listing
pub fn page(
@@ -14,41 +15,101 @@ pub fn page(
page_parent: Option<String>,
sort_method: Option<listing::SortingMethod>,
sort_order: Option<listing::SortingOrder>,
+ color_scheme: themes::ColorScheme,
) -> Markup {
html! {
- (page_header(page_title))
+ (page_header(page_title, &color_scheme))
body {
- span #top { }
- h1.title { (page_title) }
- div.download {
- (archive_button(archive::CompressionMethod::TarGz))
- }
- table {
- thead {
- th { (build_link("name", "Name", &sort_method, &sort_order)) }
- th { (build_link("size", "Size", &sort_method, &sort_order)) }
- th { (build_link("date", "Last modification", &sort_method, &sort_order)) }
+ (color_scheme_selector(&sort_method, &sort_order, &color_scheme))
+ div.container {
+ span #top { }
+ h1.title { (page_title) }
+ div.download {
+ @for compression_method in archive::CompressionMethod::get_compression_methods() {
+ (archive_button(compression_method))
+ }
}
- tbody {
- @if !is_root {
- @if let Some(parent) = page_parent {
- tr {
- td colspan="3" {
- span.chevron { (chevron_left()) }
- a.root href=(parent) {
- "Parent directory"
+ table {
+ thead {
+ th { (build_link("name", "Name", &sort_method, &sort_order, &color_scheme)) }
+ th { (build_link("size", "Size", &sort_method, &sort_order, &color_scheme)) }
+ th { (build_link("date", "Last modification", &sort_method, &sort_order, &color_scheme)) }
+ }
+ tbody {
+ @if !is_root {
+ @if let Some(parent) = page_parent {
+ tr {
+ td colspan="3" {
+ span.root-chevron { (chevron_left()) }
+ a.root href=(parametrized_link(&parent, &sort_method, &sort_order, &color_scheme)) {
+ "Parent directory"
+ }
}
}
}
}
+ @for entry in entries {
+ (entry_row(entry, &sort_method, &sort_order, &color_scheme))
+ }
+ }
+ }
+ a.back href="#top" {
+ (arrow_up())
+ }
+ }
+ }
+ }
+}
+
+/// Partial: color scheme selector
+fn color_scheme_selector(
+ sort_method: &Option<listing::SortingMethod>,
+ sort_order: &Option<listing::SortingOrder>,
+ active_color_scheme: &themes::ColorScheme,
+) -> Markup {
+ html! {
+ nav {
+ ul {
+ li {
+ a.change-theme href="#" title="Change theme" {
+ "Change theme..."
}
- @for entry in entries {
- (entry_row(entry))
+ ul {
+ @for color_scheme in themes::ColorScheme::get_color_schemes() {
+ @if active_color_scheme.get_name() == color_scheme.get_name() {
+ li.active {
+ (color_scheme_link(&sort_method, &sort_order, &color_scheme))
+ }
+ } @else {
+ li {
+ (color_scheme_link(&sort_method, &sort_order, &color_scheme))
+ }
+ }
+ }
}
}
}
- a.back href="#top" {
- (arrow_up())
+ }
+ }
+}
+
+/// Partial: color scheme link
+fn color_scheme_link(
+ sort_method: &Option<listing::SortingMethod>,
+ sort_order: &Option<listing::SortingOrder>,
+ color_scheme: &themes::ColorScheme,
+) -> Markup {
+ let link = parametrized_link("", &sort_method, &sort_order, &color_scheme);
+ let title = format!("Switch to {} theme", color_scheme.get_name());
+
+ html! {
+ a href=(link) title=(title) {
+ (color_scheme.get_name())
+ " "
+ @if color_scheme.is_dark() {
+ "(dark)"
+ } @ else {
+ "(light)"
}
}
}
@@ -66,12 +127,35 @@ fn archive_button(compress_method: archive::CompressionMethod) -> Markup {
}
}
+/// If they are set, adds query parameters to links to keep them across pages
+fn parametrized_link(
+ link: &str,
+ sort_method: &Option<listing::SortingMethod>,
+ sort_order: &Option<listing::SortingOrder>,
+ color_scheme: &themes::ColorScheme,
+) -> String {
+ if let Some(method) = sort_method {
+ if let Some(order) = sort_order {
+ return format!(
+ "{}?sort={}&order={}&theme={}",
+ link,
+ method.to_string(),
+ order.to_string(),
+ color_scheme.to_string()
+ );
+ }
+ }
+
+ format!("{}?theme={}", link.to_string(), color_scheme.to_string())
+}
+
/// Partial: table header link
fn build_link(
name: &str,
title: &str,
sort_method: &Option<listing::SortingMethod>,
sort_order: &Option<listing::SortingOrder>,
+ color_scheme: &themes::ColorScheme,
) -> Markup {
let mut link = format!("?sort={}&order=asc", name);
let mut help = format!("Sort by {} in ascending order", name);
@@ -94,31 +178,40 @@ fn build_link(
html! {
span class=(class) {
span.chevron { (chevron) }
- a href=(link) title=(help) { (title) }
+ a href=(format!("{}&theme={}", &link, color_scheme.to_string())) title=(help) { (title) }
}
}
}
/// Partial: row for an entry
-fn entry_row(entry: listing::Entry) -> Markup {
+fn entry_row(
+ entry: listing::Entry,
+ sort_method: &Option<listing::SortingMethod>,
+ sort_order: &Option<listing::SortingOrder>,
+ color_scheme: &themes::ColorScheme,
+) -> Markup {
html! {
tr {
td {
p {
@if entry.is_dir() {
- a.directory href=(entry.link) {
+ a.directory href=(parametrized_link(&entry.link, &sort_method, &sort_order, &color_scheme)) {
(entry.name) "/"
}
- } @else {
- a.file href=(entry.link) {
+ } @else if entry.is_file() {
+ a.file href=(&entry.link) {
(entry.name)
}
+ } @ else if entry.is_symlink() {
+ a.symlink href=(parametrized_link(&entry.link, &sort_method, &sort_order, &color_scheme)) {
+ (entry.name) span.symlink-symbol { "⇢" }
+ }
}
}
@if !entry.is_dir() {
@if let Some(size) = entry.size {
span .mobile-info {
- strong { "Size: " }
+ strong.field { "Size: " }
(size)
(br())
}
@@ -126,8 +219,9 @@ fn entry_row(entry: listing::Entry) -> Markup {
}
span .mobile-info {
@if let Some(modification_date) = convert_to_utc(entry.last_modification_date) {
- strong { "Last modification: " }
+ strong.field { "Last modification: " }
(modification_date.0) " "
+ span.at { " at " }
(modification_date.1) " "
}
@if let Some(modification_timer) = humanize_systemtime(entry.last_modification_date) {
@@ -146,14 +240,13 @@ fn entry_row(entry: listing::Entry) -> Markup {
@if let Some(modification_date) = convert_to_utc(entry.last_modification_date) {
span {
(modification_date.0) " "
- }
- span {
+ span.at { " at " }
(modification_date.1) " "
}
}
@if let Some(modification_timer) = humanize_systemtime(entry.last_modification_date) {
- span {
- "(" (modification_timer) ")"
+ span.history {
+ (modification_timer)
}
}
}
@@ -162,162 +255,310 @@ fn entry_row(entry: listing::Entry) -> Markup {
}
/// Partial: CSS
-fn css() -> Markup {
- (PreEscaped(r#"
- html {
+fn css(color_scheme: &themes::ColorScheme) -> Markup {
+ let theme = color_scheme.clone().get_theme();
+
+ let css = format!("
+ html {{
font-smoothing: antialiased;
text-rendering: optimizeLegibility;
- }
- body {
+ }}
+ body {{
margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,"Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto,\"Helvetica Neue\", Helvetica, Arial, sans-serif;
font-weight: 300;
- color: #444444;
- padding: 0.125rem;
- }
- strong {
+ color: {text_color};
+ background: {background};
+ }}
+ .container {{
+ padding: 1.5rem 5rem;
+ }}
+ a {{
+ text-decoration: none;
+ }}
+ a.root, a.root:visited, .root-chevron {{
font-weight: bold;
- }
- p {
+ 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 {{
+ 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;
+ }}
+ nav ul {{
+ text-align: right;
+ list-style: none;
margin: 0;
padding: 0;
- }
- h1 {
+ }}
+ nav ul li {{
+ display: block;
+ transition-duration: 0.5s;
+ float: right;
+ position: relative;
+ padding: 0.5rem 1rem;
+ background: {switch_theme_background};
+ width: 8rem;
+ text-align: center;
+ }}
+ nav ul li:hover {{
+ cursor: pointer;
+ text-decoration: none;
+ color: {change_theme_link_color}
+ }}
+ nav ul li a:hover {{
+ text-decoration: none;
+ color: {change_theme_link_color_hover};
+ }}
+ nav ul li ul {{
+ visibility: hidden;
+ opacity: 0;
+ position: absolute;
+ transition: all 0.5s ease;
+ margin-top: 0.5rem;
+ left: 0;
+ display: none;
+ text-align: center;
+ }}
+ nav ul li:hover > ul,
+ nav ul li ul:hover {{
+ visibility: visible;
+ opacity: 1;
+ display: block;
+ }}
+ nav ul li ul li:first-of-type {{
+ border-top: 1px solid {switch_theme_border};
+ }}
+ nav ul li ul li {{
+ clear: both;
+ width: 8rem;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ }}
+ nav ul li ul li a:hover {{
+ text-decoration: underline;
+ }}
+ nav ul li a, nav ul li ul li a, nav ul li a:visited, nav ul li ul li a:visited {{
+ color: {switch_theme_link_color}
+ }}
+ nav ul li ul li.active a {{
+ font-weight: bold;
+ color: {switch_theme_active};
+ }}
+ strong {{
+ font-weight: bold;
+ }}
+ .field {{
+ color: {field_color}
+ }}
+ p {{
+ margin: 0;
+ padding: 0;
+ }}
+ h1 {{
+ margin-top: 0;
font-size: 1.5rem;
- }
- table {
+ }}
+ table {{
margin-top: 2rem;
width: 100%;
- background: white;
border: 0;
table-layout: auto;
- }
- table thead {
- background: #efefef;
- }
- table tr th,
- table tr td {
+ background: {table_background};
+ }}
+ table thead tr th,
+ table tbody tr td {{
padding: 0.5625rem 0.625rem;
font-size: 0.875rem;
- color: #777c82;
+ color: {table_text_color};
text-align: left;
line-height: 1.125rem;
width: 33.333%;
- }
- table tr th {
+ }}
+ table thead tr th {{
padding: 0.5rem 0.625rem 0.625rem;
font-weight: bold;
- color: #444444;
- }
- table tr:nth-child(even) {
- background: #f6f6f6;
- }
- table tr:hover {
- background: #deeef7a6;
- }
- a {
- text-decoration: none;
- color: #3498db;
- }
- a.root, a.root:visited {
- font-weight: bold;
- color: #777c82;
- }
- a.directory {
- font-weight: bold;
- }
- a:hover {
- text-decoration: underline;
- }
- a:visited {
- color: #8e44ad;
- }
- td.date-cell {
+ }}
+ 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.date-cell {{
display: flex;
width: calc(100% - 1.25rem);
- }
- td.date-cell span:first-of-type,
- td.date-cell span:nth-of-type(2) {
- flex-basis:4.5rem;
- }
- td.date-cell span:nth-of-type(3), .history {
- color: #c5c5c5;
- }
- .file, .directory {
+ justify-content: space-between;
+ }}
+ .at {{
+ color: {at_color};
+ }}
+ .history {{
+ color: {date_text_color};
+ }}
+ .file, .directory, .symlink {{
display: block;
- }
- .mobile-info {
+ }}
+ .mobile-info {{
display: none;
- }
- th a, th a:visited, .chevron {
- color: #777c82;
- }
- .chevron {
+ }}
+ 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: #444444;
- }
- .back {
+ }}
+ th span.active a, th span.active span {{
+ color: {table_header_active_color};
+ }}
+ .back {{
position: fixed;
- bottom: 1.1rem;
- right: 0.625rem;
- background: #e0e0e0;
+ bottom: 3rem;
+ right: 3.75rem;
+ background: {back_button_background};
border-radius: 100%;
box-shadow: 0 0 8px -4px #888888;
- opacity: 0.8;
- padding: 1rem 1.1rem;
- color: #444444;
- }
- .back:visited {
- color: #444444;
- }
- .back:hover {
- color: #3498db;
+ padding: 1.4rem 1.5rem;
+ 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;
- }
- .download {
+ background: {back_button_background_hover};
+ }}
+ .download {{
display: flex;
flex-wrap: wrap;
margin-top: .5rem;
padding: 0.125rem;
- }
- .download a, .download a:visited {
- color: #3498db;
- }
- .download a {
- background: #efefef;
+ }}
+ .download a, .download a:visited {{
+ color: {download_button_link_color};
+ }}
+ .download a {{
+ background: {download_button_background};
padding: 0.5rem;
border-radius: 0.2rem;
margin-top: 1rem;
- }
- .download a:hover {
- background: #deeef7a6;
- }
- .download a:not(:last-of-type) {
+ }}
+ .download a:hover {{
+ background: {download_button_background_hover};
+ color: {download_button_link_color_hover};
+ }}
+ .download a:not(:last-of-type) {{
margin-right: 1rem;
- }
- @media (max-width: 600px) {
- h1 {
+ }}
+ @media (max-width: 760px) {{
+ nav {{
+ padding: 0 2.5rem;
+ }}
+ .container {{
+ padding: 1.5rem 2.5rem;
+ }}
+ h1 {{
font-size: 1.375em;
- }
- td:not(:nth-child(1)), th:not(:nth-child(1)){
+ }}
+ td:not(:nth-child(1)), th:not(:nth-child(1)){{
display: none;
- }
- .mobile-info {
+ }}
+ .mobile-info {{
display: block;
- }
- .file, .directory{
+ }}
+ .file, .directory, .symlink{{
padding-bottom: 1rem;
- }
- }
- @media (max-width: 400px) {
- h1 {
+ }}
+ .back {{
+ right: 1.5rem;
+ }}
+ .back {{
+ display: initial;
+ }}
+ }}
+ @media (max-width: 400px) {{
+ nav {{
+ padding: 0 0.5rem;
+ }}
+ .container {{
+ padding: 0.5rem;
+ }}
+ h1 {{
font-size: 1.375em;
- }
- }"#.to_string()))
+ }}
+ .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,
+ field_color = theme.field_color);
+ (PreEscaped(css))
}
/// Partial: up arrow
@@ -346,7 +587,7 @@ fn chevron_down() -> Markup {
}
/// Partial: page header
-fn page_header(page_title: &str) -> Markup {
+fn page_header(page_title: &str, color_scheme: &themes::ColorScheme) -> Markup {
html! {
(DOCTYPE)
html {
@@ -354,7 +595,7 @@ fn page_header(page_title: &str) -> Markup {
meta http-equiv="X-UA-Compatible" content="IE=edge";
meta name="viewport" content="width=device-width, initial-scale=1";
title { (page_title) }
- style { (css()) }
+ style { (css(&color_scheme)) }
}
}
}
@@ -365,7 +606,7 @@ fn page_header(page_title: &str) -> Markup {
fn convert_to_utc(src_time: Option<SystemTime>) -> Option<(String, String)> {
src_time.map(DateTime::<Utc>::from).map(|date_time| {
(
- date_time.format("%e %b").to_string(),
+ date_time.format("%b %e").to_string(),
date_time.format("%R").to_string(),
)
})
diff --git a/src/themes.rs b/src/themes.rs
new file mode 100644
index 0000000..d04eab2
--- /dev/null
+++ b/src/themes.rs
@@ -0,0 +1,230 @@
+use serde::Deserialize;
+use structopt::clap::{_clap_count_exprs, arg_enum};
+
+arg_enum! {
+ #[derive(Debug, Deserialize, Clone)]
+ #[serde(rename_all = "lowercase")]
+ pub enum ColorScheme {
+ Archlinux,
+ Zenburn,
+ Monokai,
+ Squirrel,
+ }
+}
+
+impl ColorScheme {
+ /// Returns the URL-compatible name of a color scheme
+ pub fn to_string(&self) -> String {
+ match &self {
+ ColorScheme::Archlinux => "archlinux",
+ ColorScheme::Zenburn => "zenburn",
+ ColorScheme::Monokai => "monokai",
+ ColorScheme::Squirrel => "squirrel",
+ }
+ .to_string()
+ }
+
+ /// 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,
+ }
+ }
+
+ /// Returns the name of a color scheme
+ pub fn get_name(&self) -> String {
+ match &self {
+ ColorScheme::Archlinux => "Archlinux",
+ ColorScheme::Zenburn => "Zenburn",
+ ColorScheme::Monokai => "Monokai",
+ ColorScheme::Squirrel => "Squirrel",
+ }
+ .to_string()
+ }
+
+ /// Lists available color schemes
+ pub fn get_color_schemes() -> Vec<Self> {
+ vec![
+ ColorScheme::Archlinux,
+ ColorScheme::Zenburn,
+ ColorScheme::Monokai,
+ ColorScheme::Squirrel,
+ ]
+ }
+
+ /// Retrieves the color palette associated to a color scheme
+ pub fn get_theme(self) -> Theme {
+ match self {
+ ColorScheme::Archlinux => Theme {
+ background: "#383c4a".to_string(),
+ text_color: "#fefefe".to_string(),
+ directory_link_color: "#03a9f4".to_string(),
+ file_link_color: "#ea95ff".to_string(),
+ symlink_link_color: "#ff9800".to_string(),
+ table_background: "#353946".to_string(),
+ table_text_color: "#eeeeee".to_string(),
+ table_header_background: "#5294e2".to_string(),
+ table_header_text_color: "#eeeeee".to_string(),
+ table_header_active_color: "#ffffff".to_string(),
+ active_row_color: "#5194e259".to_string(),
+ odd_row_background: "#404552".to_string(),
+ even_row_background: "#4b5162".to_string(),
+ root_link_color: "#abb2bb".to_string(),
+ download_button_background: "#ea95ff".to_string(),
+ download_button_background_hover: "#eea7ff".to_string(),
+ download_button_link_color: "#ffffff".to_string(),
+ download_button_link_color_hover: "#ffffff".to_string(),
+ back_button_background: "#ea95ff".to_string(),
+ back_button_background_hover: "#ea95ff".to_string(),
+ back_button_link_color: "#ffffff".to_string(),
+ back_button_link_color_hover: "#ffffff".to_string(),
+ date_text_color: "#9ebbdc".to_string(),
+ at_color: "#9ebbdc".to_string(),
+ switch_theme_background: "#4b5162".to_string(),
+ switch_theme_link_color: "#fefefe".to_string(),
+ switch_theme_active: "#ea95ff".to_string(),
+ switch_theme_border: "#6a728a".to_string(),
+ change_theme_link_color: "#fefefe".to_string(),
+ change_theme_link_color_hover: "#fefefe".to_string(),
+ field_color: "#859cb9".to_string(),
+ },
+ ColorScheme::Zenburn => Theme {
+ background: "#3f3f3f".to_string(),
+ text_color: "#efefef".to_string(),
+ directory_link_color: "#f0dfaf".to_string(),
+ file_link_color: "#87D6D5".to_string(),
+ symlink_link_color: "#FFCCEE".to_string(),
+ table_background: "#4a4949".to_string(),
+ table_text_color: "#efefef".to_string(),
+ table_header_background: "#7f9f7f".to_string(),
+ table_header_text_color: "#efefef".to_string(),
+ table_header_active_color: "#efef8f".to_string(),
+ active_row_color: "#7e9f7f9c".to_string(),
+ odd_row_background: "#777777".to_string(),
+ even_row_background: "#5a5a5a".to_string(),
+ root_link_color: "#dca3a3".to_string(),
+ download_button_background: "#cc9393".to_string(),
+ download_button_background_hover: "#dca3a3".to_string(),
+ download_button_link_color: "#efefef".to_string(),
+ download_button_link_color_hover: "#efefef".to_string(),
+ back_button_background: "#cc9393".to_string(),
+ back_button_background_hover: "#cc9393".to_string(),
+ back_button_link_color: "#efefef".to_string(),
+ back_button_link_color_hover: "#efefef".to_string(),
+ date_text_color: "#cfbfaf".to_string(),
+ at_color: "#cfbfaf".to_string(),
+ switch_theme_background: "#4a4949".to_string(),
+ switch_theme_link_color: "#efefef".to_string(),
+ switch_theme_active: "#efef8f".to_string(),
+ switch_theme_border: "#5a5a5a".to_string(),
+ change_theme_link_color: "#efefef".to_string(),
+ change_theme_link_color_hover: "#efefef".to_string(),
+ field_color: "#9fc3a1".to_string(),
+ },
+ ColorScheme::Monokai => Theme {
+ background: "#272822".to_string(),
+ text_color: "#F8F8F2".to_string(),
+ directory_link_color: "#F92672".to_string(),
+ file_link_color: "#A6E22E".to_string(),
+ symlink_link_color: "#FD971F".to_string(),
+ table_background: "#3B3A32".to_string(),
+ table_text_color: "#F8F8F0".to_string(),
+ table_header_background: "#75715E".to_string(),
+ table_header_text_color: "#F8F8F2".to_string(),
+ table_header_active_color: "#E6DB74".to_string(),
+ active_row_color: "#ae81fe3d".to_string(),
+ odd_row_background: "#3E3D32".to_string(),
+ even_row_background: "#49483E".to_string(),
+ root_link_color: "#66D9EF".to_string(),
+ download_button_background: "#AE81FF".to_string(),
+ download_button_background_hover: "#c6a6ff".to_string(),
+ download_button_link_color: "#F8F8F0".to_string(),
+ download_button_link_color_hover: "#F8F8F0".to_string(),
+ back_button_background: "#AE81FF".to_string(),
+ back_button_background_hover: "#AE81FF".to_string(),
+ back_button_link_color: "#F8F8F0".to_string(),
+ back_button_link_color_hover: "#F8F8F0".to_string(),
+ date_text_color: "#66D9EF".to_string(),
+ at_color: "#66D9EF".to_string(),
+ switch_theme_background: "#3B3A32".to_string(),
+ switch_theme_link_color: "#F8F8F2".to_string(),
+ switch_theme_active: "#A6E22E".to_string(),
+ switch_theme_border: "#49483E".to_string(),
+ change_theme_link_color: "#F8F8F2".to_string(),
+ change_theme_link_color_hover: "#F8F8F2".to_string(),
+ field_color: "#ccc7a7".to_string(),
+ },
+ ColorScheme::Squirrel => Theme {
+ background: "#FFFFFF".to_string(),
+ text_color: "#323232".to_string(),
+ directory_link_color: "#d02474".to_string(),
+ file_link_color: "#0086B3".to_string(),
+ symlink_link_color: "#ED6A43".to_string(),
+ table_background: "#F5F5F5".to_string(),
+ table_text_color: "#323232".to_string(),
+ table_header_background: "#323232".to_string(),
+ table_header_text_color: "#F5F5F5".to_string(),
+ table_header_active_color: "#FFFFFF".to_string(),
+ active_row_color: "#f6f8fa".to_string(),
+ odd_row_background: "#fbfbfb".to_string(),
+ even_row_background: "#f2f2f2".to_string(),
+ root_link_color: "#323232".to_string(),
+ download_button_background: "#d02474".to_string(),
+ download_button_background_hover: "#f52d8a".to_string(),
+ download_button_link_color: "#F8F8F0".to_string(),
+ download_button_link_color_hover: "#F8F8F0".to_string(),
+ back_button_background: "#d02474".to_string(),
+ back_button_background_hover: "#d02474".to_string(),
+ back_button_link_color: "#F8F8F0".to_string(),
+ back_button_link_color_hover: "#F8F8F0".to_string(),
+ date_text_color: "#797979".to_string(),
+ at_color: "#797979".to_string(),
+ switch_theme_background: "#323232".to_string(),
+ switch_theme_link_color: "#F5F5F5".to_string(),
+ switch_theme_active: "#d02474".to_string(),
+ switch_theme_border: "#49483E".to_string(),
+ change_theme_link_color: "#F5F5F5".to_string(),
+ change_theme_link_color_hover: "#F5F5F5".to_string(),
+ field_color: "#797979".to_string(),
+ },
+ }
+ }
+}
+
+/// Describes a theme
+pub struct Theme {
+ pub background: String,
+ pub text_color: String,
+ pub directory_link_color: String,
+ pub file_link_color: String,
+ pub symlink_link_color: String,
+ pub table_background: String,
+ pub table_text_color: String,
+ pub table_header_background: String,
+ pub table_header_text_color: String,
+ pub table_header_active_color: String,
+ pub active_row_color: String,
+ pub odd_row_background: String,
+ pub even_row_background: String,
+ pub root_link_color: String,
+ pub download_button_background: String,
+ pub download_button_background_hover: String,
+ pub download_button_link_color: String,
+ pub download_button_link_color_hover: String,
+ pub back_button_background: String,
+ pub back_button_background_hover: String,
+ pub back_button_link_color: String,
+ pub back_button_link_color_hover: String,
+ pub date_text_color: String,
+ pub at_color: String,
+ pub switch_theme_background: String,
+ pub switch_theme_link_color: String,
+ pub switch_theme_active: String,
+ pub switch_theme_border: String,
+ pub change_theme_link_color: String,
+ pub change_theme_link_color_hover: String,
+ pub field_color: String,
+}