Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb09ec0e83 | |||
| a4714f6fa8 |
@@ -0,0 +1,34 @@
|
|||||||
|
# Waypoint
|
||||||
|
|
||||||
|
Calendar, recent files, and custom bookmarks sidebar for Obsidian.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Calendar panel** — month grid with clickable days, week numbers, period indicators (day/week/month/quarter/year)
|
||||||
|
- **Recent files** — track recently opened/edited files
|
||||||
|
- **Favorites** — custom bookmarks with groups, icons, and rename
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
| Command | Default Hotkey |
|
||||||
|
|---|---|
|
||||||
|
| Go to daily note | `Ctrl+Shift+Alt+D` |
|
||||||
|
| Go to weekly note | `Ctrl+Shift+Alt+W` |
|
||||||
|
| Go to monthly note | `Ctrl+Shift+Alt+M` |
|
||||||
|
| Go to quarterly note | `Ctrl+Shift+Alt+Q` |
|
||||||
|
| Go to yearly note | `Ctrl+Shift+Alt+Y` |
|
||||||
|
| Next/Previous daily/weekly/monthly/quarterly/yearly note | — |
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Via BRAT
|
||||||
|
|
||||||
|
1. Install the [BRAT](https://obsidian.md/plugins?search=brat) plugin
|
||||||
|
2. Add `https://gitea.mithrilforge.dev/olivier/waypoint` as a Beta Plugin
|
||||||
|
3. Enable **Waypoint** in Community Plugins
|
||||||
|
|
||||||
|
### Manual
|
||||||
|
|
||||||
|
1. Download the latest release from [Gitea](https://gitea.mithrilforge.dev/olivier/waypoint/releases)
|
||||||
|
2. Extract `main.js`, `manifest.json`, and `styles.css` to `{vault}/.obsidian/plugins/waypoint/`
|
||||||
|
3. Enable **Waypoint** in Community Plugins
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "waypoint",
|
"id": "waypoint-sidebar",
|
||||||
"name": "Waypoint",
|
"name": "Waypoint Sidebar",
|
||||||
"version": "1.0.0",
|
"version": "1.2.0",
|
||||||
"minAppVersion": "0.16.3",
|
"minAppVersion": "0.16.3",
|
||||||
"description": "Calendar, recent files, and custom bookmarks sidebar.",
|
"description": "Calendar, recent files, and custom bookmarks sidebar.",
|
||||||
"author": "Olivier",
|
"author": "Olivier",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "waypoint",
|
"name": "waypoint",
|
||||||
"version": "1.0.0",
|
"version": "1.2.0",
|
||||||
"description": "Calendar, recent files, and custom bookmarks sidebar.",
|
"description": "Calendar, recent files, and custom bookmarks sidebar.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+122
-2
@@ -78,13 +78,70 @@ export default class WaypointPlugin extends Plugin {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'waypoint-go-to-today',
|
id: 'waypoint-go-to-daily',
|
||||||
name: 'Go to today\'s daily note',
|
name: 'Go to daily note',
|
||||||
|
hotkeys: [{ modifiers: ['Mod', 'Shift', 'Alt'], key: 'd' }],
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
await this.openPeriodNote('day', moment());
|
await this.openPeriodNote('day', moment());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.addCommand({
|
||||||
|
id: 'waypoint-go-to-weekly',
|
||||||
|
name: 'Go to weekly note',
|
||||||
|
hotkeys: [{ modifiers: ['Mod', 'Shift', 'Alt'], key: 'w' }],
|
||||||
|
callback: async () => {
|
||||||
|
await this.openPeriodNote('week', moment());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addCommand({
|
||||||
|
id: 'waypoint-go-to-monthly',
|
||||||
|
name: 'Go to monthly note',
|
||||||
|
hotkeys: [{ modifiers: ['Mod', 'Shift', 'Alt'], key: 'm' }],
|
||||||
|
callback: async () => {
|
||||||
|
await this.openPeriodNote('month', moment());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addCommand({
|
||||||
|
id: 'waypoint-go-to-quarterly',
|
||||||
|
name: 'Go to quarterly note',
|
||||||
|
hotkeys: [{ modifiers: ['Mod', 'Shift', 'Alt'], key: 'q' }],
|
||||||
|
callback: async () => {
|
||||||
|
await this.openPeriodNote('quarter', moment());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addCommand({
|
||||||
|
id: 'waypoint-go-to-yearly',
|
||||||
|
name: 'Go to yearly note',
|
||||||
|
hotkeys: [{ modifiers: ['Mod', 'Shift', 'Alt'], key: 'y' }],
|
||||||
|
callback: async () => {
|
||||||
|
await this.openPeriodNote('year', moment());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Period navigation commands (no hotkeys) ──
|
||||||
|
|
||||||
|
const directions = ['next', 'prev'] as const;
|
||||||
|
const periodLabels = ['daily', 'weekly', 'monthly', 'quarterly', 'yearly'] as const;
|
||||||
|
const directionLabels: Record<string, string> = { next: 'Next', prev: 'Previous' };
|
||||||
|
|
||||||
|
for (const period of periodLabels) {
|
||||||
|
for (const dir of directions) {
|
||||||
|
const id = `waypoint-go-to-${dir}-${period}`;
|
||||||
|
const name = `${directionLabels[dir]} ${period} note`;
|
||||||
|
this.addCommand({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
callback: async () => {
|
||||||
|
await this.navigatePeriodNote(dir);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Events ──
|
// ── Events ──
|
||||||
|
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
@@ -319,6 +376,69 @@ export default class WaypointPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Period note navigation (next/prev from current file) ──
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect the period type from a filename's basename.
|
||||||
|
* Returns the period key and the parsed moment, or null if not a period note.
|
||||||
|
*/
|
||||||
|
private detectPeriodType(basename: string): { period: 'day' | 'week' | 'month' | 'quarter' | 'year'; date: moment.Moment } | null {
|
||||||
|
// YYYY-MM-DD → daily
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(basename)) {
|
||||||
|
return { period: 'day', date: moment(basename, 'YYYY-MM-DD') };
|
||||||
|
}
|
||||||
|
// GGGG-WWW → weekly (e.g. 2026-W24)
|
||||||
|
if (/^\d{4}-W\d{2}$/.test(basename)) {
|
||||||
|
return { period: 'week', date: moment(basename, 'GGGG-[W]WW') };
|
||||||
|
}
|
||||||
|
// YYYY-MM → monthly
|
||||||
|
if (/^\d{4}-\d{2}$/.test(basename)) {
|
||||||
|
return { period: 'month', date: moment(basename, 'YYYY-MM') };
|
||||||
|
}
|
||||||
|
// YYYY-Q# → quarterly
|
||||||
|
if (/^\d{4}-Q[1-4]$/.test(basename)) {
|
||||||
|
return { period: 'quarter', date: moment(basename, 'YYYY-[Q]Q') };
|
||||||
|
}
|
||||||
|
// YYYY → yearly
|
||||||
|
if (/^\d{4}$/.test(basename)) {
|
||||||
|
return { period: 'year', date: moment(basename, 'YYYY') };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the next or previous period note based on the currently active file.
|
||||||
|
*/
|
||||||
|
async navigatePeriodNote(direction: 'next' | 'prev'): Promise<void> {
|
||||||
|
const file = this.app.workspace.getActiveFile();
|
||||||
|
if (!file) {
|
||||||
|
new Notice('No active file');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const detected = this.detectPeriodType(file.basename);
|
||||||
|
if (!detected) {
|
||||||
|
new Notice('Current file is not a periodic note (daily/weekly/monthly/quarterly/yearly)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { period, date } = detected;
|
||||||
|
|
||||||
|
if (!date.isValid()) {
|
||||||
|
new Notice(`Could not parse date from filename: ${file.basename}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const amount = direction === 'next' ? 1 : -1;
|
||||||
|
|
||||||
|
// Map period to moment duration unit
|
||||||
|
const newDate = period === 'quarter'
|
||||||
|
? date.clone().add(amount * 3, 'months')
|
||||||
|
: date.clone().add(amount, `${period}s` as moment.unitOfTime.DurationConstructor);
|
||||||
|
|
||||||
|
await this.openPeriodNote(period, newDate);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Redraw ──
|
// ── Redraw ──
|
||||||
|
|
||||||
private redrawAll(): void {
|
private redrawAll(): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user