summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob L. Kreuze <zerodaysfordays@sdf.org>2026-02-16 12:26:20 -0500
committerJakob L. Kreuze <zerodaysfordays@sdf.org>2026-02-16 12:26:20 -0500
commitc00ace03d64001c6fb49d7ba7e25d75386eb6ae9 (patch)
treec0a0773eefd1a53b0f8c85dc530812ade19c561f
parentc7cdffa549be231fff7a8a38dbc7caaadd9663a3 (diff)
Fix comment form
-rw-r--r--jakob/builder/blog.scm21
-rw-r--r--jakob/dynamic/capabilities/comment-form.scm111
-rw-r--r--pages/changelog.org5
-rw-r--r--static/css/style.css65
-rw-r--r--static/js/dynamic-comment-form.js25
-rw-r--r--static/js/proof-of-work.js7
6 files changed, 141 insertions, 93 deletions
diff --git a/jakob/builder/blog.scm b/jakob/builder/blog.scm
index bb5567c..00b7ddc 100644
--- a/jakob/builder/blog.scm
+++ b/jakob/builder/blog.scm
@@ -102,7 +102,6 @@
,(first-paragraph post)
(p ,(hyperlink (or crosspost-uri local-uri) "read more →")))))
-;; TODO: Should dynamically pull comments.
(define (comments-section post)
`(section (@ (class "comments-section")
(aria-labelledby "comments-heading"))
@@ -110,17 +109,11 @@
(fetch-webmentions (post-identifier post)))
(div (@ (class "comment-form-module"))
(h4 (@ (class "module-title"))
- "Leave a Comment")
- (form (@ (class "comment-form"))
- (div (@ (class "form-row"))
- (input (@ (type "text") (placeholder "Name") (required "") (aria-label "Name")))
- (input (@ (type "text") (placeholder "Subject (Optional)") (aria-label "Subject"))))
- (div (@ (class "form-row"))
- (input (@ (type "url") (placeholder "Website (https://...)") (aria-label "Website URL"))))
- (textarea (@ (placeholder "Write your comment here...")
- (rows "4")
- (required "")
- (aria-label "Comment body")))))))
+ "Leave a Comment")
+ (noscript
+ (p (@ (style "font-size: 10px; color: #666; text-align: center; padding: 10px;"))
+ "Click " ,(hyperlink (build-comment-url post) "here") " to write a comment on this post."))
+ ,(render-dynamic-comment-form (post-slug post)))))
;;;
@@ -154,8 +147,8 @@
#:scripts (if (not (eof-object? scripts)) scripts '())
#:content `(,(render-article post)
,(comments-section post)
- ,(script "section-folds.js")
- ,(script "comment-reaction.js")))
+ ,(script "comment-reaction.js")
+ ,(script "section-folds.js")))
sxml->html))
diff --git a/jakob/dynamic/capabilities/comment-form.scm b/jakob/dynamic/capabilities/comment-form.scm
index 958e9dc..5ce4008 100644
--- a/jakob/dynamic/capabilities/comment-form.scm
+++ b/jakob/dynamic/capabilities/comment-form.scm
@@ -34,40 +34,57 @@
get-comment-form))
(define (render-comment-field)
- `(fieldset (@ (id "comment-content"))
- (legend "Comment")
- (label (@ (for "name") (class "required")) "Name:")
- (input (@ (type "text") (id "name") (name "name") (required #t)))
- (label (@ (for "email")) "Email:")
- (input (@ (type "text") (id "email") (name "email")))
- (label (@ (for "url")) "Webpage URL:")
- (input (@ (type "text") (id "url") (name "url")))
- (label (@ (for "subject")) "Subject:")
- (input (@ (type "text") (id "subject") (name "subject")))
- (label (@ (for "comment") (class "required")) "Comment :")
- (textarea (@ (id "coment") (name "comment")))
- (p "(*) Indicates a required field.")))
+ `((div (@ (class "form-row"))
+ (input (@ (type "text")
+ (id "name")
+ (name "name")
+ (required #t)
+ (aria-label "Name")
+ (placeholder "Name")))
+ (input (@ (type "text")
+ (id "subject")
+ (name "subject")
+ (aria-label "Subject")
+ (placeholder "Subject (Optional)"))))
+ (div (@ (class "form-row"))
+ (input (@ (type "text")
+ (id "url")
+ (name "url")
+ (aria-label "Website URL")
+ (placeholder "Website (https://...)"))))
+ (textarea (@ (id "coment")
+ (name "comment")
+ (placeholder "Write your comment here...")
+ (rows "4")
+ (required "")
+ (aria-label "Comment body")))))
(define* (render-comment-captcha-field #:optional (captcha-id "") captcha-image
#:key hidden)
- `(fieldset ,(if hidden
- '(@ (id "comment-captcha") (hidden "#t"))
- '(@ (id "comment-captcha")))
- (legend "Captcha")
- (div (@ (id "captcha-challenge-primary"))
- (label (@ (for "captcha")) "Please evaluate the following definite integral:")
- (img (@ (id "captcha-image")
- (src ,(if captcha-image
- (format #f "data:image/jpeg;charset=utf-8;base64,~a"
- (base64-encode captcha-image))
- ""))))
- (input (@ (type "text") (id "captcha") (name "captcha") (size 24))))
- (button (@ (id "pow-trigger") (hidden #t))
- "Too hard? (Or unable to see the challenge?) Click here.")
- (input (@ (autocomplete "off") (type "text") (id "captcha-id") (name "captcha-id") (hidden #t) (value ,captcha-id)))
- (input (@ (autocomplete "off") (type "text") (id "captcha-alt") (name "captcha-alt") (hidden #t)))
- (input (@ (autocomplete "off") (type "text") (id "captcha-alt-id") (name "captcha-alt-id") (hidden #t)))
- (input (@ (type "submit") (id "submit-form") (value "Submit")))))
+ `((div (@ (id "captcha-container"))
+ (div (@ (id "captcha-image-wrapper")))
+ (div (@ (id "captcha-input-wrapper"))
+ (input (@ (type "text")
+ (id "captcha-solution")
+ (name "captcha")
+ (required "true")))
+ (button (@ (id "captcha-challenge-trigger")
+ (type "button"))
+ "Fetch Captcha"))
+ (input (@ (autocomplete "off") (type "text") (hidden #t)
+ (id "captcha-id") (name "captcha-id") (value ,captcha-id)))
+ (input (@ (autocomplete "off") (type "text") (hidden #t)
+ (id "captcha-alt") (name "captcha-alt")))
+ (input (@ (autocomplete "off") (type "text") (hidden #t)
+ (id "captcha-alt-id") (name "captcha-alt-id")))
+ (div (@ (id "captcha-alternative-wrapper")
+ (hidden "true"))
+ (p "If you can't solve this integral, click this button to run an
+alternative proof-of-work captcha.")
+ (button (@ (type "button")
+ (id "proof-of-work-trigger")
+ (aria-label "Automatic Captcha Button"))
+ "Calculate Proof-of-Work For Me")))))
(define (render-static-comment-form slug captcha-id captcha-image)
`(div (@ (id "comment-form"))
@@ -79,27 +96,17 @@
,(script "proof-of-work.js")))
(define (render-dynamic-comment-form slug)
- `(div (@ (id "comment-form"))
- (h3 (@ (id "comment-form-header")) "Comment form")
- (form (@ (id "comment-input") (action "/api/comment") (method "post"))
- (input (@ (autocomplete "off")
- (type "text")
- (name "slug")
- (hidden #t)
- (value ,slug)))
- (input (@ (autocomplete "off")
- (type "text")
- (name "reply-to")
- (id "reply-to")
- (hidden #t)
- (value "")))
- ,(render-comment-field)
- (fieldset (@ (id "captcha-trigger-block"))
- (legend "Captcha")
- (label "You need to complete a captcha to write a comment.")
- (button (@ (id "captcha-challenge-trigger"))
- "Click here to generate a captcha challenge"))
- ,(render-comment-captcha-field #:hidden #t))
+ `(form (@ (id "comment-form") (action "/api/comment")
+ (method "post") (hidden "true"))
+ ;; Hidden fields.
+ (input (@ (autocomplete "off") (type "text") (hidden #t)
+ (name "slug") (value ,slug)))
+ (input (@ (autocomplete "off") (type "text") (hidden #t)
+ (name "reply-to") (id "reply-to") (value "")))
+ ,@(render-comment-field)
+ ,@(render-comment-captcha-field)
+ (button (@ (type "submit") (style "margin-top: 10px;"))
+ "Submit Comment")
,(script "dynamic-comment-form.js")
,(script "proof-of-work.js")))
diff --git a/pages/changelog.org b/pages/changelog.org
index 6a779da..cb15388 100644
--- a/pages/changelog.org
+++ b/pages/changelog.org
@@ -10,11 +10,6 @@ A record of any notable user-facing changes made to this website. For more detai
- Comments
- Comment replies
- Comment reactions
-- The following features are planned for this new theme:
- - [[https://danilafe.com/blog/blog_microfeatures/][Grouping series of posts]]
- - [[https://danilafe.com/blog/blog_microfeatures/][Origin for code blocks]]
- - [[https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images][Responsive images]]
- - Feed for new comments
** Wednesday, August 20, 2025
diff --git a/static/css/style.css b/static/css/style.css
index 3328f8c..442af67 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -278,7 +278,7 @@ a:hover {
border-color: #2e8b57
}
-.comment-form textarea,.form-row input,.search-module input {
+#comment-form textarea,.form-row input,.search-module input {
flex: 1;
font-size: 9px;
border: 1px solid #ccc;
@@ -286,7 +286,7 @@ a:hover {
font-family: inherit
}
-.comment-form button,.search-module button {
+#comment-form button,.search-module button {
text-transform: uppercase;
font-size: 9px;
cursor: pointer
@@ -392,20 +392,21 @@ hr {
border: 1px solid #ccc
}
-.comment-form {
+#comment-form {
+ display: flex;
flex-direction: column
}
-.comment-form,.form-row,.legacy-warning {
+#comment-form,.form-row,.legacy-warning {
display: flex;
gap: 8px
}
-.comment-form textarea,.form-row input {
+#comment-form textarea,.form-row input {
padding: 5px
}
-.comment-form button {
+#comment-form button {
align-self: flex-start;
background: #333;
color: #fff;
@@ -413,10 +414,60 @@ hr {
padding: 5px 15px
}
-.comment-form button:hover {
+#comment-form button:hover {
background: #2e8b57
}
+#captcha-image-wrapper img {
+ border: 1px solid #ccc;
+ background: #fff;
+ display: block;
+ max-width: 100%
+}
+
+#captcha-input-wrapper {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ gap: 8px;
+ margin-top: 12px
+}
+
+#captcha-input-wrapper input {
+ flex: 1;
+ font-size: 9px;
+ border: 1px solid #ccc;
+ padding: 5px
+}
+
+#captcha-challenge-trigger {
+ height: 23px;
+ padding: 0 10px;
+ font-size: 9px;
+ text-transform: uppercase;
+ cursor: pointer;
+ background: #eee;
+ border: 1px solid #ccc
+}
+
+#captcha-alternative-wrapper {
+ align-items: center;
+ gap: 10px;
+}
+
+#captcha-alternative-wrapper button {
+ background: #eee;
+ border: 1px solid #ccc;
+ color: #333;
+ font-size: 9px
+}
+
+#captcha-alternative-wrapper p {
+ font-size: 9px;
+ margin: 0;
+ color: #999
+}
+
.legacy-warning {
background: linear-gradient(to bottom,#fff3e0 0,#ffe0b2 100%);
border: 1px dotted #fb8c00;
diff --git a/static/js/dynamic-comment-form.js b/static/js/dynamic-comment-form.js
index ff5f875..809a755 100644
--- a/static/js/dynamic-comment-form.js
+++ b/static/js/dynamic-comment-form.js
@@ -42,19 +42,15 @@ function makeRequest (method, url) {
}
window.addEventListener("load", () => {
- let primaryDisplay = document.getElementById("comment-form-primary");
+ let primaryDisplay = document.getElementById("comment-form");
primaryDisplay.removeAttribute("hidden");
- let altDisplay = document.getElementById("comment-form-alt");
- altDisplay.remove();
+ const altDisplay = document.getElementById("captcha-alternative-wrapper");
let trigger = document.getElementById("captcha-challenge-trigger");
- let triggerBlock = document.getElementById("captcha-trigger-block");
trigger.addEventListener("click", (event) => {
- let captchaField = document.getElementById("comment-captcha");
-
let captchaId = document.getElementById("captcha-id");
- let captchaImage = document.getElementById("captcha-image");
+ let wrapper = document.getElementById("captcha-image-wrapper");
trigger.innerHTML = "Please wait...";
trigger.disabled = true;
@@ -65,13 +61,17 @@ window.addEventListener("load", () => {
// Update form with parsed values.
let challengeData = JSON.parse(data);
captchaId.value = challengeData["challenge-id"];
- captchaImage.src = challengeData["image"];
- // Unhide fieldset.
- captchaField.removeAttribute("hidden");
+ const img = document.createElement('img');
+ img.src = challengeData["image"];
+ // img.src = `data:image/png;base64,${base64Data}`;
+ wrapper.innerHTML = '';
+ wrapper.appendChild(img);
+
+ trigger.hidden = true;
- // Hide initial trigger.
- triggerBlock.remove();
+ // Unhide fieldset.
+ altDisplay.removeAttribute("hidden");
})
.catch(console.err);
}
@@ -80,7 +80,6 @@ window.addEventListener("load", () => {
});
});
-
window.addEventListener("load", () => {
// Unhide additional comment actions if Javascript is enabled.
let additionalActionsRows = document.querySelectorAll(".comment-additional-actions");
diff --git a/static/js/proof-of-work.js b/static/js/proof-of-work.js
index b4b5c57..f2f0484 100644
--- a/static/js/proof-of-work.js
+++ b/static/js/proof-of-work.js
@@ -81,7 +81,7 @@ function raceEndpoint() {
}
window.addEventListener("load", () => {
- let trigger = document.getElementById("pow-trigger");
+ let trigger = document.getElementById("proof-of-work-trigger");
trigger.removeAttribute("hidden");
trigger.addEventListener("click", (e) => {
trigger.innerHTML = "Please wait...";
@@ -96,8 +96,11 @@ window.addEventListener("load", () => {
trigger.disabled = true;
// Hide the primary captcha, too, to make it clear that it isn't necessary to complete.
- let primaryChallenge = document.getElementById("captcha-challenge-primary");
+ let primaryChallenge = document.getElementById("captcha-image-wrapper");
primaryChallenge.hidden = true;
+ let solutionInput = document.getElementById("captcha-solution");
+ solutionInput.hidden = true;
+ solutionInput.removeAttribute("required");
})
.catch((err) => {
console.log(err);