The Progress Tracker Dashboard (components/ProgressTrackerView.tsx) gives writers a daily analytics hub: circular progress ring, live session timer, streak display, 30-day velocity chart, and a 12-week GitHub-style heatmap. All charts are pure SVG β no external chart library.
features/progressTracker/progressTrackerSlice.ts manages the live session and goal state:
interface ProgressTrackerState {
dailyGoalWords: number; // default 500; clamped β₯ 1
weeklyGoalWords: number; // default 2500
activeSession: WritingSessionLive | null;
streakDays: number; // synced from computeStreak
longestStreak: number;
totalWordsAllTime: number; // running total added by endSession
}
interface WritingSessionLive {
startedAt: number; // Date.now()
startWordCount: number; // snapshot of project total words at session start
}The slice does NOT store writing history β that lives in projectSlice as writingHistory: { date: string; words: number }[].
- Start:
progressTrackerActions.startSession(totalWordCount)β snapshots current word count. - Live timer:
useProgressTrackerViewruns asetInterval(1s)to updatesessionElapsedlocal state. Cleared onendSession. - End:
progressTrackerActions.endSession({ currentWordCount })β calculatesdelta = max(0, current - start), adds tototalWordsAllTime, dispatchesprojectActions.addWritingSession({ startedAt, endedAt, wordsWritten })(via the hook, not the slice directly). - Streak sync: After endSession,
computeStreak(writingHistory)is re-run andprogressTrackerActions.syncStreakis dispatched if the result differs.
export function computeStreak(history: { date: string; words: number }[]):
{ current: number; longest: number }- Filters to entries where
words > 0. - Sorts by date ascending.
- Iterates: consecutive calendar days β increment streak; gap β reset current.
currentis non-zero only if the most-recent entry is today or yesterday.
All charts use pure inline SVG (consistent with existing CharacterGraphView and SceneTimelinePanel):
| Chart | Description |
|---|---|
| ProgressRing | Circular <circle> with strokeDashoffset computed from goal progress |
| VelocityChart | 30-day area chart: <polyline> + <polygon> fill + SVG gradient; role="img" + aria-label with data summary |
| Heatmap | 12Γ7 <rect> cells; 5 intensity colors from #ebedf0 to #216e39; week-columns, day-rows |
| WeeklyBars | 7 mini <rect> bars for current week day-by-day breakdown |
Ctrl+Shift+S β registered in hooks/useGlobalKeyboardShortcuts.ts β toggles start/stop session.
All keys under the progress.* namespace (25 keys) in locales/*/common.json:
progress.title,progress.session.*,progress.daily.*,progress.weekly.*progress.streak.*,progress.velocity.*,progress.heatmap.*,progress.goal.*