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:
+37
-27
@@ -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.
|
||||
|
||||
|
||||
@@ -70,6 +70,17 @@
|
||||
- [ ] Note indicator dots show on days with existing .md files
|
||||
- [ ] Today is highlighted with accent border
|
||||
|
||||
## Calendar: System-coloured indicators
|
||||
|
||||
- [ ] Calendar → Indicator style → **Single dot** keeps one neutral dot for an existing date-named note
|
||||
- [ ] Calendar → Indicator style → **Colour by note system** shows a blue dot for Daily, green for Journal, and violet for Meeting by default
|
||||
- [ ] A day holding multiple meeting notes still shows exactly one Meeting dot
|
||||
- [ ] A day holding Daily, Journal, and Meeting notes shows three compact dots in that order
|
||||
- [ ] Hover each coloured dot → its date-system name is identified by a tooltip
|
||||
- [ ] Change Daily/Journal/Meeting indicator colours in Settings → the visible calendar refreshes immediately
|
||||
- [ ] Add a custom date system with its own colour and a matching note → its dot appears in system order
|
||||
- [ ] Create, delete, or rename a date-system note → coloured dots refresh without changing months
|
||||
|
||||
|
||||
## Calendar: Date systems
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ Calendar, recent files, and custom bookmarks sidebar for Obsidian.
|
||||
|
||||
## Features
|
||||
|
||||
- **Calendar panel** — month grid; left-click a day opens its daily note, middle-click opens it in a tab, and right-click reveals every configured note system for that date
|
||||
- **Date systems** — browse or create daily, journal, meeting, and other date-prefixed notes from a single day menu
|
||||
- **Calendar panel** — refined month grid; left-click a day opens its daily note, middle-click opens it in a tab, and right-click reveals every configured note system for that date
|
||||
- **Calendar indicators** — retain a single neutral note dot or show a coloured dot per daily, journal, meeting, or custom date system
|
||||
- **Recent files** — track recently opened/edited files
|
||||
- **Favorites** — custom bookmarks with groups, icons, and rename
|
||||
|
||||
@@ -35,6 +35,15 @@ Configure systems in **Settings → Waypoint Sidebar → Date systems**. Each sy
|
||||
|
||||
Ctrl/Cmd-click a menu entry opens it in a new tab.
|
||||
|
||||
### Calendar indicators
|
||||
|
||||
Under **Settings → Waypoint Sidebar → Calendar**, choose either:
|
||||
|
||||
- **Single dot** — the existing neutral marker for any note whose filename is exactly the date.
|
||||
- **Colour by note system** — one compact coloured dot per configured date system that has a note on that day. Hover a dot to identify its system.
|
||||
|
||||
Daily uses the Calendar tab’s colour picker. Journal, Meeting, and every custom system have their own **Indicator colour** picker under **Date systems**. The defaults are blue for Daily, green for Journal, and violet for Meeting.
|
||||
|
||||
### Templater folder templates
|
||||
|
||||
If a date system uses a Templater folder mapping, leave its Waypoint **Template file** field blank. Waypoint then creates a safe fallback note while Templater owns prompts, scripts, cursors, and rendered content.
|
||||
|
||||
+97
-3
@@ -42,6 +42,13 @@ export interface DateSystemNotes {
|
||||
multiple: boolean;
|
||||
}
|
||||
|
||||
/** One coloured calendar marker for a date system that has a note on a day. */
|
||||
export interface DateSystemIndicator {
|
||||
id: string;
|
||||
name: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export default class WaypointPlugin extends Plugin {
|
||||
public settings: WaypointSettings;
|
||||
public waypointData: WaypointData;
|
||||
@@ -51,6 +58,9 @@ export default class WaypointPlugin extends Plugin {
|
||||
private savePromise: Promise<void> = Promise.resolve();
|
||||
/** Basenames of every markdown file in the vault, for O(1) calendar lookups. */
|
||||
private markdownBasenames: Set<string> = new Set();
|
||||
/** Results for the displayed month; invalidated by markdown-file changes. */
|
||||
private dateSystemIndicators: Map<string, DateSystemIndicator[]> | null = null;
|
||||
private dateSystemIndicatorKey = '';
|
||||
|
||||
async onload(): Promise<void> {
|
||||
console.debug('Waypoint: loading plugin v' + this.manifest.version);
|
||||
@@ -73,6 +83,7 @@ export default class WaypointPlugin extends Plugin {
|
||||
this.settings,
|
||||
() => {
|
||||
this.enforceRecentFilesLimit();
|
||||
this.invalidateDateSystemIndicators();
|
||||
this.redrawAll();
|
||||
},
|
||||
));
|
||||
@@ -247,10 +258,14 @@ export default class WaypointPlugin extends Plugin {
|
||||
}
|
||||
// Cloned, not Object.assign'd in: the defaults array would otherwise be
|
||||
// aliased into the live settings and the settings UI would edit
|
||||
// DEFAULT_SETTINGS itself. Saved entries merge over
|
||||
// DEFAULT_DATE_SYSTEM so older configs pick up fields added since.
|
||||
// DEFAULT_DATE_SYSTEM so older configs pick up fields added since. Known
|
||||
// built-ins use their own defaults too, preserving Journal/Meeting colours
|
||||
// when this field is first introduced.
|
||||
this.settings.dateSystems = Array.isArray(s.dateSystems)
|
||||
? s.dateSystems.map(sys => Object.assign({}, DEFAULT_DATE_SYSTEM, sys))
|
||||
? s.dateSystems.map(sys => {
|
||||
const builtIn = DEFAULT_SETTINGS.dateSystems.find(defaultSystem => defaultSystem.id === sys.id);
|
||||
return Object.assign({}, builtIn || DEFAULT_DATE_SYSTEM, sys);
|
||||
})
|
||||
: DEFAULT_SETTINGS.dateSystems.map(sys => Object.assign({}, sys));
|
||||
}
|
||||
|
||||
@@ -392,6 +407,7 @@ export default class WaypointPlugin extends Plugin {
|
||||
* inside a renamed folder.
|
||||
*/
|
||||
private onRename(file: TAbstractFile, oldPath: string): void {
|
||||
this.invalidateDateSystemIndicators();
|
||||
const indexChanged = this.syncIndexForRename(file, oldPath);
|
||||
let dataChanged = false;
|
||||
|
||||
@@ -427,6 +443,7 @@ export default class WaypointPlugin extends Plugin {
|
||||
private onVaultCreate(file: TAbstractFile): void {
|
||||
if (file instanceof TFile && file.extension === 'md') {
|
||||
this.markdownBasenames.add(file.basename);
|
||||
this.invalidateDateSystemIndicators();
|
||||
}
|
||||
this.broadcastRedraw();
|
||||
}
|
||||
@@ -434,6 +451,7 @@ export default class WaypointPlugin extends Plugin {
|
||||
private onVaultDelete(file: TAbstractFile): void {
|
||||
if (file instanceof TFile && file.extension === 'md') {
|
||||
this.removeFromMarkdownIndex(file.basename, file.path);
|
||||
this.invalidateDateSystemIndicators();
|
||||
}
|
||||
this.broadcastRedraw();
|
||||
}
|
||||
@@ -447,6 +465,12 @@ export default class WaypointPlugin extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
/** Forget a month result when markdown paths or date-system settings change. */
|
||||
private invalidateDateSystemIndicators(): void {
|
||||
this.dateSystemIndicators = null;
|
||||
this.dateSystemIndicatorKey = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a basename from the index, unless another markdown file still
|
||||
* carries it. `path` is excluded from that check because the vault may not
|
||||
@@ -555,6 +579,7 @@ export default class WaypointPlugin extends Plugin {
|
||||
templateFile: periodSettings.templateFile,
|
||||
typeProperty: periodSettings.typeProperty,
|
||||
icon: 'calendar',
|
||||
indicatorColor: this.settings.calendar.dailyIndicatorColor,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -609,6 +634,75 @@ export default class WaypointPlugin extends Plugin {
|
||||
.map(bucket => bucket.result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return one coloured indicator per system with a note on each requested day.
|
||||
*
|
||||
* The day grid calls this once per render. Its result is cached by the
|
||||
* displayed dates and system settings, then invalidated by file changes, so
|
||||
* a 42-cell calendar never repeats the vault scan per cell or per redraw.
|
||||
*/
|
||||
getDateSystemIndicators(dates: moment.Moment[]): Map<string, DateSystemIndicator[]> {
|
||||
const dateStrings = dates.map(date => date.format('YYYY-MM-DD'));
|
||||
const systems = this.dateSystems()
|
||||
.filter(system => formatHasDateToken(system.nameFormat))
|
||||
.map(system => {
|
||||
const parts = splitNameFormat(system.nameFormat);
|
||||
return {
|
||||
system,
|
||||
hasTitle: parts.hasTitle,
|
||||
dates: dates.map((date, index) => ({
|
||||
dateStr: dateStrings[index],
|
||||
before: formatDatePart(date, parts.before),
|
||||
after: formatDatePart(date, parts.after),
|
||||
})),
|
||||
};
|
||||
});
|
||||
const key = JSON.stringify({
|
||||
dates: dateStrings,
|
||||
systems: systems.map(({ system }) => [
|
||||
system.id,
|
||||
system.name,
|
||||
system.folder,
|
||||
system.nameFormat,
|
||||
system.indicatorColor,
|
||||
]),
|
||||
});
|
||||
if (this.dateSystemIndicators && this.dateSystemIndicatorKey === key) {
|
||||
return this.dateSystemIndicators;
|
||||
}
|
||||
|
||||
const matchingIds = new Map<string, Set<string>>();
|
||||
for (const dateStr of dateStrings) matchingIds.set(dateStr, new Set());
|
||||
|
||||
// The only vault traversal: filter a file by system folder first, then
|
||||
// test it against the month's at-most-42 formatted day patterns.
|
||||
for (const file of this.app.vault.getMarkdownFiles()) {
|
||||
for (const entry of systems) {
|
||||
if (!isInFolder(file.path, entry.system.folder)) continue;
|
||||
for (const match of entry.dates) {
|
||||
if (matchesSystemName(file.basename, match.before, match.after, entry.hasTitle)) {
|
||||
matchingIds.get(match.dateStr)?.add(entry.system.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = new Map<string, DateSystemIndicator[]>();
|
||||
for (const dateStr of dateStrings) {
|
||||
const ids = matchingIds.get(dateStr);
|
||||
result.set(dateStr, systems
|
||||
.filter(({ system }) => ids?.has(system.id))
|
||||
.map(({ system }) => ({
|
||||
id: system.id,
|
||||
name: system.name,
|
||||
color: system.indicatorColor,
|
||||
})));
|
||||
}
|
||||
this.dateSystemIndicatorKey = key;
|
||||
this.dateSystemIndicators = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open (creating if needed) the note `system` holds for `date`.
|
||||
*
|
||||
|
||||
@@ -96,6 +96,32 @@ export class WaypointSettingTab extends PluginSettingTab {
|
||||
this.saveAndRefresh();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(container)
|
||||
.setName('Indicator style')
|
||||
.setDesc('Show one neutral dot for any dated note, or one coloured dot for each configured date system.')
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption('any', 'Single dot')
|
||||
.addOption('systems', 'Colour by note system')
|
||||
.setValue(this.settings.calendar.indicatorMode)
|
||||
.onChange((value: 'any' | 'systems') => {
|
||||
this.settings.calendar.indicatorMode = value;
|
||||
this.saveAndRefresh();
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(container)
|
||||
.setName('Daily indicator colour')
|
||||
.setDesc('Colour for daily-note dots when using “Colour by note system”.')
|
||||
.addColorPicker((color) => {
|
||||
color
|
||||
.setValue(this.settings.calendar.dailyIndicatorColor)
|
||||
.onChange((value) => {
|
||||
this.settings.calendar.dailyIndicatorColor = value;
|
||||
this.saveAndRefresh();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ═══════════════════════════════
|
||||
@@ -254,6 +280,18 @@ export class WaypointSettingTab extends PluginSettingTab {
|
||||
'Icon', 'Lucide icon name for the menu item. Browse names at lucide.dev.',
|
||||
system, 'icon', 'book-open',
|
||||
);
|
||||
|
||||
new Setting(container)
|
||||
.setName('Indicator colour')
|
||||
.setDesc('Colour for this system’s dot when using “Colour by note system”.')
|
||||
.addColorPicker((color) => {
|
||||
color
|
||||
.setValue(system.indicatorColor)
|
||||
.onChange((value) => {
|
||||
system.indicatorColor = value;
|
||||
this.saveAndRefresh();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(container)
|
||||
|
||||
@@ -27,6 +27,8 @@ export interface DateSystemSettings {
|
||||
typeProperty: string;
|
||||
/** Lucide icon name for the menu item. */
|
||||
icon: string;
|
||||
/** CSS colour for this system's calendar indicator. */
|
||||
indicatorColor: string;
|
||||
}
|
||||
|
||||
/** Field defaults for a newly added system, and the merge base for saved ones. */
|
||||
@@ -37,11 +39,16 @@ export const DEFAULT_DATE_SYSTEM: Omit<DateSystemSettings, 'id'> = {
|
||||
templateFile: '',
|
||||
typeProperty: '',
|
||||
icon: 'file',
|
||||
indicatorColor: '#64748b',
|
||||
};
|
||||
|
||||
export interface CalendarSettings {
|
||||
firstDayOfWeek: number; // 0=Sunday, 1=Monday
|
||||
showNoteIndicators: boolean;
|
||||
/** Single neutral dot, or a dot per day-scoped date system. */
|
||||
indicatorMode: 'any' | 'systems';
|
||||
/** Colour used by the synthesized daily system in `systems` mode. */
|
||||
dailyIndicatorColor: string;
|
||||
}
|
||||
|
||||
export interface RecentFilesSettings {
|
||||
@@ -78,6 +85,8 @@ export const DEFAULT_SETTINGS: WaypointSettings = {
|
||||
calendar: {
|
||||
firstDayOfWeek: 1, // Monday
|
||||
showNoteIndicators: true,
|
||||
indicatorMode: 'any',
|
||||
dailyIndicatorColor: '#3b82f6',
|
||||
},
|
||||
daily: {
|
||||
folder: 'periodic/daily',
|
||||
@@ -118,6 +127,7 @@ export const DEFAULT_SETTINGS: WaypointSettings = {
|
||||
templateFile: '',
|
||||
typeProperty: 'journal',
|
||||
icon: 'book-open',
|
||||
indicatorColor: '#22c55e',
|
||||
},
|
||||
{
|
||||
id: 'meetings',
|
||||
@@ -127,6 +137,7 @@ export const DEFAULT_SETTINGS: WaypointSettings = {
|
||||
templateFile: '',
|
||||
typeProperty: 'meeting',
|
||||
icon: 'users',
|
||||
indicatorColor: '#a855f7',
|
||||
},
|
||||
],
|
||||
recentFiles: {
|
||||
|
||||
@@ -181,6 +181,14 @@ export class WaypointView extends ItemView {
|
||||
this.plugin.settings.calendar.firstDayOfWeek,
|
||||
);
|
||||
|
||||
const systemIndicators = this.plugin.settings.calendar.showNoteIndicators
|
||||
&& this.plugin.settings.calendar.indicatorMode === 'systems'
|
||||
? this.plugin.getDateSystemIndicators(weeks.reduce<moment.Moment[]>(
|
||||
(dates, week) => dates.concat(week.days.map(day => day.date)),
|
||||
[],
|
||||
))
|
||||
: null;
|
||||
|
||||
for (const week of weeks) {
|
||||
const row = tbody.createEl('tr');
|
||||
|
||||
@@ -211,8 +219,20 @@ export class WaypointView extends ItemView {
|
||||
|
||||
if (this.plugin.settings.calendar.showNoteIndicators) {
|
||||
const dateStr = day.date.format('YYYY-MM-DD');
|
||||
if (this.plugin.hasNoteForDate(dateStr)) {
|
||||
cell.addClass('has-note');
|
||||
if (this.plugin.settings.calendar.indicatorMode === 'any') {
|
||||
if (this.plugin.hasNoteForDate(dateStr)) {
|
||||
cell.addClass('has-note');
|
||||
}
|
||||
} else {
|
||||
const indicators = systemIndicators?.get(dateStr) || [];
|
||||
if (indicators.length > 0) {
|
||||
const dots = cell.createDiv({ cls: 'waypoint-day-indicators' });
|
||||
for (const indicator of indicators) {
|
||||
const dot = dots.createSpan({ cls: 'waypoint-day-indicator' });
|
||||
dot.style.setProperty('--waypoint-indicator-color', indicator.color);
|
||||
setTooltip(dot, indicator.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+60
-20
@@ -58,29 +58,34 @@
|
||||
|
||||
.waypoint-calendar {
|
||||
font-size: var(--font-ui-small);
|
||||
padding: 8px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 10px;
|
||||
background: var(--background-primary-alt);
|
||||
}
|
||||
|
||||
.waypoint-calendar-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.waypoint-calendar-breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
font-size: var(--font-ui-medium);
|
||||
font-weight: var(--font-semibold);
|
||||
padding: 4px 0;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.waypoint-calendar-breadcrumb .waypoint-clickable {
|
||||
color: var(--text-muted);
|
||||
cursor: var(--cursor-link, pointer);
|
||||
padding: 1px 4px;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
transition: color 80ms, background-color 80ms;
|
||||
}
|
||||
|
||||
.waypoint-calendar-breadcrumb .waypoint-clickable:hover {
|
||||
@@ -90,23 +95,25 @@
|
||||
|
||||
.waypoint-calendar .waypoint-separator {
|
||||
color: var(--text-faint);
|
||||
margin: 0 2px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.waypoint-calendar-today-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.waypoint-calendar-today-group button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: var(--cursor-link, pointer);
|
||||
padding: 2px 6px;
|
||||
padding: 2px 5px;
|
||||
border-radius: 4px;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-ui-small);
|
||||
transition: color 80ms, background-color 80ms;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.waypoint-calendar-today-group button:hover {
|
||||
@@ -121,30 +128,34 @@
|
||||
.waypoint-calendar table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-collapse: separate;
|
||||
border-spacing: 2px 3px;
|
||||
}
|
||||
|
||||
.waypoint-calendar th,
|
||||
.waypoint-calendar td {
|
||||
text-align: center;
|
||||
padding: 4px 2px;
|
||||
padding: 0;
|
||||
font-size: var(--font-ui-small);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.waypoint-calendar th {
|
||||
padding-bottom: 2px;
|
||||
font-weight: var(--font-medium);
|
||||
color: var(--text-faint);
|
||||
font-size: calc(var(--font-ui-small) * 0.85);
|
||||
font-size: calc(var(--font-ui-small) * 0.78);
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.waypoint-calendar .waypoint-weeknum {
|
||||
font-size: calc(var(--font-ui-small) * 0.75);
|
||||
width: 18px;
|
||||
font-size: calc(var(--font-ui-small) * 0.72);
|
||||
color: var(--text-faint);
|
||||
font-weight: var(--font-light);
|
||||
cursor: var(--cursor-link, pointer);
|
||||
padding: 2px 0;
|
||||
border-radius: 4px;
|
||||
transition: color 80ms, background-color 80ms;
|
||||
}
|
||||
|
||||
.waypoint-calendar .waypoint-weeknum:hover {
|
||||
@@ -154,42 +165,71 @@
|
||||
|
||||
.waypoint-calendar .waypoint-day {
|
||||
cursor: var(--cursor-link, pointer);
|
||||
min-height: var(--wp-cal-cell-size, 32px);
|
||||
padding: 2px 0;
|
||||
height: var(--wp-cal-cell-size, 32px);
|
||||
box-sizing: border-box;
|
||||
padding: 2px 0 7px;
|
||||
border-radius: 6px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
transition: color 80ms, background-color 80ms, box-shadow 80ms;
|
||||
}
|
||||
|
||||
.waypoint-calendar .waypoint-day:hover {
|
||||
background-color: var(--background-modifier-active-hover);
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.waypoint-calendar .waypoint-day.other-month {
|
||||
opacity: 0.35;
|
||||
color: var(--text-faint);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.waypoint-calendar .waypoint-day.today {
|
||||
color: var(--text-accent);
|
||||
border: 1px solid var(--text-accent);
|
||||
font-weight: var(--font-semibold);
|
||||
box-shadow: inset 0 0 0 1px var(--text-accent);
|
||||
}
|
||||
|
||||
.waypoint-calendar .waypoint-day.today:hover {
|
||||
background-color: var(--background-modifier-active-hover);
|
||||
}
|
||||
|
||||
/* Single-dot mode preserves the pre-date-systems indicator. */
|
||||
.waypoint-calendar .waypoint-day.has-note::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 3px;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--text-faint);
|
||||
margin: 0 auto;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.waypoint-calendar .waypoint-day.today.has-note::after {
|
||||
background-color: var(--text-accent);
|
||||
}
|
||||
|
||||
/* Colour-by-system mode: one dot per matching system, in Settings order. */
|
||||
.waypoint-day-indicators {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 3px;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.waypoint-day-indicator {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--waypoint-indicator-color);
|
||||
box-shadow: 0 0 0 1px var(--background-primary);
|
||||
}
|
||||
|
||||
/* ── Recent Files ── */
|
||||
|
||||
.waypoint-recent-filter {
|
||||
|
||||
Reference in New Issue
Block a user