styles: semantic html rather than all being pre-wrapped to generalise for others
This commit is contained in:
parent
9f0e3aae6d
commit
a7c74241a0
7 changed files with 52 additions and 38 deletions
|
|
@ -51,16 +51,13 @@
|
||||||
# Maximum length for website URLs. 0 for unlimited.
|
# Maximum length for website URLs. 0 for unlimited.
|
||||||
# BOOK_MAX_WEBSITE_LENGTH=0
|
# BOOK_MAX_WEBSITE_LENGTH=0
|
||||||
|
|
||||||
# Separator between guestbook entries.
|
|
||||||
# BOOK_SEPARATOR=------------------------------------------------------------
|
|
||||||
|
|
||||||
# Path to a CSS file. Takes precedence over BOOK_STYLE.
|
# Path to a CSS file. Takes precedence over BOOK_STYLE.
|
||||||
# BOOK_STYLE_FILE=./templates/default.css
|
# BOOK_STYLE_FILE=./templates/default.css
|
||||||
|
|
||||||
# Custom CSS injected into a style tag.
|
# Custom CSS injected into a style tag.
|
||||||
# Classes: .guestbook-form, .guestbook-prompt, .guestbook-label, .guestbook-input,
|
# Classes: .guestbook-form, .guestbook-prompt, .guestbook-label, .guestbook-input,
|
||||||
# .guestbook-textarea, .guestbook-button, .entry-header, .entry-date, .entry-name,
|
# .guestbook-textarea, .guestbook-button, .entry, .entry-header, .entry-date,
|
||||||
# .entry-website, .entry-body, .entry-separator
|
# .entry-name, .entry-website, .entry-body
|
||||||
# BOOK_STYLE=
|
# BOOK_STYLE=
|
||||||
|
|
||||||
# Text shown above the form.
|
# Text shown above the form.
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ in
|
||||||
css = mkOption {
|
css = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "";
|
default = "";
|
||||||
description = "Custom CSS injected into a style tag. Use class names: .guestbook-form, .guestbook-prompt, .guestbook-label, .guestbook-input, .guestbook-textarea, .guestbook-button, .guestbook-canvas, .entry-header, .entry-date, .entry-name, .entry-website, .entry-body, .entry-drawing, .entry-separator";
|
description = "Custom CSS injected into a style tag. Use class names: .guestbook-form, .guestbook-prompt, .guestbook-label, .guestbook-input, .guestbook-textarea, .guestbook-button, .guestbook-canvas, .entry, .entry-header, .entry-date, .entry-name, .entry-website, .entry-body, .entry-drawing";
|
||||||
};
|
};
|
||||||
|
|
||||||
cssFile = mkOption {
|
cssFile = mkOption {
|
||||||
|
|
@ -229,12 +229,6 @@ in
|
||||||
description = "Custom success page template with {{title}} and {{style}} placeholders. Uses built-in default if null.";
|
description = "Custom success page template with {{title}} and {{style}} placeholders. Uses built-in default if null.";
|
||||||
};
|
};
|
||||||
|
|
||||||
separator = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "------------------------------------------------------------";
|
|
||||||
description = "Separator between guestbook entries.";
|
|
||||||
};
|
|
||||||
|
|
||||||
greeting = mkOption {
|
greeting = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "Thanks for visiting. Sign the guestbook!";
|
default = "Thanks for visiting. Sign the guestbook!";
|
||||||
|
|
@ -308,7 +302,6 @@ in
|
||||||
BOOK_MAX_NAME_LENGTH = toString cfg.limits.name;
|
BOOK_MAX_NAME_LENGTH = toString cfg.limits.name;
|
||||||
BOOK_MAX_MESSAGE_LENGTH = toString cfg.limits.message;
|
BOOK_MAX_MESSAGE_LENGTH = toString cfg.limits.message;
|
||||||
BOOK_MAX_WEBSITE_LENGTH = toString cfg.limits.website;
|
BOOK_MAX_WEBSITE_LENGTH = toString cfg.limits.website;
|
||||||
BOOK_SEPARATOR = cfg.styles.separator;
|
|
||||||
BOOK_STYLE = cfg.styles.css;
|
BOOK_STYLE = cfg.styles.css;
|
||||||
BOOK_FORM_PROMPT = cfg.styles.greeting;
|
BOOK_FORM_PROMPT = cfg.styles.greeting;
|
||||||
BOOK_BUTTON_TEXT = cfg.styles.labels.submit;
|
BOOK_BUTTON_TEXT = cfg.styles.labels.submit;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ pub struct Config {
|
||||||
pub voice_note_max_duration: u32,
|
pub voice_note_max_duration: u32,
|
||||||
pub template: Option<String>,
|
pub template: Option<String>,
|
||||||
pub success_template: Option<String>,
|
pub success_template: Option<String>,
|
||||||
pub separator: String,
|
|
||||||
pub style: String,
|
pub style: String,
|
||||||
pub form_prompt: String,
|
pub form_prompt: String,
|
||||||
pub button_text: String,
|
pub button_text: String,
|
||||||
|
|
@ -131,8 +130,6 @@ impl Config {
|
||||||
.unwrap_or_else(|_| "20".into())
|
.unwrap_or_else(|_| "20".into())
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| "BOOK_VOICE_NOTE_MAX_DURATION must be a number")?,
|
.map_err(|_| "BOOK_VOICE_NOTE_MAX_DURATION must be a number")?,
|
||||||
separator: env::var("BOOK_SEPARATOR")
|
|
||||||
.unwrap_or_else(|_| "------------------------------------------------------------".into()),
|
|
||||||
template: env::var("BOOK_TEMPLATE").ok().map(|path| {
|
template: env::var("BOOK_TEMPLATE").ok().map(|path| {
|
||||||
std::fs::read_to_string(&path)
|
std::fs::read_to_string(&path)
|
||||||
.unwrap_or_else(|e| panic!("failed to read template {path}: {e}"))
|
.unwrap_or_else(|e| panic!("failed to read template {path}: {e}"))
|
||||||
|
|
|
||||||
|
|
@ -238,9 +238,8 @@ pub fn render_error_page(config: &Config, error: &str) -> String {
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
{error}
|
<p>{error}</p>
|
||||||
|
<p><a href="/">← back</a></p>
|
||||||
<a href="/">← back</a>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>"#,
|
</html>"#,
|
||||||
|
|
@ -271,7 +270,7 @@ fn render_entry(entry: &Entry, config: &Config) -> String {
|
||||||
escape_html(&entry.meta.name)
|
escape_html(&entry.meta.name)
|
||||||
};
|
};
|
||||||
let mut header = format!(
|
let mut header = format!(
|
||||||
"<span class=\"entry-header\"><span class=\"entry-date\">{}</span> - <span class=\"entry-name\">{}</span>",
|
"<header class=\"entry-header\"><span class=\"entry-date\">{}</span> - <span class=\"entry-name\">{}</span>",
|
||||||
&entry.meta.date[..10], name
|
&entry.meta.date[..10], name
|
||||||
);
|
);
|
||||||
if config.enable_website_links && !entry.meta.website.is_empty() {
|
if config.enable_website_links && !entry.meta.website.is_empty() {
|
||||||
|
|
@ -281,7 +280,7 @@ fn render_entry(entry: &Entry, config: &Config) -> String {
|
||||||
website, website
|
website, website
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
header.push_str("</span>");
|
header.push_str("</header>");
|
||||||
let body = if config.enable_html_injection {
|
let body = if config.enable_html_injection {
|
||||||
entry.body.clone()
|
entry.body.clone()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -305,8 +304,7 @@ fn render_entry(entry: &Entry, config: &Config) -> String {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
format!(
|
format!(
|
||||||
"\n{header}\n{drawing_html}{voice_note_html}\n<span class=\"entry-body\">{body}</span>\n\n<span class=\"entry-separator\">{}</span>\n",
|
"<article class=\"entry\">{header}{drawing_html}{voice_note_html}<div class=\"entry-body\">{body}</div></article>"
|
||||||
config.separator
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,7 +343,6 @@ mod tests {
|
||||||
voice_note_max_duration: 20,
|
voice_note_max_duration: 20,
|
||||||
template: None,
|
template: None,
|
||||||
success_template: None,
|
success_template: None,
|
||||||
separator: "---".into(),
|
|
||||||
style: String::new(),
|
style: String::new(),
|
||||||
form_prompt: "Thanks for visiting. Sign the guestbook!".into(),
|
form_prompt: "Thanks for visiting. Sign the guestbook!".into(),
|
||||||
button_text: "sign".into(),
|
button_text: "sign".into(),
|
||||||
|
|
@ -400,7 +397,7 @@ mod tests {
|
||||||
assert!(html.contains("entry-header"));
|
assert!(html.contains("entry-header"));
|
||||||
assert!(html.contains("entry-name"));
|
assert!(html.contains("entry-name"));
|
||||||
assert!(html.contains("entry-body"));
|
assert!(html.contains("entry-body"));
|
||||||
assert!(html.contains("entry-separator"));
|
assert!(html.contains("<article class=\"entry\">"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,6 @@ mod tests {
|
||||||
voice_note_max_duration: 20,
|
voice_note_max_duration: 20,
|
||||||
template: None,
|
template: None,
|
||||||
success_template: None,
|
success_template: None,
|
||||||
separator: "---".into(),
|
|
||||||
style: String::new(),
|
style: String::new(),
|
||||||
form_prompt: "Thanks for visiting. Sign the guestbook!".into(),
|
form_prompt: "Thanks for visiting. Sign the guestbook!".into(),
|
||||||
button_text: "sign".into(),
|
button_text: "sign".into(),
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,32 @@
|
||||||
max-width: 70ch;
|
max-width: 70ch;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Form */
|
/* Form */
|
||||||
.guestbook-prompt {}
|
.guestbook-prompt {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
.guestbook-form {}
|
.guestbook-form {}
|
||||||
.guestbook-label {}
|
.guestbook-label {
|
||||||
.guestbook-input {}
|
display: block;
|
||||||
|
}
|
||||||
|
.guestbook-input {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
.guestbook-textarea {
|
.guestbook-textarea {
|
||||||
|
display: block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
.guestbook-button {
|
.guestbook-button {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drawings */
|
/* Drawings */
|
||||||
|
|
@ -35,12 +45,19 @@
|
||||||
.guestbook-canvas-tools a {
|
.guestbook-canvas-tools a {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.guestbook-drawing-wrap {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
.guestbook-drawing-inline a {
|
.guestbook-drawing-inline a {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.guestbook-drawing-content:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.guestbook-drawing-content {
|
.guestbook-drawing-content {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
.guestbook-swatch {
|
.guestbook-swatch {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
@ -65,6 +82,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Voice notes */
|
/* Voice notes */
|
||||||
|
.guestbook-voice-wrap {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
.guestbook-voice-record.recording {
|
.guestbook-voice-record.recording {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
@ -80,14 +101,25 @@
|
||||||
}
|
}
|
||||||
audio {
|
audio {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 0.6em;
|
margin-bottom: 0.3em;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Entries */
|
/* Entries */
|
||||||
.entry-header {}
|
.entry {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
.entry:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.entry-header {
|
||||||
|
margin-bottom: 0.2em;
|
||||||
|
}
|
||||||
.entry-date {}
|
.entry-date {}
|
||||||
.entry-name {}
|
.entry-name {}
|
||||||
.entry-website {}
|
.entry-website {}
|
||||||
.entry-body {}
|
.entry-body {
|
||||||
.entry-separator {}
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,8 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
Thanks! Your message is pending approval.
|
<p>Thanks! Your message is pending approval.</p>
|
||||||
|
<p><a href="/">← back</a></p>
|
||||||
<a href="/">← back</a>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue