skip to content
walterra.dev
Table of Contents

This is a recap of what I’ve been up to with Eddo, my personal GTD todo app, during October 2025. I started looking into performance problems and ended up printing daily briefings on thermal receipt paper. Guess I got a bit sidetracked!

The CouchDB Sync Problem

The month started with performance becoming more sluggish. The web client was showing noticeable lag when syncing with CouchDB, especially as todo lists grew larger after I merged my account with another instance. It’s still something I haven’t solved yet. The root cause seems that PouchDB indices are not properly cached when just a few documents change. Something causes the full indices to be recreated which of course slows things down.

Originally I thought it was caused by the sync with CouchDB. I tweaked some settings along the way in packages/web-client/src/hooks/use_couchdb_sync.ts:

// Reduced batch size from 500 to 100
batch_size: 100,
batches_limit: 3,
// Added index pre-warming on sync pause
sync.on('paused', () => {
// Pre-warm indexes to prevent stale reads
warmupIndexes();
});

Related commit: 1225c9f - Optimize CouchDB sync performance and fix memory issues

Introducing TanStack Query

I couldn’t fully solve the problem so I thought why not throw yet another library at the problem? I’ve used TanStack Query before and like it’s approach. Adding another state layer felt redundant at first - but TanStack Query isn’t really about state management, it’s about data synchronization.

I did the migration in two phases:

Phase 1 (Oct 1): Basic integration with todo queries

  • Added @tanstack/react-query to the web-client
  • Created query hooks for weekly todo fetching: use_todos_by_week.ts and use_activities_by_week.ts
  • Set up the QueryClient with sensible defaults in config/query_client.ts

Phase 2 (Oct 5): Time tracking migration

  • Moved active time tracking to TanStack Query
  • Added tests in use_time_tracking_active.test.ts
  • Simplified the todo_board.tsx component significantly by removing manual cache management

The result? The todo_board.tsx component went from managing its own query caching logic to just declaring what data it needs:

const { data: todos } = useTodosByWeek(currentWeek);
const { data: activeTracking } = useTimeTrackingActive();

Much cleaner. The component shrunk from a complex mix of PouchDB queries and local state to focused UI logic.

Related commits:

  • 3923b77 - Add TanStack Query for data caching and state management
  • 568da08 - Migrate time tracking to TanStack Query
  • 462b542 - Consolidate database structure definitions into shared package

The Thermal Printer Adventure

Here’s where things got fun. I’ve been using my Telegram bot for daily briefings - it queries my todos, checks what’s due, and gives me a morning summary. But I wanted something more tangible. A while ago I came across Laurie Hérault’s post A receipt printer cured my procrastination and it really stuck with me, I wanted to combine this approach with Eddo. Enter the Epson TM-m30III thermal receipt printer.

The printer integration turned into its own mini-project:

  1. Created a new printer_service package with a CLI and formatter

  2. Built a text-to-receipt formatter that handles:

    • Markdown-style formatting (headers, lists, emphasis)
    • Emoji stripping (thermal printers don’t love emojis)
    • QR code generation for repo links
    • Proper line wrapping at 48 characters
  3. Integrated with the Telegram bot so briefings can auto-print

  4. Added user preferences so people can opt-in to printed briefings

node-thermal-printer does all the heavy lifting!

Related commits:

  • a309824 - Add thermal printer support for daily briefings
Thermal receipt printout showing a daily briefing from Eddo GTD app dated 10/5/2025, displaying today's tasks (doctor appointment, project review, groceries), overdue items (authentication bug fix, expense report), next actions (client calls, documentation updates), and active time tracking for printer service implementation

Daily Briefing Recap Command

With the printer working, I noticed the briefings were good for “what’s happening today” but didn’t help with reflection. So I added a /briefing recap command that looks back at what actually got done.

This feature involved:

  • Adding user preferences for recap format (detailed vs. summary)
  • Dynamic date handling so you can request recaps for any day or date range
  • Printer integration with custom headers for recap printouts
  • MCP server enhancements to support the new query patterns

The prompt engineering for the recap was interesting. Instead of just listing completed todos, I wanted the AI agent to synthesize patterns and provide insights:

const recapPrompt = `
Analyze completed tasks for ${dateRange} and provide:
1. Key accomplishments and themes
2. Time investment patterns
3. Suggestions for tomorrow
User preferences: ${userPreferences}
`;

The Telegram bot’s simple agent loop (packages/telegram_bot/src/agent/simple-agent.ts) handles this with just 60-ish lines of code, just a loop that asks the LLM what to do next.

Related commits:

  • 63c897c - feat: add daily briefing recap command with user preferences
  • 93b491e - feat: add gtd:calendar query to daily briefing

UI Polish

Amid all the backend work, I shipped one small UI improvement that made me unreasonably happy: todo action icons now only appear on hover.

Before, every todo item had a row of icons (edit, delete, timer) always visible, making the interface feel cluttered. Now they fade in smoothly when you hover:

.todo-icons {
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.todo-item:hover .todo-icons {
opacity: 1;
}

It’s a tiny change (3 files, 12 lines), but it makes the interface feel cleaner and more focused.

Related commits:

  • 636ec5f - feat: show todo icons only on hover with transitions

Documentation & Dependency Maintenance

I also spent time improving the project structure:

  • Split the growing design docs into separate focused documents (9e3dd6a)
  • Migrated to prettier-plugin-organize-imports for better import sorting (600932a)
  • Moved commitizen config to its own .czrc file

Dependabot was busy this month too - 14 dependency updates merged, including major version bumps for:

  • commander (12.1.0 → 14.0.1)
  • uuid (11.1.0 → 13.0.0)
  • nano (10.1.4 → 11.0.0)
  • lint-staged (15.5.2 → 16.2.3)

That’s for this month. I’m using the printed daily briefings now for a few weeks now, they just jump out of the printer every morning. It’s nice to have something tangible.