Commit Graph

52 Commits

Author SHA1 Message Date
Thomas Richter
20d9ebf2ff test(09-02): add unit tests for highlightText and parseHashtags utilities
- highlightText: 24 tests covering highlighting, case sensitivity, HTML escaping
- parseHashtags: 29 tests for extraction, 6 tests for highlightHashtags
- Tests verify XSS prevention, regex escaping, edge cases
2026-02-03 23:33:36 +01:00
Thomas Richter
b930f1842c test(09-01): add filterEntries unit tests proving infrastructure
- Test empty input handling
- Test query filter (min 2 chars, case insensitive, title OR content)
- Test tag filter (AND logic, case insensitive)
- Test type filter (task/thought/all)
- Test date range filter (start, end, both)
- Test combined filters
- Test generic type preservation

17 tests covering filterEntries.ts with 100% coverage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 23:29:44 +01:00
Thomas Richter
f60aad2864 feat(08-01): add Prometheus /metrics endpoint with prom-client
- Install prom-client library for Prometheus metrics
- Create src/lib/server/metrics.ts with default Node.js process metrics
- Add /metrics endpoint that returns Prometheus-format text
- Exposes CPU, memory, heap, event loop metrics

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 22:05:16 +01:00
Thomas Richter
b205fedde6 fix: remove deleted tags from filter automatically
When a tag is deleted as orphaned, it's now automatically removed
from the active filter selection.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 23:16:49 +01:00
Thomas Richter
c92aec14d3 feat: auto-cleanup orphaned tags when removed from entries
Tags that are no longer associated with any entry are automatically
deleted from the database when a tag is removed from an entry.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 23:13:50 +01:00
Thomas Richter
b00d71956e fix: explicit tag scanning with delete UI
- Remove auto-parsing on collapse
- Add "Scan for #tags" button that parses from current textarea content
- Always show tags section with delete buttons
- Show help text when no tags exist

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:41:35 +01:00
Thomas Richter
a19e51b6b9 fix: parse hashtags only when collapsing entry card
Move tag parsing from textarea blur to entry collapse action.
This prevents incomplete tags from being created while editing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:35:32 +01:00
Thomas Richter
79c9b12702 fix: parse hashtags only on blur/submit, add tag delete UI
- Parse hashtags when content textarea loses focus (blur)
- Parse hashtags when QuickCapture form is submitted
- Add tag display with delete buttons in expanded entry view
- Remove automatic hashtag parsing during typing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:27:02 +01:00
Thomas Richter
4a57000c38 fix: only parse complete hashtags (followed by word boundary)
Prevents incomplete hashtags from being saved during typing when
auto-save fires mid-word.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 22:17:39 +01:00
Thomas Richter
ce07d79652 feat: parse hashtags from content instead of dedicated tag input
Tags are now automatically extracted from #hashtags in content text.
Removed TagInput from entry editing, keeping it only in FilterBar.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 21:59:04 +01:00
Thomas Richter
84ad332737 fix(deploy): resolve Docker startup and CSRF issues
- Rename TASKPLANER_DATA_DIR to DATA_DIR (avoid adapter-node envPrefix conflict)
- Add TASKPLANER_ORIGIN for CSRF protection in docker-compose.yml
- Add automatic database schema initialization on startup
- Add Playwright E2E tests for Docker deployment verification
- Update .env.example with correct variable names

Fixes container restart loop and 403 errors on form submission.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 15:54:44 +01:00
Thomas Richter
de09b14239 feat(06-02): create health check endpoint
- Returns 200 'ok' when database is accessible
- Returns 503 'unhealthy' when database query fails
- Simple text response for Docker HEALTHCHECK
2026-02-01 13:23:12 +01:00
Thomas Richter
457bacaeab feat(06-01): configure data paths via TASKPLANER_DATA_DIR env var
- Update db/index.ts to read data directory from env var
- Update storage.ts to read data directory from env var
- Both default to ./data for local development
2026-02-01 13:20:49 +01:00
Thomas Richter
bb1e64ad2b feat(05-03): add highlighting to EntryCard and integrate search UI
- Add searchQuery prop to EntryCard for text highlighting
- Use {@html highlightText()} for title and content in collapsed view
- Integrate SearchBar and FilterBar in +page.svelte
- Add search/filter state with reactive sync to filters.query
- Pass recentSearches store to SearchBar
- Update filterEntries to use generics for type preservation
2026-01-31 17:18:55 +01:00
Thomas Richter
9642b51ef8 feat(05-03): integrate filtering into EntryList with flat list mode
- Add filters and searchQuery props to EntryList
- Apply filterEntries via $derived for reactive filtering
- Display flat list (no pinned separation) when filtering
- Show search-specific empty state when no matches
- Pass searchQuery to EntryCard for highlighting
2026-01-31 17:17:31 +01:00
Thomas Richter
af61b10a59 feat(05-03): create recent searches store and add dropdown UI
- Add recentSearches persisted store with 5-item limit
- Add addRecentSearch() function for deduplication
- Update SearchBar with recentSearches dropdown
- Show dropdown on focus when input empty
- Use onmousedown to handle selection before blur
- Add to recent searches on blur with >= 2 chars
2026-01-31 17:17:03 +01:00
Thomas Richter
5e94609286 feat(05-01): create FilterBar component with type, tag, date controls
- Type filter: three-state toggle (All/Tasks/Thoughts)
- Tag filter: multi-select using Svelecte
- Date range: quick presets (Today/Week/Month) + custom inputs
- Clear button: only visible when hasActiveFilters() returns true
- Horizontal flex-wrap layout for responsive design
2026-01-31 17:13:16 +01:00
Thomas Richter
6dbe660a8e feat(05-02): create highlightText utility function
- Wrap matching text in <mark> tags with bold styling
- Escape HTML before highlighting to prevent XSS
- 2-character minimum query requirement
- Uses bg-transparent (no background per CONTEXT.md)
2026-01-31 17:12:43 +01:00
Thomas Richter
b7a982c104 feat(05-01): create SearchBar component with debounced input
- Debounced value binding (300ms delay via $effect cleanup)
- Only trigger search when query >= 2 chars OR cleared to empty
- Bindable value prop for parent integration
- '/' keyboard shortcut to focus search (GitHub-style)
- Native event listener to avoid Svelte 5 keydown bug
2026-01-31 17:12:25 +01:00
Thomas Richter
8f544a9989 feat(05-02): create filterEntries utility function
- Add SearchFilters type and utilities (search.ts)
- Implement filterEntries with query, tags, type, dateRange filters
- Case-insensitive text search with 2-character minimum
- AND logic for tag filtering
- Date range inclusive of end date
2026-01-31 17:11:53 +01:00
Thomas Richter
b1e62a4658 feat(05-01): create search types and utility functions
- Add SearchFilters interface for query, tags, type, dateRange
- Add defaultFilters constant with empty/default values
- Add hasActiveFilters() to detect when filters are active
- Add getDatePreset() helper for today/week/month presets
2026-01-31 17:11:51 +01:00
Thomas Richter
0c1a66b4c6 feat(04-03): create TagInput component and integrate into EntryCard
- Create TagInput.svelte with Svelecte for multi-select autocomplete
- Support creating new tags inline via creatable mode
- Display tags on collapsed entry cards (max 3 with +N indicator)
- Add TagInput in expanded view for editing entry tags
- Pass availableTags through EntryList to EntryCard
- Handle tag changes with immediate save via updateTags action
2026-01-31 13:09:34 +01:00
Thomas Richter
cfdb804118 feat(04-03): install Svelecte and add tag support to load/actions
- Install svelecte package for tag input with autocomplete
- Import tagRepository in +page.server.ts
- Attach tags to entries in load function alongside images
- Return allTags for autocomplete suggestions
- Add updateTags action to update entry tags via JSON payload
2026-01-31 13:07:08 +01:00
Thomas Richter
164fc73532 feat(04-02): add pin button, due date picker, and pinned section UI
- Add pin button in expanded view with toggle functionality
- Add due date picker in expanded view with date input
- Show pin indicator and due date in collapsed view
- Separate EntryList into pinned and unpinned sections
- Pinned section appears at top with header label
2026-01-31 13:04:10 +01:00
Thomas Richter
378d928b53 feat(04-01): create tagRepository with tag operations
- Add TagRepository interface with findOrCreate, getAll, getById, getByEntryId, updateEntryTags
- Implement SQLiteTagRepository with case-insensitive tag lookup
- updateEntryTags atomically replaces all tags for an entry
- Export tagRepository singleton
2026-01-31 13:03:53 +01:00
Thomas Richter
7dc63e625d feat(04-01): add tags schema with case-insensitive unique index
- Add tags table with nanoid PK and case-insensitive unique index on name
- Add entry_tags junction table with composite PK and cascade deletes
- Export lower() helper function for case-insensitive queries
- Export Tag, NewTag, EntryTag types for TypeScript inference
2026-01-31 13:03:17 +01:00
Thomas Richter
a232a95ced feat(04-02): add togglePin and updateDueDate form actions
- Add togglePin action to toggle entry.pinned boolean state
- Add updateDueDate action to set/clear entry.dueDate
- Both actions validate entry exists before updating
2026-01-31 13:02:55 +01:00
Thomas Richter
a2f9183011 fix(03-04): replace CameraCapture with file input capture="environment"
- Remove CameraCapture modal which required getUserMedia (HTTPS only)
- Use native file input with capture="environment" attribute
- Opens camera directly via OS file picker on mobile
- Works over HTTP during development
- Simpler implementation with better browser compatibility
2026-01-31 12:19:16 +01:00
Thomas Richter
b239862854 feat(03-04): add deleteImage form action
- Delete image files from filesystem (original + thumbnail)
- Delete image record from database
- Continue database deletion even if file deletion fails
- Returns success after cleanup
2026-01-29 15:33:18 +01:00
Thomas Richter
0acff1b438 feat(03-04): integrate images into EntryCard
- Show image count indicator in collapsed entry view
- Add horizontal scrolling gallery in expanded view
- Edit mode reveals delete buttons on images
- Add ImageUpload and Camera button for adding images
- CameraCapture modal for mobile photo capture
- Updated EntryList to pass entries with images
2026-01-29 15:32:47 +01:00
Thomas Richter
eaf976f24b feat(03-04): ImageGallery and ImageLightbox components
- ImageGallery renders horizontal scrolling thumbnails
- Edit mode shows delete buttons on images
- ImageLightbox provides fullscreen image viewer
- Keyboard navigation (Escape, arrows) in lightbox
- Click outside image or X button closes lightbox
- Image counter shown when multiple images
2026-01-29 15:31:15 +01:00
Thomas Richter
400e4999fd feat(03-02): integrate image upload into QuickCapture
- Add image button next to submit button
- Show pending image preview with filename
- Allow removing pending image before submit
- Upload image after entry creation with new entry ID
- Disable button during upload to prevent duplicates
- Clean up object URLs properly
2026-01-29 15:28:22 +01:00
Thomas Richter
8248e0cd91 feat(03-03): CameraCapture modal with getUserMedia
- Full-screen camera modal with live preview via getUserMedia
- Photo capture using canvas.toBlob (JPEG at 0.9 quality)
- Preview captured photo with retake/confirm flow
- Camera stream properly stopped on close and after capture
- Switch camera button for front/back camera toggle
- Upload integration via ?/uploadImage form action

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 15:27:51 +01:00
Thomas Richter
a35b07e45e feat(03-02): create ImageUpload component with drag-drop
- Add drag-and-drop zone for image uploads
- Show optimistic preview during upload
- Support keyboard navigation (Enter/Space to trigger)
- Handle upload via fetch to ?/uploadImage action
- Clean up object URLs after upload completes
- Provide callbacks for upload lifecycle events
2026-01-29 15:27:48 +01:00
Thomas Richter
de3aa5ac4e feat(03-02): add uploadImage form action with thumbnail generation
- Add uploadImage action that handles multipart file uploads
- Validate file type and entry existence before processing
- Generate thumbnail using Sharp with EXIF rotation
- Save original and thumbnail to filesystem
- Store image metadata in database
- Return entries with their images attached from load function
2026-01-29 15:27:12 +01:00
Thomas Richter
b3640e78a1 feat(03-01): add API endpoints for serving images
- Add GET /api/images/[id] for original images
- Add GET /api/images/[id]/thumbnail for thumbnails
- Both endpoints return 404 for missing images
- Immutable cache headers for CDN optimization
2026-01-29 15:23:33 +01:00
Thomas Richter
0987d16dc0 feat(03-01): add storage and thumbnail utilities
- Add storage.ts with directory management and file I/O
- Add thumbnails.ts with EXIF-aware thumbnail generation
- Thumbnails always output as JPEG with 80% quality
2026-01-29 15:23:08 +01:00
Thomas Richter
f5b5034f07 feat(03-01): add images table and repository
- Add images table with entryId foreign key and cascade delete
- Add Image and NewImage types
- Add ImageRepository with create, getById, getByEntryId, delete, deleteByEntryId
- Install sharp for thumbnail generation
2026-01-29 15:22:38 +01:00
Thomas Richter
1533759c47 fix(02-04): improve swipe-to-delete with direct touch handlers
- Replace svelte-gestures with native touch event handlers
- Add invalidateAll() after save/delete for seamless list updates
- Add $effect to sync edit state when entry prop changes
- Improve swipe animation with isSwiping state tracking
2026-01-29 14:27:05 +01:00
Thomas Richter
104c437ea6 feat(02-04): add swipe-to-delete gesture for mobile
- Install svelte-gestures package for touch gesture support
- Add useSwipe hook to EntryCard for left-swipe detection
- Implement delete confirmation overlay on swipe
- Add red delete background revealed during swipe
- Maintain desktop delete button in expanded view
2026-01-29 11:17:50 +01:00
Thomas Richter
fc7c1f6c98 feat(02-03): add CompletedToggle to show/hide completed tasks
- Create CompletedToggle component with checkbox UI
- Add showCompleted URL param to +page.server.ts load function
- Update +page.svelte to sync preferences with URL
- Toggle state persists in localStorage via preferences store
- Clicking toggle updates URL and invalidates data for server filter
2026-01-29 11:12:51 +01:00
Thomas Richter
7d66a8f6fc feat(02-03): add expand/collapse and inline editing to EntryCard
- Add expanded state for expand/collapse toggle
- Implement inline editing for title, content, and type
- Add 400ms debounced auto-save using fetch to ?/update
- Show 'Saving...' indicator during save
- Add delete button with confirmation dialog
- Use Svelte slide transition for smooth expand/collapse
- Fix button nesting by restructuring layout
2026-01-29 11:11:41 +01:00
Thomas Richter
fed184828e feat(02-02): add QuickCapture component and integrate main page
- QuickCapture fixed at bottom with type selector and submit button
- Uses enhance for progressive form enhancement
- Persists last used type via preferences store
- Clears form on successful submit
- Main page composes EntryList and QuickCapture
- Adequate bottom padding (pb-40) for capture bar clearance
- Sticky header with app title

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:07:51 +01:00
Thomas Richter
7c3a8b024d feat(02-02): add EntryCard and EntryList components
- EntryCard displays entry with type indicator (checkbox/badge)
- Tasks have checkbox for toggle completion via form action
- Thoughts show purple "T" badge indicator
- Completed tasks show strikethrough styling
- EntryList renders entries with mobile compact/desktop card layout
- Type badge shows blue for task, purple for thought

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:06:51 +01:00
Thomas Richter
ed6659f266 feat(02-02): add preferences store with svelte-persisted-store
- Install svelte-persisted-store for localStorage persistence
- Create preferences store with lastEntryType and showCompleted
- Sticky type preference across sessions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:06:19 +01:00
Thomas Richter
234d765749 feat(02-01): add accessible base styling
- Set html font-size to 100% (respects browser settings)
- Set body font-size to 1rem (16px minimum)
- Add font smoothing for better readability
- Add safe-bottom class for mobile notch support
- Add touch-target utility for WCAG 44x44px minimum
2026-01-29 11:03:07 +01:00
Thomas Richter
9a449228b7 feat(02-01): add CRUD form actions
- Add create action with content validation (title optional, type defaults to thought)
- Add update action with field-by-field updates and validation
- Add delete action with existence check
- Add toggleComplete action to toggle between open/done
- Use getOrdered() in load function for tasks-first ordering
- Update page component to use new data structure
2026-01-29 11:02:43 +01:00
Thomas Richter
87bf928c12 feat(02-01): add getOrdered method to repository
- Add getOrdered(options?) method to EntryRepository interface
- Implement filtering for completed entries (showCompleted option)
- Order by type ASC (task before thought), then createdAt DESC
- Import asc, ne operators from drizzle-orm
2026-01-29 11:01:59 +01:00
Thomas Richter
d7c7e9448d feat(01-02): add data directory, server hooks, and verification page
- Create data/ directory with attachments/ subdirectory and .gitkeep files
- Update .gitignore to track .gitkeep but ignore database files
- Server hooks initialize database on first request
- Page server load fetches entries and creates test entry if none exist
- Verification page shows database status, entry count, and recent entries
2026-01-29 04:38:44 +01:00
Thomas Richter
a15dbfd3d8 feat(01-02): create EntryRepository with typed CRUD operations
- Implement EntryRepository interface with create, getById, getAll, update, delete, count
- SQLiteEntryRepository class using Drizzle ORM
- Automatic ID generation with nanoid
- Automatic timestamp management (createdAt, updatedAt)
- Pagination support via limit/offset
- Singleton pattern for consistent access
2026-01-29 04:37:37 +01:00