diff --git a/.env.example b/.env.example
index 48f9687..2f47748 100644
--- a/.env.example
+++ b/.env.example
@@ -78,11 +78,11 @@
# Label for the message field.
# BOOK_LABEL_MESSAGE=Your message:
-# Number of rows for the message textarea.
-# BOOK_TEXTAREA_ROWS=8
+# Message textarea width in pixels.
+# BOOK_TEXTAREA_WIDTH=400
-# Number of columns for the message textarea.
-# BOOK_TEXTAREA_COLS=60
+# Message textarea height in pixels.
+# BOOK_TEXTAREA_HEIGHT=150
# Custom HTML template file with {{title}}, {{form}}, {{entries}}, and {{style}} placeholders.
# Uses built-in default if unset.
diff --git a/README.md b/README.md
index 25f440b..48d1af0 100644
--- a/README.md
+++ b/README.md
@@ -167,11 +167,11 @@ Running `guestbook` with no env vars will give you a working guestbook on `local
# Label for the message field.
# BOOK_LABEL_MESSAGE=Your message:
-# Number of rows for the message textarea.
-# BOOK_TEXTAREA_ROWS=8
+# Message textarea width in pixels.
+# BOOK_TEXTAREA_WIDTH=400
-# Number of columns for the message textarea.
-# BOOK_TEXTAREA_COLS=60
+# Message textarea height in pixels.
+# BOOK_TEXTAREA_HEIGHT=150
# Custom HTML template file with {{title}}, {{form}}, {{entries}}, and {{style}} placeholders.
# Uses built-in default if unset.
@@ -235,8 +235,8 @@ services.guestbook = {
message = "Your message:";
};
message = {
- rows = 8;
- cols = 60;
+ width = 400;
+ height = 150;
};
};
};
@@ -286,8 +286,8 @@ The `status` field can be `pending`, `approved`, or `denied`. Only approved entr
title - Site title (BOOK_SITE_TITLE). Useful in
and headings.
form - The submission form (labels, inputs, button). Controlled by
BOOK_FORM_PROMPT, BOOK_LABEL_NAME, BOOK_LABEL_WEBSITE,
- BOOK_LABEL_MESSAGE, BOOK_BUTTON_TEXT, BOOK_TEXTAREA_ROWS,
- BOOK_TEXTAREA_COLS. Empty when BOOK_ENABLE_SUBMISSIONS=false.
+ BOOK_LABEL_MESSAGE, BOOK_BUTTON_TEXT, BOOK_TEXTAREA_WIDTH,
+ BOOK_TEXTAREA_HEIGHT. Empty when BOOK_ENABLE_SUBMISSIONS=false.
entries - Approved guestbook entries, newest first. Entry separator
controlled by BOOK_SEPARATOR.
style - Custom CSS from BOOK_STYLE or BOOK_STYLE_FILE, wrapped in
@@ -340,6 +340,16 @@ entries
.guestbook-textarea {}
.guestbook-button {}
+/* Drawings */
+.guestbook-canvas {
+ border: 1px solid #000;
+ cursor: crosshair;
+}
+.guestbook-canvas-reset {}
+.entry-drawing {
+ max-width: 100%;
+}
+
/* Entries */
.entry-header {}
.entry-name {}
diff --git a/module.nix b/module.nix
index 09647c3..c6200c3 100644
--- a/module.nix
+++ b/module.nix
@@ -203,16 +203,16 @@ in
};
message = {
- rows = mkOption {
+ width = mkOption {
type = types.int;
- default = 8;
- description = "Number of rows for the message textarea.";
+ default = 400;
+ description = "Message textarea width in pixels.";
};
- cols = mkOption {
+ height = mkOption {
type = types.int;
- default = 60;
- description = "Number of columns for the message textarea.";
+ default = 150;
+ description = "Message textarea height in pixels.";
};
};
};
@@ -249,8 +249,8 @@ in
BOOK_LABEL_NAME = cfg.styles.labels.name;
BOOK_LABEL_WEBSITE = cfg.styles.labels.website;
BOOK_LABEL_MESSAGE = cfg.styles.labels.message;
- BOOK_TEXTAREA_ROWS = toString cfg.styles.message.rows;
- BOOK_TEXTAREA_COLS = toString cfg.styles.message.cols;
+ BOOK_TEXTAREA_WIDTH = toString cfg.styles.message.width;
+ BOOK_TEXTAREA_HEIGHT = toString cfg.styles.message.height;
} // lib.optionalAttrs (cfg.styles.cssFile != null) {
BOOK_STYLE_FILE = cfg.styles.cssFile;
} // lib.optionalAttrs (cfg.styles.templateFile != null) {
diff --git a/src/config.rs b/src/config.rs
index 6806929..6633961 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -33,8 +33,8 @@ pub struct Config {
pub label_name: String,
pub label_website: String,
pub label_message: String,
- pub textarea_rows: u32,
- pub textarea_cols: u32,
+ pub textarea_width: u32,
+ pub textarea_height: u32,
}
impl Config {
@@ -138,14 +138,14 @@ impl Config {
.unwrap_or_else(|_| "Your website (optional):".into()),
label_message: env::var("BOOK_LABEL_MESSAGE")
.unwrap_or_else(|_| "Your message:".into()),
- textarea_rows: env::var("BOOK_TEXTAREA_ROWS")
- .unwrap_or_else(|_| "8".into())
+ textarea_width: env::var("BOOK_TEXTAREA_WIDTH")
+ .unwrap_or_else(|_| "400".into())
.parse()
- .map_err(|_| "BOOK_TEXTAREA_ROWS must be a number")?,
- textarea_cols: env::var("BOOK_TEXTAREA_COLS")
- .unwrap_or_else(|_| "60".into())
+ .map_err(|_| "BOOK_TEXTAREA_WIDTH must be a number")?,
+ textarea_height: env::var("BOOK_TEXTAREA_HEIGHT")
+ .unwrap_or_else(|_| "150".into())
.parse()
- .map_err(|_| "BOOK_TEXTAREA_COLS must be a number")?,
+ .map_err(|_| "BOOK_TEXTAREA_HEIGHT must be a number")?,
})
}
}
diff --git a/src/render.rs b/src/render.rs
index 338f756..07ab1b4 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -40,8 +40,7 @@ pub fn render_form(config: &Config) -> String {
let drawing_section = if config.enable_drawings {
format!(
- r##"
-{label}
+ r##"{label}
Reset
@@ -87,7 +86,7 @@ pub fn render_form(config: &Config) -> String {
{website_section}
{label_message}
-
+
{captcha_section}
{drawing_section}
@@ -97,8 +96,8 @@ pub fn render_form(config: &Config) -> String {
label_name = config.label_name,
website_section = website_section,
label_message = config.label_message,
- rows = config.textarea_rows,
- cols = config.textarea_cols,
+ tw = config.textarea_width,
+ th = config.textarea_height,
captcha_section = captcha_section,
drawing_section = drawing_section,
button = config.button_text,
@@ -196,8 +195,8 @@ mod tests {
label_name: "Your name:".into(),
label_website: "Your website (optional):".into(),
label_message: "Your message:".into(),
- textarea_rows: 8,
- textarea_cols: 60,
+ textarea_width: 400,
+ textarea_height: 150,
}
}
@@ -298,11 +297,11 @@ mod tests {
#[test]
fn test_render_form_custom_textarea() {
let mut config = test_config();
- config.textarea_rows = 12;
- config.textarea_cols = 40;
+ config.textarea_width = 500;
+ config.textarea_height = 200;
let form = render_form(&config);
- assert!(form.contains("rows=\"12\""));
- assert!(form.contains("cols=\"40\""));
+ assert!(form.contains("width:500px"));
+ assert!(form.contains("height:200px"));
}
#[test]
@@ -424,6 +423,6 @@ mod tests {
let entry = make_entry("alice", "2026-04-09", "Hello!");
let form = render_form(&config);
let html = render_page(DEFAULT_TEMPLATE, &config, &[entry], &form);
- assert!(!html.contains("entry-drawing"));
+ assert!(!html.contains("