1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
# Phase 3: Calendar Renderer (`src/calendar/calendar-renderer.ts`)
**Status:** Not started
**Depends on:** Phase 2 (calendar-state.ts — reads state for rendering)
**Output file:** `src/calendar/calendar-renderer.ts`
---
## Overview
Pure DOM rendering — replaces `Calendar.svelte` and `obsidian-calendar-ui`. Builds the month grid using Obsidian's `createEl`/`createDiv` helpers. Subscribes to `CalendarState` and re-renders on changes.
---
## Design
```
class CalendarRenderer:
constructor(containerEl: HTMLElement, state: CalendarState, callbacks: CalendarCallbacks)
interface CalendarCallbacks:
onClickDay(date: Moment, event: MouseEvent): void
onClickWeek(date: Moment, event: MouseEvent): void
onClickMonth(date: Moment, event: MouseEvent): void // from fork: click month label
onClickYear(date: Moment, event: MouseEvent): void // from fork: click year label
onClickQuarter(date: Moment, event: MouseEvent): void // from fork: click quarter label
onContextMenuDay(date: Moment, event: MouseEvent): void
onContextMenuWeek(date: Moment, event: MouseEvent): void
onContextMenuMonth(date: Moment, event: MouseEvent): void // from fork
onContextMenuYear(date: Moment, event: MouseEvent): void // from fork
onContextMenuQuarter(date: Moment, event: MouseEvent): void // from fork
onHoverDay(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void
onHoverWeek(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void
onHoverMonth(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void // from fork
onHoverYear(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void // from fork
onHoverQuarter(date: Moment, targetEl: EventTarget, isMetaPressed: boolean): void // from fork
Methods:
- render(): void
Clears containerEl, builds:
- Navigation bar: [<] [Month Year] [>] [Today]
- Month and Year labels are clickable → callbacks.onClickMonth / onClickYear (from fork)
- Quarter label (e.g. Q1) shown if calendarShowQuarter is true (from fork)
- Weekday headers row (Mon, Tue, ...)
- Optional week number column (left or right based on calendarShowWeekNumbersRight — from fork)
- 6 rows × 7 day cells
Each day cell:
- CSS class: "today", "has-note", "active", "other-month"
- Dots container (word count, tasks — uses settings from Phase 5)
- Click handler → callbacks.onClickDay
- Context menu → callbacks.onContextMenuDay
- destroy(): void
Cleanup intervals, event listeners
private:
- renderNavBar(): HTMLElement
- renderDayHeaders(): HTMLElement
- renderWeeks(): HTMLElement
- renderDay(date: Moment): HTMLElement
- renderWeekNumber(date: Moment, position: "left" | "right"): HTMLElement // from fork: position option
- renderQuarterLabel(date: Moment): HTMLElement // from fork
- getDaysInMonthGrid(month: Moment): Moment[][]
Returns 6 rows of 7 days, padding with prev/next month days
```
---
## Notes
- All rendering uses Obsidian's `createEl`/`createDiv` helpers — no innerHTML.
- CSS classes follow the `ai-pulse-calendar-*` naming convention (see Phase 9).
- The renderer needs access to settings (week start, show week numbers, show quarter, words per dot) — these are passed via constructor or a settings reference.
- Word count dots: read the file's cached metadata or content to count words, show N dots where N = floor(wordCount / wordsPerDot), capped at a reasonable max.
|