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.
|
||||
# BOOK_MAX_WEBSITE_LENGTH=0
|
||||
|
||||
# Separator between guestbook entries.
|
||||
# BOOK_SEPARATOR=------------------------------------------------------------
|
||||
|
||||
# Path to a CSS file. Takes precedence over BOOK_STYLE.
|
||||
# BOOK_STYLE_FILE=./templates/default.css
|
||||
|
||||
# Custom CSS injected into a style tag.
|
||||
# Classes: .guestbook-form, .guestbook-prompt, .guestbook-label, .guestbook-input,
|
||||
# .guestbook-textarea, .guestbook-button, .entry-header, .entry-date, .entry-name,
|
||||
# .entry-website, .entry-body, .entry-separator
|
||||
# .guestbook-textarea, .guestbook-button, .entry, .entry-header, .entry-date,
|
||||
# .entry-name, .entry-website, .entry-body
|
||||
# BOOK_STYLE=
|
||||
|
||||
# Text shown above the form.
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ in
|
|||
css = mkOption {
|
||||
type = types.str;
|
||||
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 {
|
||||
|
|
@ -229,12 +229,6 @@ in
|
|||
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 {
|
||||
type = types.str;
|
||||
default = "Thanks for visiting. Sign the guestbook!";
|
||||
|
|
@ -308,7 +302,6 @@ in
|
|||
BOOK_MAX_NAME_LENGTH = toString cfg.limits.name;
|
||||
BOOK_MAX_MESSAGE_LENGTH = toString cfg.limits.message;
|
||||
BOOK_MAX_WEBSITE_LENGTH = toString cfg.limits.website;
|
||||
BOOK_SEPARATOR = cfg.styles.separator;
|
||||
BOOK_STYLE = cfg.styles.css;
|
||||
BOOK_FORM_PROMPT = cfg.styles.greeting;
|
||||
BOOK_BUTTON_TEXT = cfg.styles.labels.submit;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ pub struct Config {
|
|||
pub voice_note_max_duration: u32,
|
||||
pub template: Option<String>,
|
||||
pub success_template: Option<String>,
|
||||
pub separator: String,
|
||||
pub style: String,
|
||||
pub form_prompt: String,
|
||||
pub button_text: String,
|
||||
|
|
@ -131,8 +130,6 @@ impl Config {
|
|||
.unwrap_or_else(|_| "20".into())
|
||||
.parse()
|
||||
.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| {
|
||||
std::fs::read_to_string(&path)
|
||||
.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>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
{error}
|
||||
|
||||
<a href="/">← back</a>
|
||||
<p>{error}</p>
|
||||
<p><a href="/">← back</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>"#,
|
||||
|
|
@ -271,7 +270,7 @@ fn render_entry(entry: &Entry, config: &Config) -> String {
|
|||
escape_html(&entry.meta.name)
|
||||
};
|
||||
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
|
||||
);
|
||||
if config.enable_website_links && !entry.meta.website.is_empty() {
|
||||
|
|
@ -281,7 +280,7 @@ fn render_entry(entry: &Entry, config: &Config) -> String {
|
|||
website, website
|
||||
));
|
||||
}
|
||||
header.push_str("</span>");
|
||||
header.push_str("</header>");
|
||||
let body = if config.enable_html_injection {
|
||||
entry.body.clone()
|
||||
} else {
|
||||
|
|
@ -305,8 +304,7 @@ fn render_entry(entry: &Entry, config: &Config) -> String {
|
|||
String::new()
|
||||
};
|
||||
format!(
|
||||
"\n{header}\n{drawing_html}{voice_note_html}\n<span class=\"entry-body\">{body}</span>\n\n<span class=\"entry-separator\">{}</span>\n",
|
||||
config.separator
|
||||
"<article class=\"entry\">{header}{drawing_html}{voice_note_html}<div class=\"entry-body\">{body}</div></article>"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -345,7 +343,6 @@ mod tests {
|
|||
voice_note_max_duration: 20,
|
||||
template: None,
|
||||
success_template: None,
|
||||
separator: "---".into(),
|
||||
style: String::new(),
|
||||
form_prompt: "Thanks for visiting. Sign the guestbook!".into(),
|
||||
button_text: "sign".into(),
|
||||
|
|
@ -400,7 +397,7 @@ mod tests {
|
|||
assert!(html.contains("entry-header"));
|
||||
assert!(html.contains("entry-name"));
|
||||
assert!(html.contains("entry-body"));
|
||||
assert!(html.contains("entry-separator"));
|
||||
assert!(html.contains("<article class=\"entry\">"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -368,7 +368,6 @@ mod tests {
|
|||
voice_note_max_duration: 20,
|
||||
template: None,
|
||||
success_template: None,
|
||||
separator: "---".into(),
|
||||
style: String::new(),
|
||||
form_prompt: "Thanks for visiting. Sign the guestbook!".into(),
|
||||
button_text: "sign".into(),
|
||||
|
|
|
|||
|
|
@ -3,22 +3,32 @@
|
|||
max-width: 70ch;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Form */
|
||||
.guestbook-prompt {}
|
||||
.guestbook-prompt {
|
||||
display: block;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.guestbook-form {}
|
||||
.guestbook-label {}
|
||||
.guestbook-input {}
|
||||
.guestbook-label {
|
||||
display: block;
|
||||
}
|
||||
.guestbook-input {
|
||||
display: block;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.guestbook-textarea {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.guestbook-button {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
/* Drawings */
|
||||
|
|
@ -35,12 +45,19 @@
|
|||
.guestbook-canvas-tools a {
|
||||
cursor: pointer;
|
||||
}
|
||||
.guestbook-drawing-wrap {
|
||||
display: block;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.guestbook-drawing-inline a {
|
||||
cursor: pointer;
|
||||
}
|
||||
.guestbook-drawing-content:empty {
|
||||
display: none;
|
||||
}
|
||||
.guestbook-drawing-content {
|
||||
display: block;
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.guestbook-swatch {
|
||||
display: inline-block;
|
||||
|
|
@ -65,6 +82,10 @@
|
|||
}
|
||||
|
||||
/* Voice notes */
|
||||
.guestbook-voice-wrap {
|
||||
display: block;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.guestbook-voice-record.recording {
|
||||
color: red;
|
||||
}
|
||||
|
|
@ -80,14 +101,25 @@
|
|||
}
|
||||
audio {
|
||||
display: block;
|
||||
margin-top: 0.6em;
|
||||
margin-bottom: 0.3em;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
/* 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-name {}
|
||||
.entry-website {}
|
||||
.entry-body {}
|
||||
.entry-separator {}
|
||||
.entry-body {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,8 @@
|
|||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
Thanks! Your message is pending approval.
|
||||
|
||||
<a href="/">← back</a>
|
||||
<p>Thanks! Your message is pending approval.</p>
|
||||
<p><a href="/">← back</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue