This commit is contained in:
452
assets/content/comment-pages.json
Normal file
452
assets/content/comment-pages.json
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"slug": "benefits-of-reading",
|
||||||
|
"title": "Benefits of Reading",
|
||||||
|
"url": "/blogs/2025/08-august/benefits-of-reading.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "hilberts-hotel",
|
||||||
|
"title": "Hilbert's Hotel",
|
||||||
|
"url": "/blogs/2025/08-august/hilberts.hotel.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "spending-all-day-on-this-website",
|
||||||
|
"title": "09-08-2025: Website Changes",
|
||||||
|
"url": "/blogs/2025/08-august/spending-the-whole-day-on-this-website.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "third-time",
|
||||||
|
"title": "Third Time",
|
||||||
|
"url": "/blogs/2025/08-august/third-time.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "wacom-with-arch",
|
||||||
|
"title": "Wacom With Arch",
|
||||||
|
"url": "/blogs/2025/08-august/wacom-with-arch.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "what-i-want-to-do-with-emacs",
|
||||||
|
"title": "What Do I Want To Do With Emacs",
|
||||||
|
"url": "/blogs/2025/08-august/what-do-i-want-to-do-with-emacs.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "zettlekasten",
|
||||||
|
"title": "Zettelkasten Method",
|
||||||
|
"url": "/blogs/2025/08-august/zettelkasten.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "02-11-25-week-review",
|
||||||
|
"title": "[02-11-2025] - Weekly Review",
|
||||||
|
"url": "/blogs/2025/11-november/02-11-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "09-11-25-week-review",
|
||||||
|
"title": "[09-11-2025] - Weekly Review",
|
||||||
|
"url": "/blogs/2025/11-november/09-11-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "16-11-25-week-review",
|
||||||
|
"title": "[16-11-2025] - Weekly Review",
|
||||||
|
"url": "/blogs/2025/11-november/16-11-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "23-11-25-week-review",
|
||||||
|
"title": "[23-11-2025] - Weekly Review",
|
||||||
|
"url": "/blogs/2025/11-november/23-11-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "30-11-25-week-review",
|
||||||
|
"title": "[30-11-2025] - Weekly Review",
|
||||||
|
"url": "/blogs/2025/11-november/30-11-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "07-12-25-week-review",
|
||||||
|
"title": "[07-12-2025] - Weekly Review",
|
||||||
|
"url": "/blogs/2025/12-december/07-12-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "14-12-25-week-review",
|
||||||
|
"title": "[14-12-2025] - Weekly Review",
|
||||||
|
"url": "/blogs/2025/12-december/14-12-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "21-12-25-week-review",
|
||||||
|
"title": "[21-12-2025] - Weekly Review",
|
||||||
|
"url": "/blogs/2025/12-december/21-12-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "28-12-25-week-review",
|
||||||
|
"title": "[28-12-2025] - Weekly Review",
|
||||||
|
"url": "/blogs/2025/12-december/28-12-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "04-01-26-week-review",
|
||||||
|
"title": "[04-01-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/01-january/04-01-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "11-01-26-week-review",
|
||||||
|
"title": "[11-01-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/01-january/11-01-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "18-01-26-week-review",
|
||||||
|
"title": "[18-01-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/01-january/18-01-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "25-01-26-week-review",
|
||||||
|
"title": "[25-01-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/01-january/25-01-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "01-02-26-week-review",
|
||||||
|
"title": "[01-02-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/02-february/01-02-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "08-02-26-week-review",
|
||||||
|
"title": "[08-02-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/02-february/08-02-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "15-02-26-week-review",
|
||||||
|
"title": "[15-02-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/02-february/15-02-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "22-02-26-week-review",
|
||||||
|
"title": "[22-02-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/02-february/22-02-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "24-02-26",
|
||||||
|
"title": "Integration tests failing (sob)",
|
||||||
|
"url": "/blogs/2026/02-february/24-02-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "26-02-26",
|
||||||
|
"title": "Starting the Journeys Upgrade",
|
||||||
|
"url": "/blogs/2026/02-february/26-02-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "27-02-26",
|
||||||
|
"title": "Journeys rambles again...",
|
||||||
|
"url": "/blogs/2026/02-february/27-02-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "third-meeting-with-lima",
|
||||||
|
"title": "Third Meeting with lima :)",
|
||||||
|
"url": "/blogs/2026/02-february/third-meeting.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "01-03-26-week-review",
|
||||||
|
"title": "[01-03-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/03-march/01-03-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "08-03-26-week-review",
|
||||||
|
"title": "[08-03-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/03-march/08-03-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "15-03-26-week-review",
|
||||||
|
"title": "[15-03-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/03-march/15-03-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "22-03-26-week-review",
|
||||||
|
"title": "[22-03-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/03-march/22-03-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "29-03-26-week-review",
|
||||||
|
"title": "[29-03-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/03-march/29-03-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "cooking-dinner-29-03-26",
|
||||||
|
"title": "Cooking Dinner (num)",
|
||||||
|
"url": "/blogs/2026/03-march/cooking-dinner-29-03-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "feeling-sleepy",
|
||||||
|
"title": "Feeling extremely sleepy",
|
||||||
|
"url": "/blogs/2026/03-march/feeling-sleepy.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "fixing-the-dag-18-03",
|
||||||
|
"title": "DAG fixes",
|
||||||
|
"url": "/blogs/2026/03-march/fixing-the-dag-18-03.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "installing-emacs-23-03",
|
||||||
|
"title": "Installing Emacs on Windows",
|
||||||
|
"url": "/blogs/2026/03-march/installing-emacs-23-03.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "intellectually-challenging-myself-30-03-26",
|
||||||
|
"title": "Intellectually challenging oneself",
|
||||||
|
"url": "/blogs/2026/03-march/intellectually-challenging-myself-30-03-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "more-thoughts-regarding-changes-26-03-26",
|
||||||
|
"title": "More thoughts on schedule changes",
|
||||||
|
"url": "/blogs/2026/03-march/more-thoughts-regarding-changes-26-03-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "oversleeping-16-03",
|
||||||
|
"title": "Oversleeping and missing a meeting...",
|
||||||
|
"url": "/blogs/2026/03-march/oversleeping-16-03.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "quick-lunch-24-03",
|
||||||
|
"title": "Making Lunch QUiCk!!",
|
||||||
|
"url": "/blogs/2026/03-march/quick-lunch-24-03.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "setting-a-wird-tracker-19-03",
|
||||||
|
"title": "Setting a Wird Tracker",
|
||||||
|
"url": "/blogs/2026/03-march/setting-a-wird-tracker-19-03.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "thoughts-on-life-25-03-26",
|
||||||
|
"title": "Thoughts on life...",
|
||||||
|
"url": "/blogs/2026/03-march/thoughts-on-life-25-03-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "training-on-friday-afternoon-27-03-26",
|
||||||
|
"title": "Training time on Friday afternoon",
|
||||||
|
"url": "/blogs/2026/03-march/training-on-friday-afternoon-27-03-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "05-04-26-week-review",
|
||||||
|
"title": "[05-04-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/04-april/05-04-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "12-04-26-week-review",
|
||||||
|
"title": "[12-04-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/04-april/12-04-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "16-04-26",
|
||||||
|
"title": "Rambles",
|
||||||
|
"url": "/blogs/2026/04-april/16-04-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "19-04-26-week-review",
|
||||||
|
"title": "[19-04-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/04-april/19-04-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "26-04-26-week-review",
|
||||||
|
"title": "[26-04-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/04-april/26-04-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "action-plan-07-04-26",
|
||||||
|
"title": "Action plan to change teams",
|
||||||
|
"url": "/blogs/2026/04-april/action-plan-07-04-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "adding-more-wird-22-04-26",
|
||||||
|
"title": "Adding another Wird",
|
||||||
|
"url": "/blogs/2026/04-april/adding-more-wird-22-04-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "april-almost-over-28-04",
|
||||||
|
"title": "April is almost over...",
|
||||||
|
"url": "/blogs/2026/04-april/april-almost-over-28-04.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "comparison-14-04",
|
||||||
|
"title": "Comparison is the thief of joy",
|
||||||
|
"url": "/blogs/2026/04-april/comparison-14-04.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "ending-the-week-17-04-26",
|
||||||
|
"title": "End of week thoughts",
|
||||||
|
"url": "/blogs/2026/04-april/ending-the-week-17-04-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "joining-new-team-meeting-02-04-2026",
|
||||||
|
"title": "New team meeting",
|
||||||
|
"url": "/blogs/2026/04-april/joining-new-team-meeting-02-04-2026.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "making-notes-through-org-noter-06-05",
|
||||||
|
"title": "Making Notes through Org Noter",
|
||||||
|
"url": "/blogs/2026/04-april/making-notes-through-org-noter-06-05.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "sitting-outside-in-the-sun-21-04-26",
|
||||||
|
"title": "Sitting in the sun",
|
||||||
|
"url": "/blogs/2026/04-april/sitting-outside-in-the-sun-21-04-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "starting-new-rotation-08-04-26",
|
||||||
|
"title": "Starting new rotation",
|
||||||
|
"url": "/blogs/2026/04-april/starting-new-rotation-08-04-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "weekly-target-06-04-26",
|
||||||
|
"title": "[06-04-2026] - Weekly target",
|
||||||
|
"url": "/blogs/2026/04-april/weekly-target-06-04-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "wise-words-29-04",
|
||||||
|
"title": "Wise Words I need to engrain",
|
||||||
|
"url": "/blogs/2026/04-april/wise-words-29-04.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "03-05-26-week-review",
|
||||||
|
"title": "[03-05-2026] - Weekly Review",
|
||||||
|
"url": "/blogs/2026/05-may/03-05-week-review.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "ai-datacamp-08-05",
|
||||||
|
"title": "AI Datacamp",
|
||||||
|
"url": "/blogs/2026/05-may/ai-datacamp-08-05.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "authoring-service-09-05-26",
|
||||||
|
"title": "Authoring Service",
|
||||||
|
"url": "/blogs/2026/05-may/authoring-service-09-05-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "sun-soak-01-05",
|
||||||
|
"title": "Soaking in the sun",
|
||||||
|
"url": "/blogs/2026/05-may/sun-soak-01-05.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "using-codex-07-05-26",
|
||||||
|
"title": "Using Codex",
|
||||||
|
"url": "/blogs/2026/05-may/using-codex-07-05-26.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "publish-pages",
|
||||||
|
"title": "How to publish pages using Org Publish",
|
||||||
|
"url": "/blogs/publish-pages.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "setup",
|
||||||
|
"title": "Setup",
|
||||||
|
"url": "/home/guide/setup.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "backlog",
|
||||||
|
"title": "Backlog",
|
||||||
|
"url": "/home/backlog.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "wedding-countdown",
|
||||||
|
"title": "Countdown",
|
||||||
|
"url": "/home/countdown.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "notes",
|
||||||
|
"title": "Notes",
|
||||||
|
"url": "/home/notes.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "services",
|
||||||
|
"title": "Service",
|
||||||
|
"url": "/home/services.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "competency-board",
|
||||||
|
"title": "Competency Status Board",
|
||||||
|
"url": "/home/status.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "airflow",
|
||||||
|
"title": "Datamarts, Airflow and DAG's",
|
||||||
|
"url": "/posts/career/airflow.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "career-intro",
|
||||||
|
"title": "Career Introduction",
|
||||||
|
"url": "/posts/career/career-intro.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "database-permissions",
|
||||||
|
"title": "Database Permissions, Roles, and Accounts",
|
||||||
|
"url": "/posts/career/database-permissions.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "high-availability-disaster-recovery",
|
||||||
|
"title": "High Availability, Disaster Recovery and Business Continuity",
|
||||||
|
"url": "/posts/career/ha-dr.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "invest-principles",
|
||||||
|
"title": "Invest Principles",
|
||||||
|
"url": "/posts/career/invest-principles.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "understands-the-javascript-language",
|
||||||
|
"title": "Understands the Javascript language",
|
||||||
|
"url": "/posts/career/javascript.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "lean",
|
||||||
|
"title": "Lean",
|
||||||
|
"url": "/posts/career/lean.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "management-of-self",
|
||||||
|
"title": "Management of self training",
|
||||||
|
"url": "/posts/career/management-of-self.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "monitoring-and-logging",
|
||||||
|
"title": "Monitoring and Logging",
|
||||||
|
"url": "/posts/career/monitoring-and-logging.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "normalisation",
|
||||||
|
"title": "Benefits of Normalisation",
|
||||||
|
"url": "/posts/career/normalisation.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "owasp-top-10",
|
||||||
|
"title": "OWASP Top Ten",
|
||||||
|
"url": "/posts/career/owasp.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "pipelines-learning",
|
||||||
|
"title": "Pipelines and how they work (as well as CI/CD)",
|
||||||
|
"url": "/posts/career/pipelines.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "probation-objectives",
|
||||||
|
"title": "Probation Objectives:",
|
||||||
|
"url": "/posts/career/probation-objectives.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "requirements-features",
|
||||||
|
"title": "Requirements, features, user stories, tasks, walking skeletons",
|
||||||
|
"url": "/posts/career/requirements-features.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "restful-api",
|
||||||
|
"title": "Restful API",
|
||||||
|
"url": "/posts/career/restful-api.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "retrospectives",
|
||||||
|
"title": "Retrospectives",
|
||||||
|
"url": "/posts/career/retrospectives.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "solid-principles",
|
||||||
|
"title": "SOLID Principles",
|
||||||
|
"url": "/posts/career/solid-principles.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "wireframe-designs",
|
||||||
|
"title": "Wireframe Designs",
|
||||||
|
"url": "/posts/career/wireframe-designs.html"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -69,6 +69,10 @@
|
|||||||
.replace(/"/g, """);
|
.replace(/"/g, """);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escAttr(str) {
|
||||||
|
return escHtml(str).replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
function normaliseHref(href, prefix) {
|
function normaliseHref(href, prefix) {
|
||||||
if (!href || href.startsWith("http") || href.startsWith("#")) return null;
|
if (!href || href.startsWith("http") || href.startsWith("#")) return null;
|
||||||
if (href.startsWith("/")) return href;
|
if (href.startsWith("/")) return href;
|
||||||
@@ -132,6 +136,129 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function commentSlug(comment) {
|
||||||
|
return comment.pageSlug || comment.page_slug || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function commentDate(comment) {
|
||||||
|
return comment.created_at || comment.createdAt || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCommentDate(value) {
|
||||||
|
const date = new Date(value);
|
||||||
|
if (Number.isNaN(date.getTime())) return "";
|
||||||
|
|
||||||
|
return new Intl.DateTimeFormat(undefined, {
|
||||||
|
month: "short",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
}).format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
function commentExcerpt(content) {
|
||||||
|
const text = String(content || "").replace(/\s+/g, " ").trim();
|
||||||
|
if (text.length <= 150) return text;
|
||||||
|
return text.slice(0, 147).trimEnd() + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRecentComments(comments, pageMap) {
|
||||||
|
const ul = document.getElementById("db-feed-comments");
|
||||||
|
if (!ul) return;
|
||||||
|
|
||||||
|
if (!comments.length) {
|
||||||
|
ul.innerHTML = (
|
||||||
|
`<li class="db-comment-feed__item">` +
|
||||||
|
`<span class="db-feed__dot"></span>` +
|
||||||
|
`<div class="db-comment-feed__body">` +
|
||||||
|
`<span class="db-comment-feed__empty">No comments yet.</span>` +
|
||||||
|
`</div>` +
|
||||||
|
`</li>`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.innerHTML = comments.map((comment) => {
|
||||||
|
const slug = commentSlug(comment);
|
||||||
|
const page = pageMap.get(slug);
|
||||||
|
const title = page?.title || slug || "Unknown page";
|
||||||
|
const href = page?.url ? page.url + "#comments" : null;
|
||||||
|
const author = comment.author || "Anonymous";
|
||||||
|
const date = formatCommentDate(commentDate(comment));
|
||||||
|
const pageLink = href
|
||||||
|
? `<a class="db-comment-feed__page" href="${escAttr(href)}">${escHtml(title)}</a>`
|
||||||
|
: `<span class="db-comment-feed__page">${escHtml(title)}</span>`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
`<li class="db-comment-feed__item">` +
|
||||||
|
`<span class="db-feed__dot"></span>` +
|
||||||
|
`<div class="db-comment-feed__body">` +
|
||||||
|
`<div class="db-comment-feed__top">` +
|
||||||
|
`<strong>${escHtml(author)}</strong>` +
|
||||||
|
`<span>on</span>` +
|
||||||
|
pageLink +
|
||||||
|
(date ? `<time datetime="${escAttr(commentDate(comment))}">${escHtml(date)}</time>` : "") +
|
||||||
|
`</div>` +
|
||||||
|
`<p>${escHtml(commentExcerpt(comment.content))}</p>` +
|
||||||
|
`</div>` +
|
||||||
|
`</li>`
|
||||||
|
);
|
||||||
|
}).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadCommentPageMap() {
|
||||||
|
try {
|
||||||
|
const resp = await fetch("/assets/content/comment-pages.json", { credentials: "same-origin" });
|
||||||
|
if (!resp.ok) return new Map();
|
||||||
|
|
||||||
|
const pages = await resp.json();
|
||||||
|
return new Map(
|
||||||
|
pages
|
||||||
|
.filter((page) => page.slug && page.url)
|
||||||
|
.map((page) => [page.slug, page])
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
return new Map();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRecentComments() {
|
||||||
|
const ul = document.getElementById("db-feed-comments");
|
||||||
|
if (!ul) return;
|
||||||
|
|
||||||
|
const renderUnavailable = () => {
|
||||||
|
ul.innerHTML = (
|
||||||
|
`<li class="db-comment-feed__item">` +
|
||||||
|
`<span class="db-feed__dot"></span>` +
|
||||||
|
`<div class="db-comment-feed__body">` +
|
||||||
|
`<span class="db-comment-feed__empty">Recent comments are unavailable.</span>` +
|
||||||
|
`</div>` +
|
||||||
|
`</li>`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [pageMap, resp] = await Promise.all([
|
||||||
|
loadCommentPageMap(),
|
||||||
|
fetch("/api/comments", { credentials: "same-origin" }),
|
||||||
|
]);
|
||||||
|
if (!resp.ok) {
|
||||||
|
renderUnavailable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const comments = await resp.json();
|
||||||
|
const recent = comments
|
||||||
|
.filter((comment) => commentDate(comment))
|
||||||
|
.sort((a, b) => new Date(commentDate(b)) - new Date(commentDate(a)))
|
||||||
|
.slice(0, 10);
|
||||||
|
|
||||||
|
renderRecentComments(recent, pageMap);
|
||||||
|
} catch (_) {
|
||||||
|
renderUnavailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function initCommandFilter() {
|
function initCommandFilter() {
|
||||||
const input = document.getElementById("db-command-search");
|
const input = document.getElementById("db-command-search");
|
||||||
const nav = document.getElementById("db-quicknav");
|
const nav = document.getElementById("db-quicknav");
|
||||||
@@ -169,6 +296,7 @@
|
|||||||
loadFeed("/blogs/blogs-list.html", "db-feed-blogs", 6, "/blogs");
|
loadFeed("/blogs/blogs-list.html", "db-feed-blogs", 6, "/blogs");
|
||||||
loadFeed("/posts/posts-list.html", "db-feed-posts", 6, "/posts");
|
loadFeed("/posts/posts-list.html", "db-feed-posts", 6, "/posts");
|
||||||
loadFeed("/recently-updated.html", "db-feed-recent", 6, "");
|
loadFeed("/recently-updated.html", "db-feed-recent", 6, "");
|
||||||
|
loadRecentComments();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
|
|||||||
@@ -351,6 +351,81 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.db-comment-feed {
|
||||||
|
display: grid;
|
||||||
|
gap: 0;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-comment-feed__item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
|
gap: 0.65rem;
|
||||||
|
align-items: start;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0.72rem 0;
|
||||||
|
margin: 0;
|
||||||
|
border-top: 1px solid color-mix(in oklab, var(--db-line) 70%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-comment-feed__item::before {
|
||||||
|
content: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-comment-feed__body {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-comment-feed__top {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.32rem;
|
||||||
|
align-items: baseline;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-comment-feed__top strong {
|
||||||
|
color: var(--fg);
|
||||||
|
font-size: 0.86rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-comment-feed__page {
|
||||||
|
color: var(--link);
|
||||||
|
font-weight: 800;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.db-comment-feed__page:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-comment-feed__top time {
|
||||||
|
margin-left: auto;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.74rem;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-comment-feed__body p {
|
||||||
|
margin: 0.22rem 0 0;
|
||||||
|
color: var(--db-text-soft);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-comment-feed__empty {
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
.db-stats-grid {
|
.db-stats-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ FMT / ARGS are passed to `format'."
|
|||||||
|
|
||||||
(require 'ox-publish)
|
(require 'ox-publish)
|
||||||
(require 'cl-lib)
|
(require 'cl-lib)
|
||||||
|
(require 'json)
|
||||||
(require 'org)
|
(require 'org)
|
||||||
(require 'ox)
|
(require 'ox)
|
||||||
(require 'ox-html)
|
(require 'ox-html)
|
||||||
@@ -339,6 +340,47 @@ Created with %c on <a href=\"https://www.archlinux.org/\">Arch</a> \
|
|||||||
slug
|
slug
|
||||||
(file-name-base file)))))
|
(file-name-base file)))))
|
||||||
|
|
||||||
|
(defun z/comment-page-url (file)
|
||||||
|
"Return root-relative published HTML URL for source FILE."
|
||||||
|
(concat "/"
|
||||||
|
(file-name-sans-extension
|
||||||
|
(file-relative-name file z/site-root))
|
||||||
|
".html"))
|
||||||
|
|
||||||
|
(defun z/comment-page-title (file)
|
||||||
|
"Return exported page title for FILE, or the file basename."
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents file nil 0 4096)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(if (re-search-forward "^#\\+TITLE:[ \t]*\\(.+?\\)[ \t]*$" nil t)
|
||||||
|
(match-string 1)
|
||||||
|
(file-name-base file))))
|
||||||
|
|
||||||
|
(defun z/comment-page-record (file)
|
||||||
|
"Return a JSON-ready comment page record for FILE."
|
||||||
|
`((slug . ,(z/comments-file-slug file))
|
||||||
|
(title . ,(z/comment-page-title file))
|
||||||
|
(url . ,(z/comment-page-url file))))
|
||||||
|
|
||||||
|
(defun z/generate-comment-pages-json ()
|
||||||
|
"Write a slug-to-page manifest for comment-enabled Org files."
|
||||||
|
(let* ((files (directory-files-recursively z/site-root "\\.org\\'"))
|
||||||
|
(records
|
||||||
|
(cl-loop for file in files
|
||||||
|
unless (or (string-prefix-p (site-path "output/") file)
|
||||||
|
(string-prefix-p (site-path "backups/") file)
|
||||||
|
(string-prefix-p (site-path ".org-timestamps/") file)
|
||||||
|
(string-prefix-p (site-path ".packages/") file))
|
||||||
|
when (z/comments-file-p file)
|
||||||
|
collect (z/comment-page-record file)))
|
||||||
|
(target (site-path "assets/content/comment-pages.json"))
|
||||||
|
(json-encoding-pretty-print t))
|
||||||
|
(make-directory (file-name-directory target) t)
|
||||||
|
(with-temp-file target
|
||||||
|
(insert (json-encode (vconcat records)))
|
||||||
|
(insert "\n"))
|
||||||
|
(z/log 'done "Wrote comment page manifest (%d pages)" (length records))))
|
||||||
|
|
||||||
(defun z/org-html-insert-comments-into-body (body backend info)
|
(defun z/org-html-insert-comments-into-body (body backend info)
|
||||||
"Append a comments section to BODY for files that opt in."
|
"Append a comments section to BODY for files that opt in."
|
||||||
(when (org-export-derived-backend-p backend 'html)
|
(when (org-export-derived-backend-p backend 'html)
|
||||||
@@ -760,6 +802,13 @@ A nil result means the timestamp cache is stale and we must force-republish."
|
|||||||
(z/log 'error "Tag generation failed: %s" (error-message-string err))
|
(z/log 'error "Tag generation failed: %s" (error-message-string err))
|
||||||
(error "Aborting build: tag generation error"))))
|
(error "Aborting build: tag generation error"))))
|
||||||
|
|
||||||
|
(z/with-timing "comment page manifest"
|
||||||
|
(condition-case err
|
||||||
|
(z/generate-comment-pages-json)
|
||||||
|
(error
|
||||||
|
(z/log 'error "Comment page manifest failed: %s" (error-message-string err))
|
||||||
|
(error "Aborting build: comment page manifest error"))))
|
||||||
|
|
||||||
;; ─────────────────────────────────────────────────────────────────────────────
|
;; ─────────────────────────────────────────────────────────────────────────────
|
||||||
;; 16. Publish
|
;; 16. Publish
|
||||||
;; ─────────────────────────────────────────────────────────────────────────────
|
;; ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
17
index.org
17
index.org
@@ -170,6 +170,23 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="db-panel db-panel--wide" aria-labelledby="db-comments-title">
|
||||||
|
<header class="db-panel__head">
|
||||||
|
<div>
|
||||||
|
<p class="db-kicker">Discussion</p>
|
||||||
|
<h2 class="db-panel__title" id="db-comments-title">Recent comments</h2>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<ul class="db-comment-feed" id="db-feed-comments" aria-live="polite">
|
||||||
|
<li class="db-comment-feed__item">
|
||||||
|
<span class="db-feed__dot"></span>
|
||||||
|
<div class="db-comment-feed__body">
|
||||||
|
<span class="db-comment-feed__empty">Loading recent comments...</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="db-panel" aria-labelledby="db-system-title">
|
<section class="db-panel" aria-labelledby="db-system-title">
|
||||||
<header class="db-panel__head">
|
<header class="db-panel__head">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user