From b2b2e80f0a33935da129f5ceaab90ac175c5d3e0 Mon Sep 17 00:00:00 2001
From: lotherk
Date: Fri, 27 Mar 2026 14:33:05 +0000
Subject: [PATCH] feat: add blog system with static site generator
- Add blog posts in Markdown (_posts/)
- Build script converts MD to HTML at container build time
- First posts: building with AI lessons, quick start guide
- AGENTS.md documents blog writing style (unixsheikh-inspired)
---
.gitignore | 2 +
Dockerfile.website | 27 ++-
www/AGENTS.md | 120 +++++++++++
.../2026-03-27-building-deardiary-with-ai.md | 135 ++++++++++++
www/_posts/2026-03-27-quick-start-guide.md | 59 ++++++
www/build-blog.js | 200 ++++++++++++++++++
www/package-lock.json | 123 +++++++++++
www/package.json | 7 +
8 files changed, 667 insertions(+), 6 deletions(-)
create mode 100644 www/AGENTS.md
create mode 100644 www/_posts/2026-03-27-building-deardiary-with-ai.md
create mode 100644 www/_posts/2026-03-27-quick-start-guide.md
create mode 100644 www/build-blog.js
create mode 100644 www/package-lock.json
create mode 100644 www/package.json
diff --git a/.gitignore b/.gitignore
index 48347e6..9f0ce3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,12 +3,14 @@ node_modules/
.pnp
.pnp.js
docs/node_modules/
+www/node_modules/
# Build
dist/
build/
docs/dist/
docs/.astro/
+www/blog/
# Data
data/
diff --git a/Dockerfile.website b/Dockerfile.website
index 981271e..ed7eff3 100644
--- a/Dockerfile.website
+++ b/Dockerfile.website
@@ -1,11 +1,26 @@
-FROM nginx:alpine
+FROM node:20-alpine
-COPY www/ /usr/share/nginx/html/
-COPY docker-entrypoint.d/ /docker-entrypoint.d/
+WORKDIR /app
+# Install dependencies for blog build
+COPY www/package.json ./www/package.json
+RUN cd www && npm install --silent 2>/dev/null || true
+
+# Copy www source files
+COPY www/ /app/www/
+
+# Build blog (processes _posts/*.md to blog/*.html)
+RUN node www/build-blog.js
+
+# Run envsubst on HTML files
RUN apk add --no-cache gettext && \
- chmod +x /docker-entrypoint.d/*.sh
+ chmod +x /docker-entrypoint.d/*.sh 2>/dev/null || true
+
+# Copy entrypoint
+COPY docker-entrypoint.d/ /docker-entrypoint.d/
+RUN chmod +x /docker-entrypoint.d/*.sh
+
+# Copy to nginx document root
+COPY --from=0 /app/www/ /usr/share/nginx/html/
ENTRYPOINT ["/docker-entrypoint.d/30-envsubst.sh", "--", "nginx", "-g", "daemon off;"]
-
-CMD ["nginx", "-g", "daemon off;"]
diff --git a/www/AGENTS.md b/www/AGENTS.md
new file mode 100644
index 0000000..eb630cd
--- /dev/null
+++ b/www/AGENTS.md
@@ -0,0 +1,120 @@
+# DearDiary Website & Blog - Agent Instructions
+
+## Overview
+
+The DearDiary website is a static HTML site generated from Markdown blog posts. No frameworks, no build complexity.
+
+## Structure
+
+```
+www/
+├── _posts/ # Blog posts in Markdown (source)
+│ └── YYYY-MM-DD-slug.md
+├── blog/ # Generated HTML (don't edit manually)
+│ ├── index.html
+│ └── YYYY-MM-DD-slug/
+│ └── index.html
+├── css/
+│ └── styles.css
+├── docs/ # Documentation (also static HTML)
+├── js/
+│ └── main.js
+├── index.html # Main website (uses envsubst for templating)
+├── build-blog.js # Blog generator script
+└── package.json # npm deps (gray-matter for parsing)
+```
+
+## Building
+
+```bash
+cd www
+npm install
+node build-blog.js
+```
+
+This converts `_posts/*.md` → `blog/*.html`.
+
+## Blog Post Format
+
+Frontmatter:
+```yaml
+---
+title: Your Post Title
+date: 2026-03-27
+author: Konrad Lother
+excerpt: One sentence summary for the blog index
+---
+```
+
+## Tone & Style Guide
+
+Read unixsheikh.com for inspiration. Key characteristics:
+
+### Voice
+- **Direct and authoritative** - Say what you mean, don't hedge unnecessarily
+- **Personal opinion is welcome** - Write from a first-person perspective
+- **Technical but accessible** - Explain complex topics simply
+- **Not afraid to be contrarian** - Challenge conventional wisdom when you disagree with it
+
+### Structure
+- **Strong opening** - Hook the reader immediately
+- **Clear sections** - Use headers to organize thoughts
+- **Concrete examples** - Illustrate abstract concepts
+- **Practical takeaways** - End with actionable advice
+
+### What to Avoid
+- Corporate/tech-blog speak ("leverage", "synergy", "cutting-edge")
+- Unnecessary hedging ("might", "could potentially", "in some cases")
+- Padding or fluff - every sentence should add value
+- Being mean-spirited - critique ideas, not people
+
+### Example Openers
+
+Bad:
+> "In today's fast-paced digital world, journaling has evolved..."
+
+Good:
+> "Traditional journaling apps ask you to write long entries at the end of the day. But let's be honest - who has time for that?"
+
+### Length
+- 500-1500 words is ideal
+- If you're writing more, consider splitting into multiple posts
+- Short posts can be 200-300 words if the idea is complete
+
+## Topics to Cover
+
+### DearDiary-Specific
+- How to use features effectively
+- Tips and tricks for better journaling
+- Behind-the-scenes development stories
+- Why AI-assisted journaling makes sense
+- Privacy and self-hosting benefits
+
+### General Tech Thoughts
+- Software craftsmanship
+- Human-AI collaboration
+- Simplicity in development
+- Critical thinking about tech trends
+- The value of boring technology
+
+### Personal/Philosophy
+- Productivity and reflection
+- Memory and identity
+- The importance of记录 (record-keeping)
+- Learning from daily life
+
+## Post Ideas
+
+1. Why capture events, not essays (the thesis behind DearDiary)
+2. My journaling experiment - 30 days of DearDiary
+3. What I learned building an app with AI
+4. The case for boring technology in side projects
+5. How to remember your life better
+6. Self-hosting isn't hard, it's just unfamiliar
+
+## Technical Notes
+
+- The `build-blog.js` uses a simple regex-based Markdown converter
+- For full Markdown support (tables, footnotes), consider using a proper parser
+- Images should be hosted externally or added to the repo
+- Code blocks work but syntax highlighting is limited
diff --git a/www/_posts/2026-03-27-building-deardiary-with-ai.md b/www/_posts/2026-03-27-building-deardiary-with-ai.md
new file mode 100644
index 0000000..3c0e316
--- /dev/null
+++ b/www/_posts/2026-03-27-building-deardiary-with-ai.md
@@ -0,0 +1,135 @@
+---
+title: Building DearDiary with AI - Lessons from Human-AI Collaboration
+date: 2026-03-27
+author: Konrad Lother
+excerpt: What I learned from building a full-stack app using an AI coding assistant
+---
+
+# Building DearDiary with AI
+
+## A Tale of Miscommunication and Debugging
+
+I built DearDiary using an AI coding assistant. It was enlightening, frustrating, sometimes hilarious, and ultimately successful. Here's what I learned about human-AI collaboration.
+
+## The Setup
+
+DearDiary is a full-stack journaling app: Bun + Hono backend, React + Vite frontend, SQLite database, Docker deployment. Not trivial, but not rocket science either.
+
+I gave the AI context about the project structure, my preferences, and let it work.
+
+## The Problems We Hit
+
+### 1. "It Should Work, But..."
+
+The first major issue was the most classic: the AI made changes that *should* have worked according to its understanding, but didn't.
+
+We consolidated environment variables into a single `.env` file with prefixes. The AI updated most references to `DATABASE_URL` → `BACKEND_DATABASE_URL`, but missed several:
+- The Prisma schema
+- The test helpers
+- A variable in the healthcheck config
+
+The app failed to start. The error message? Cryptic Prisma errors that took time to trace back to a simple env var mismatch.
+
+**Lesson**: AI is great at systematic changes, but when it misses something, the gap is invisible to it. Always verify systematically.
+
+### 2. The Disappearing Routes
+
+The AI moved API routes into a separate file (`events.ts`) and mounted them at `/api/v1`. Simple, clean.
+
+Except the routes were at `/api/v1/events` but the frontend was calling `/events`. The AI didn't catch that the mounting path was part of the route definition.
+
+**Lesson**: AI understands code structure well, but context about how pieces connect across files is easily lost.
+
+### 3. "I Fixed That"
+
+Multiple times, the AI would say "Fixed!" and show the corrected code, but the actual file hadn't been changed. Or it would describe a solution that wasn't implemented.
+
+This is the most dangerous mode of failure - confidence without execution.
+
+**Lesson**: Never trust "fixed" without verification. Make it show you the actual changes.
+
+### 4. Permission Denied
+
+Docker entrypoint scripts kept failing with "permission denied". The AI knew about `chmod +x`, but the order of operations was wrong - file copied after chmod, or Docker cache serving old versions.
+
+**Lesson**: AI knows facts, but execution order matters. Sometimes you need to walk through the sequence step by step.
+
+### 5. The 404 Debugging Journey
+
+Events returned 404. We checked:
+1. Routes - correct
+2. Mounting - fixed
+3. Auth middleware - fixed
+4. The actual problem: nginx port mapping. Port 3000 on the host was mapped directly to the backend, not through nginx. The frontend (served by nginx) couldn't reach the API.
+
+**Lesson**: The AI focused on the obvious layers. The problem was in the infrastructure/configuration layer. AI needs explicit context about the full stack.
+
+## What Went Well
+
+Despite these issues, things also went surprisingly well:
+
+- **Feature implementation**: The core features (event capture, AI generation, search) worked on first try
+- **Consistency**: Once a pattern was established, the AI maintained it consistently
+- **Refactoring**: Moving from multiple `.env` files to one was smooth after the initial issues
+- **Documentation**: README updates, code comments, and AGENTS.md were accurate
+
+## The Communication Patterns That Worked
+
+### Be Specific About Failures
+Instead of "it doesn't work", I'd say:
+> "Events endpoint returns 404, checked docker logs and the route is registered"
+
+The more context, the better the fix.
+
+### Ask for Verification
+> "Show me the exact changes you're making before committing"
+
+This caught the "I said I fixed it" problem.
+
+### Break Down Complex Changes
+Instead of "consolidate all env vars", we did it in stages:
+1. List all current env vars
+2. Decide on naming convention
+3. Update backend
+4. Update frontend
+5. Update docker-compose
+6. Verify
+
+### State What You Know Works
+> "Previous similar changes worked with `docker compose build && docker compose up -d`"
+
+Context about what has worked before helps the AI avoid untested approaches.
+
+## The Meta-Lesson
+
+Building with AI is like working with a very knowledgeable junior developer who:
+- Has read every Stack Overflow post
+- Can write code faster than you can type
+- Sometimes confidently does the wrong thing
+- Needs supervision, especially for changes spanning multiple files
+- Gets better with clearer instructions
+
+The key insight: **Your job becomes managing the AI, not just writing code.** You need to:
+1. Provide good context
+2. Verify systematically
+3. Catch the invisible gaps
+4. Maintain the mental model of the system
+
+## What I'd Do Differently
+
+1. **Track changes more carefully** - Use a changelog when AI makes changes, not just git diff
+2. **Test incrementally** - Don't let the AI make 20 changes before testing
+3. **Be clearer about expectations** - "This should work out of the box" is less clear than explicit test criteria
+4. **Document the debugging journey** - The process of finding issues is valuable context for future fixes
+
+## Conclusion
+
+DearDiary is live. The AI and I built it together, argued about typos in environment variables, debugged at 2am, and shipped something I'm proud of.
+
+Human-AI collaboration isn't about replacing programmers. It's about amplifying what humans do well (context, judgment, verification) with what AI does well (speed, consistency, pattern matching).
+
+The future is not "AI replaces developers." It's "developers who use AI replace developers who don't."
+
+Now go build something with AI. Just keep an eye on those env vars.
+
+*— Konrad*
diff --git a/www/_posts/2026-03-27-quick-start-guide.md b/www/_posts/2026-03-27-quick-start-guide.md
new file mode 100644
index 0000000..01443d4
--- /dev/null
+++ b/www/_posts/2026-03-27-quick-start-guide.md
@@ -0,0 +1,59 @@
+---
+title: Quick Start Guide
+date: 2026-03-26
+author: Konrad Lother
+excerpt: How to get started with DearDiary in 5 minutes
+---
+
+# Quick Start Guide
+
+Getting started with DearDiary takes about 5 minutes. Here's how:
+
+## 1. Create an Event
+
+Press **Ctrl+J** anywhere in the app to open the Quick Add widget. Type your event and press Enter.
+
+That's it. It takes 3 seconds.
+
+Try these:
+- "Had coffee with Sarah at the new cafe downtown"
+- "Finished the chapter on machine learning"
+- "Rain started around 3pm, got soaked walking back"
+
+## 2. Add More Events
+
+Throughout your day, capture anything that feels worth remembering:
+- Meetings and conversations
+- Meals and what you ate
+- Exercise and how you felt
+- Thoughts and ideas
+- Photos and voice memos
+
+Don't overthink it. A short note is better than nothing.
+
+## 3. Generate Your Diary
+
+When you're ready, click the **Generate** button on today's page. AI reads all your events and writes a narrative diary entry.
+
+You can:
+- Regenerate with different instructions
+- Add context by including previous days' diaries
+- Edit the generated diary (but the events stay locked)
+
+## 4. Review and Reflect
+
+Read your generated diary. Does it capture the essence of your day? If not, regenerate with instructions like:
+
+> "Focus more on the interesting conversations I had"
+
+or
+
+> "Make it more concise, highlight the key moments"
+
+## Pro Tips
+
+- **Be specific**: "Lunch with Marcus, talked about his new hiking trip to Patagonia" beats "Had lunch"
+- **Capture emotions**: "Felt anxious before the presentation" is more interesting than "Presented to team"
+- **Use voice**: Record voice memos while driving or walking - very efficient
+
+That's it. Happy journaling!
diff --git a/www/build-blog.js b/www/build-blog.js
new file mode 100644
index 0000000..c656c9d
--- /dev/null
+++ b/www/build-blog.js
@@ -0,0 +1,200 @@
+const fs = require('fs');
+const path = require('path');
+const matter = require('gray-matter');
+
+const postsDir = path.join(__dirname, '_posts');
+const outputDir = path.join(__dirname, 'blog');
+
+// Ensure output directory exists
+if (!fs.existsSync(outputDir)) {
+ fs.mkdirSync(outputDir, { recursive: true });
+}
+
+// Get all markdown files
+const files = fs.readdirSync(postsDir).filter(f => f.endsWith('.md'));
+
+// Parse and generate HTML for each post
+const posts = files.map(file => {
+ const content = fs.readFileSync(path.join(postsDir, file), 'utf-8');
+ const { data: frontmatter, content: markdown } = matter(content);
+
+ // Simple markdown to HTML conversion
+ let html = markdown
+ // Headers
+ .replace(/^### (.*$)/gim, '