- Never include 'generated by Claude' or similar AI attribution in commit messages. Write commit messages as if the developer wrote them.
- Always confirm which branch you're on before committing. If unsure, run
git branch --show-currentand ask the user before proceeding. - Do not use git worktrees unless explicitly asked. Work on the current branch in the current directory.
FaveDay is a personal daily scoring/journaling application built with Electron. Users rate their days with scores and add notes with tags.
- Frontend: HTML/CSS/JavaScript with Mustache templates
- Backend: Electron main process (Node.js)
- Data Storage: JSON files in user's data directory
- Tag System: Comprehensive caching system for performance
app/js/faveday.js- Main frontend logic, FaveDayApp classapp/main.js- Electron main process, data handlingapp/index.html- UI templates (Mustache)app/css/style.css- Stylingtag-cache.json- Cached tag statistics for performance
- Uses regex
/([#@])\p{L}[\p{L}\d]*/guifor tag detection - Two types: Topic tags (
#tag) and Person tags (@person) - Comprehensive caching system stores:
totalUses- Total usage countavgScore- Average score of entries with this tagtotalScore- Sum of all scores for entries with this taghotness- Day-based weighted score (recent entries weighted higher)firstUsage/lastUsage- Date trackingyearStats- Usage breakdown by yearpeakYear/peakYearCount- Most active yearisPerson- Whether it's a person tagrecentActivity- Used in current/previous year
- Extended tags page with 7 sorting options:
- Usage Count (default)
- Average Score
- Total Score - sums all scores for the tag
- Hotness - day-based weighted scoring combining recency and quality
- A-Z (NEW - alphabetical sorting)
- Oldest (first usage)
- Newest (most recent usage)
- Uses cached tag statistics instead of recalculating
- Integrated sort controls into table header with active state indicators
- Added fallback date calculation for when cache doesn't have date fields yet
- Enhanced tag cache to include firstUsage/lastUsage timestamps
- Purpose: Surface tags that combine recent activity with quality scores AND total engagement
- Methodology: Sum of each individual score weighted by its recency (no averaging)
- Formula:
hotness = sum(score × weight) - Weight per Score:
max(0.01, 0.6^yearsAgo)(extremely aggressive exponential decay)- Current day: weight = 1.0 (full value)
- 1 year ago: weight = 0.6 (60% value)
- 3 years ago: weight = 0.22 (22% value)
- 5 years ago: weight = 0.08 (8% value)
- 10+ years ago: weight = 0.006 (0.6% value, minimum 1%)
- Key Advantage: Consistent long-term activity can compete with single recent mentions
- Example: Tag with 10 old scores of 5.0 (hotness ~5.0) vs tag with 1 recent score of 5.0 (hotness ~5.0)
- Moved sort controls from separate box into table header row for better integration
- Added visual indicators for currently active sort mode (.sort-active CSS class)
- Renamed "First"/"Recent" to "Oldest"/"Newest" for clearer distinction
- Fixed confusing tag-style button highlighting - now uses completely different styling
- Improved active vs passive button contrast (white underline for active, NOT blue background)
- Removed ugly punctuation (colon, dash) and used better whitespace/spacing
- Active buttons use white underline to distinguish from blue tag buttons
- Passive buttons are subtle gray text, avoiding any color conflict with actual tags
- LEARNED: Never use same colors for meta-controls as content elements (tags are blue, so sort controls must be different)
- Avoid fallback code duplication: When tag cache was missing yearStats, removed clunky fallback calculations
- Keep it simple: No complex cache validation logic - user can just edit a score to trigger cache rebuild during development
- Clean data flow: Sparklines now rely purely on properly structured cache data, no duplicate logic
- Reordered bento-grid widgets per user request:
- Year 2025 (calendar progress)
- Age 46 (life progress)
- Coverage
- Avg Score (moved up 2 positions)
- Total Entries (moved up 2 positions)
- Avg Words/Day (moved up 2 positions)
- Last 5+ Score (moved down 2 positions)
- Last 1 Score (moved down 2 positions)
- Top Tag
- Added tiny sparkline charts to tag popups showing usage distribution over years
- Uses existing yearStats data from tag cache system and full year timeline (this.years)
- Shows complete diary timeline: 3px bars for usage years, 2px dots for empty years
- Every tag gets a sparkline covering the full 10+ year timeline for context
- Hover tooltips show "Year: X uses" for all years (including 0 uses)
- Positioned inline between main stats and peak year info
- Bars scale relative to maximum usage year for that tag
- Empty years show as subtle dots (20% opacity) for visual reference
- Usage years show as bars (60% opacity) scaled to 2-12px height
- FIXED: Now readable - you can see which years correspond to which bars/dots
- DO NOT run npm commands - User handles app startup themselves
- Previous session was interrupted by npm command causing stall
- User prefers leveraging existing systems (like tag cache) over reimplementing
- Keep CLAUDE.md updated with learnings and design decisions
- Global functions defined in
faveday.jsaswindow.onFunctionName - Templates use Mustache syntax with partials
- Tag cache automatically updates when scores are saved
- IPC bridge in
preload.jsfor main/renderer communication
Fixed Score Aggregation Issue: Replaced complex async config handling with simple synchronous config store to eliminate async shenanigans and data duplication.
Problem: ScoreCalculator was storing copies of config data via setConfig(), creating multiple sources of truth and potential synchronization issues.
Solution: Created ConfigStore singleton that provides single source of truth for all configuration:
config-store.js: Synchronous config storage loaded once at startup- ScoreCalculator: Now reads directly from
window.configStorewith no local storage - Settings changes: Update configStore directly instead of API -> setConfig chains
- Eliminated: All
setConfig()calls, async config loading in modules, data duplication
Benefits:
- Single source of truth for configuration
- No async complexity in score calculations
- No risk of config synchronization bugs
- Simpler mental model and error-free config access
Successfully extracted core functionality from monolithic FaveDayApp class into focused modules:
-
ScoreCalculator (
/app/lib/faveday/score-calculator.js)- Centralized score calculation system with proper config caching
- Handles average, median, and quality calculations consistently
- Unit tested with 12 passing tests
- Fixed default empty score functionality across all views
-
DataManager (
/app/lib/faveday/data-manager.js)- Centralized data access and persistence operations
- Handles score loading/saving, filtering, and enhancement
- Provides consistent data interface across the application
- Unit tested with 17 passing tests
- Maintains backward compatibility with legacy
this.allaccess patterns
-
Router (
/app/lib/faveday/router.js)- Navigation and URL routing management
- Browser history handling with proper state management
- Route parsing and building utilities
- Breadcrumb generation for better UX
-
SettingsManager (
/app/lib/faveday/settings-manager.js)- User configuration and settings management
- Handles birthdate, score types, life quality weights, default empty scores
- Form validation and DOM manipulation for settings UI
- Unit tested with 7 passing tests
- Properly integrated with other modules via dependency injection
- Single Source of Truth: All score calculations now go through ScoreCalculator
- Consistent Data Access: DataManager provides unified interface for all data operations
- Settings Isolation: All configuration logic centralized in SettingsManager module
- Better Testability: Extracted modules can be unit tested independently (36 total tests)
- Reduced Complexity: Main FaveDayApp class reduced from 3421 to 3318 lines (~3% reduction)
- Easier Maintenance: Clear separation of concerns makes changes safer and more predictable
- Improved Reliability: Centralized systems prevent scattered calculation bugs
- Focused Modules: Each module has a single, clear responsibility
- CSS classes:
.tagfor topics,.personfor people,.recentfor recent activity - Data attributes used for popups:
data-tag,data-uses,data-avg, etc. - Sorting implementations handle null values gracefully
- Templates kept simple, logic in JavaScript