feat: add coloured calendar system indicators

Add a selectable single-dot or per-system indicator mode. Date systems now carry configurable colours; results are cached per displayed month and invalidated when paths/settings change. Refine calendar hierarchy, spacing, hover, and today states.
This commit is contained in:
2026-09-07 21:43:21 -04:00
parent 12209155dc
commit 069456799e
9 changed files with 305 additions and 72 deletions
+37 -27
View File
@@ -51,14 +51,14 @@ Detaches all leaves of `WAYPOINT_VIEW_TYPE`.
```typescript
interface WaypointSettings {
calendar: CalendarSettings; // firstDayOfWeek (0=Sun,1=Mon), showNoteIndicators
calendar: CalendarSettings; // week start, indicator visibility/style, daily indicator colour
daily: PeriodNoteSettings;
weekly: PeriodNoteSettings;
monthly: PeriodNoteSettings;
quarterly: PeriodNoteSettings;
yearly: PeriodNoteSettings;
dateSystems: DateSystemSettings[]; // day-scoped systems beyond the daily note, in menu order
recentFiles: RecentFilesSettings; // maxItems, updateOn, omittedPaths[], omittedTags[], filterTags[]
recentFiles: RecentFilesSettings; // maxItems, updateOn, omittedPaths[], omittedTags[], filterTags[]
display: DisplaySettings; // px sizing for bookmark rows, fonts, icons, calendar cells
}
@@ -70,8 +70,10 @@ interface PeriodNoteSettings {
}
interface CalendarSettings {
firstDayOfWeek: number; // 0 = Sunday, 1 = Monday
showNoteIndicators: boolean; // dot on days with existing .md files
firstDayOfWeek: number; // 0 = Sunday, 1 = Monday
showNoteIndicators: boolean; // show or hide all calendar markers
indicatorMode: 'any' | 'systems'; // one neutral dot, or one coloured dot per date system
dailyIndicatorColor: string; // daily's colour in systems mode
}
interface RecentFilesSettings {
@@ -157,24 +159,24 @@ A **date system** is a folder of notes whose filenames begin with a date. Daily
### `DateSystemSettings`
```typescript
interface DateSystemSettings {
id: string; // stable across edits + reordering, so settings rows can key on it
name: string; // menu label, e.g. "Journal"
folder: string; // e.g. "periodic/journal"
nameFormat: string; // moment format string; may contain "{title}"
templateFile: string; // may omit the .md extension
typeProperty: string; // fallback frontmatter `type:` value
icon: string; // Lucide icon name for the menu item
id: string; // stable across edits + reordering, so settings rows can key on it
name: string; // menu label, e.g. "Journal"
folder: string; // e.g. "periodic/journal"
nameFormat: string; // moment format string; may contain "{title}"
templateFile: string; // may omit the .md extension
typeProperty: string; // fallback frontmatter `type:` value
icon: string; // Lucide icon name for the menu item
indicatorColor: string; // calendar dot colour in systems mode
}
```
Configured systems live in `settings.dateSystems`, where array order is menu order. `DEFAULT_DATE_SYSTEM` (in `settings.ts`, typed `Omit<DateSystemSettings, 'id'>`) supplies the field defaults for a newly added row **and** the merge base for saved ones, so a system persisted before a field existed still loads with that field defined. `DEFAULT_SETTINGS.dateSystems` ships two:
| `id` | `name` | Folder | `nameFormat` | Notes per date |
|---|---|---|---|---|
| `journal` | Journal | `periodic/journal` | `YYYY-MM-DD - [Journal]` | one |
| `meetings` | Meeting | `periodic/meetings` | `YYYY-MM-DD - {title}` | many |
| `id` | `name` | Folder | `nameFormat` | Indicator colour | Notes per date |
|---|---|---|---|---|---|
| `journal` | Journal | `periodic/journal` | `YYYY-MM-DD - [Journal]` | green (`#22c55e`) | one |
| `meetings` | Meeting | `periodic/meetings` | `YYYY-MM-DD - {title}` | violet (`#a855f7`) | many |
### Templater folder triggers
@@ -210,21 +212,26 @@ Imports nothing from `'obsidian'` — callers make every moment call and pass th
### Plugin API (`main.ts`)
```typescript
/** One existing note of a date system, with the label the menu should show. */
interface DateSystemNote {
file: TFile;
label: string; // free-text title for many-per-date systems, else the system name
label: string; // free-text title for many-per-date systems, else the system name
}
/** A day-scoped system and the notes it already holds for one date. */
interface DateSystemNotes {
system: DateSystemSettings;
notes: DateSystemNote[]; // sorted by basename; empty when the date has no note
multiple: boolean; // nameFormat carries {title}, i.e. many notes per date
notes: DateSystemNote[];
multiple: boolean; // nameFormat carries {title}
}
interface DateSystemIndicator {
id: string;
name: string;
color: string;
}
dateSystems(): DateSystemSettings[]
findDateSystemNotes(date: moment.Moment): DateSystemNotes[]
getDateSystemIndicators(dates: moment.Moment[]): Map<string, DateSystemIndicator[]>
openDateSystemNote(
system: DateSystemSettings,
date: moment.Moment,
@@ -232,6 +239,8 @@ openDateSystemNote(
): Promise<void>
```
**`getDateSystemIndicators(dates)`** — returns one `{ id, name, color }` marker per configured system with one or more notes on a requested day. The calendar calls it once for the visible month only in `indicatorMode: 'systems'`. It makes one vault pass, caches the result by displayed dates and date-system settings, and invalidates it on markdown create/delete/rename or any Settings change. It does not run on ordinary markdown edits: content edits cannot change a filename-derived indicator.
**`dateSystems()`** — every day-scoped system in menu order: the daily periodic note, synthesized into a `DateSystemSettings` from `settings.daily`, followed by `settings.dateSystems`. The daily note is therefore not a special case in any consumer.
**`findDateSystemNotes(date)`** — **one** vault scan per call, not one per system. It formats each usable system's `before`/`after` affixes for `date` once, then walks the markdown file list a single time, bucketing each file into the system that claims it (`isInFolder` + `matchesSystemName`). Systems whose formats have no date token are omitted rather than offering an unsafe create action. It returns the remaining `DateSystemNotes` in `dateSystems()` order, each bucket sorted by basename. The calendar's day context menu is built from exactly one of these calls per right-click.
@@ -348,7 +357,7 @@ The last section (`waypoint-section:last-child`) gets `margin-top: auto`, pushin
- **Breadcrumb:** Q-label, month name, year — each clickable to open that period note.
- **Nav buttons:** ◀/▶ shift month ±1. "Today" resets to current month.
- **Week number column:** Clicking a week number opens the weekly note for that week's Monday; middle-clicking opens it in a new tab.
- **Day cells:** Left-click opens the daily note, middle-click opens it in a new tab, right-click opens the day context menu (below). `.other-month` dimmed. `.today` has accent border. `.has-note` gets a dot indicator, decided by the synchronous O(1) `plugin.hasNoteForDate(dateStr)` (a `Set` lookup — no per-cell vault scan).
- **Day cells:** Left-click opens the daily note, middle-click opens it in a new tab, right-click opens the day context menu (below). `.other-month` is muted; `.today` has an inset accent ring; hover receives a quiet fill. With `indicatorMode: 'any'`, `.has-note` shows the existing single neutral dot through the O(1) `plugin.hasNoteForDate(dateStr)` lookup. With `indicatorMode: 'systems'`, the view calls `getDateSystemIndicators()` once for the month and renders one tooltip-labelled coloured dot per matching daily/journal/meeting/custom system.
- **Grid generation:** `getMonthGrid(year, month, firstDayOfWeek)` in `date-utils.ts` produces up to 6 weeks, each with 7 `CalendarDay` objects containing `moment`, `dayOfMonth`, `isToday`, `isCurrentMonth`, `isoWeekNumber`.
**Day context menu (right-click):** Built from a single `plugin.findDateSystemNotes(day.date)` call. The first entry is a non-clickable header showing the full date. Then, per system in `dateSystems()` order:
@@ -423,17 +432,18 @@ All classes prefixed with `waypoint-`. Uses Obsidian CSS variables throughout:
- `--font-ui-small`, `--font-ui-medium`, `--font-semibold`, `--font-medium`, `--font-light`
- `--text-muted`, `--text-faint`, `--text-accent`, `--text-on-accent`, `--text-error`
- `--background-modifier-border`, `--background-modifier-active-hover`, `--background-primary`, `--background-secondary`
- `--background-modifier-border`, `--background-modifier-active-hover`, `--background-modifier-hover`, `--background-primary`, `--background-primary-alt`, `--background-secondary`
- `--interactive-accent`
- `--cursor-link` (pointer cursor, with a `pointer` fallback)
Key layout:
- `.waypoint-view` — flex column, `overflow-y: auto`, 8px padding.
- `.waypoint-section``flex-shrink: 0`, 16px bottom margin. Last section gets `margin-top: auto` (pins calendar to bottom).
- `.waypoint-section-header` — uppercase, muted, with bottom border.
- Calendar table — `table-layout: fixed`, `border-collapse: collapse`.
- `.waypoint-day.today` — accent color text + 1px accent border.
- `.waypoint-day.has-note::after`4px dot indicator.
- `.waypoint-calendar` — padded, bordered surface with separated day cells for clear scan lines.
- Calendar table — `table-layout: fixed`, `border-collapse: separate`, 2px horizontal / 3px vertical cell spacing.
- `.waypoint-day.today` — accent text with an inset accent ring, avoiding layout shifts.
- `.waypoint-day.has-note::after`neutral single-dot indicator.
- `.waypoint-day-indicators` / `.waypoint-day-indicator` — compact ordered dot row; `--waypoint-indicator-color` carries each configured system colour.
- `.waypoint-bm-chevron``rotate(-90deg)` on collapsed groups.
- `.waypoint-bm-drop-line` / `.waypoint-bm-drop-below` — 3px accent border for drag indicators.