330 lines
15 KiB
Markdown
330 lines
15 KiB
Markdown
# Waypoint Plugin — Implementation Plan
|
|
|
|
## Overview
|
|
|
|
A sidebar plugin for Obsidian that combines three panels into one coherent view: a **periodic note calendar** (day/week/month/quarter/year), a **recent files list**, and a **custom favorites/bookmarks system** with rename, icons, and grouping.
|
|
|
|
---
|
|
|
|
## Features (from requirements)
|
|
|
|
1. **Calendar panel** — shows a month grid with every day clickable
|
|
2. **Period indicators** — day, week, month, quarter, year all displayed and clickable
|
|
3. **Note creation on click** — clicking a period creates a note at a configurable path with a configurable template
|
|
4. **Recent files** — list of recently opened/edited files
|
|
5. **Favorites/Bookmarks** — custom bookmarks (distinct from Obsidian native bookmarks) with:
|
|
- Command: "Add current file as Waypoint bookmark"
|
|
- Rename without renaming the underlying file
|
|
- Assign an icon (from Lucide icons)
|
|
- Right-click context menu: rename, change icon, remove, move
|
|
- Foldable groups and indented hierarchy
|
|
6. **Settings** — configure folder paths and templates for each period type
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
### Approach
|
|
|
|
- **Plain ItemView** (not React) — simpler, matches recent-files-obsidian's approach, avoids extra dependencies
|
|
- **Single view type** `waypoint-view` that contains all three panels stacked vertically
|
|
- **Plugin data** stored via `Plugin.loadData()` / `Plugin.saveData()` for bookmarks and recent files
|
|
- **Settings** via `PluginSettingTab`
|
|
|
|
### Why not React?
|
|
|
|
The daily-note-calendar plugin uses React, but that adds complexity (esbuild config for JSX, react-dom dependency). The recent-files plugin does everything with plain Obsidian DOM helpers (`createDiv`, `setIcon`, etc.) and is much simpler. We can build a clean, native-feeling UI without React. If the calendar grid becomes complex, we can reconsider.
|
|
|
|
### File Structure
|
|
|
|
```
|
|
waypoint/
|
|
├── manifest.json
|
|
├── package.json
|
|
├── tsconfig.json
|
|
├── esbuild.config.mjs
|
|
├── styles.css
|
|
├── src/
|
|
│ ├── main.ts # Plugin entry point
|
|
│ ├── settings.ts # Settings interface + defaults + SettingTab
|
|
│ ├── views/
|
|
│ │ ├── waypoint-view.ts # Main ItemView (orchestrates panels)
|
|
│ │ ├── calendar-panel.ts # Calendar month grid + period indicators
|
|
│ │ ├── recent-files-panel.ts # Recent files list
|
|
│ │ └── favorites-panel.ts # Favorites/bookmarks tree
|
|
│ ├── models/
|
|
│ │ └── bookmark.ts # Bookmark item/group interfaces
|
|
│ ├── services/
|
|
│ │ ├── calendar-service.ts # Date math, period detection, note creation
|
|
│ │ ├── recent-files-service.ts # Track and filter recent files
|
|
│ │ └── favorites-service.ts # CRUD for custom bookmarks
|
|
│ └── utils/
|
|
│ ├── date-utils.ts # Date formatting, period navigation
|
|
│ └── icon-utils.ts # Icon picker modal
|
|
└── README.md
|
|
```
|
|
|
|
---
|
|
|
|
## Component Details
|
|
|
|
### 1. Calendar Panel (`calendar-panel.ts`)
|
|
|
|
**Visual layout (top to bottom):**
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ Q3 · June · 1st, 2026 │ ← Breadcrumb: quarter, month, day (all clickable)
|
|
├─────────────────────────────────────┤
|
|
│ ◀ June 2026 ▶ │ ← Month+Year header with nav
|
|
├─────────────────────────────────────┤
|
|
│ Mon Tue Wed Thu Fri Sat Sun │ ← Day-of-week headers
|
|
├─────────────────────────────────────┤
|
|
│ W22 1 2 3 4 5 6 │ ← Week rows (week number + 7 days)
|
|
│ W23 7 8 9 10 11 12 13 │
|
|
│ W24 14 15 16 17 18 19 20 │
|
|
│ W25 21 22 23 24 25 26 27 │
|
|
│ W26 28 29 30 │
|
|
├─────────────────────────────────────┤
|
|
│ ◀ [Today] ▶ │ ← Today navigation
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
**Behavior:**
|
|
- **Click a day cell** → open or create the daily note for that date
|
|
- **Click week number** → open/create the weekly note
|
|
- **Click breadcrumb quarter** → open/create the quarterly note
|
|
- **Click breadcrumb month** → open/create the monthly note
|
|
- **Click breadcrumb day** → open/create the daily note
|
|
- **Left/right arrows on month header** → navigate months
|
|
- **"Today" button** → jump to current month
|
|
- **Dot indicator** → show which days already have notes
|
|
- **Today highlight** → accent-colored border on current day
|
|
- **Week numbers** displayed as the first column, using ISO week numbering (weeks start on Monday)
|
|
|
|
**Note creation logic:**
|
|
1. Check if a note exists at `{folder}/{nameTemplate}` using the date
|
|
2. If it exists, open it
|
|
3. If not, create it using the configured template file (via Obsidian API)
|
|
4. Apply the `type` property matching the period type (daily-note, weekly-note, etc.)
|
|
|
|
### 2. Recent Files Panel (`recent-files-panel.ts`)
|
|
|
|
Based closely on the recent-files-obsidian plugin:
|
|
- Track file opens/edits via `app.workspace.on('file-open')`
|
|
- Store as ordered list of `{ path, basename }`
|
|
- Display in a `nav-folder` styled list (matches Obsidian's file explorer look)
|
|
- Each item shows:
|
|
- File name (supporting frontmatter-title plugin if available)
|
|
- Hover preview via `hover-link` event
|
|
- Drag support (wikilink insertion)
|
|
- Right-click: "Open in new tab" + Obsidian file menu
|
|
- Middle-click / Ctrl+click: open in new pane
|
|
- X button on hover to remove from list
|
|
- Respect omitted paths/tags filter (from settings)
|
|
- Max length configurable
|
|
|
|
### 3. Favorites / Bookmarks Panel (`favorites-panel.ts`)
|
|
|
|
**Data model:**
|
|
```typescript
|
|
interface BookmarkItem {
|
|
id: string; // unique ID
|
|
type: 'file' | 'group';
|
|
label: string; // display name (can differ from file name)
|
|
filePath: string; // path to the Obsidian file (empty for groups)
|
|
icon: string; // Lucide icon name (default: 'file')
|
|
children: BookmarkItem[]; // for groups
|
|
collapsed: boolean; // group fold state
|
|
indent: number; // indent level (0 = top level)
|
|
}
|
|
```
|
|
|
|
**Visual layout:**
|
|
```
|
|
── Waypoint Bookmarks ────
|
|
📁 Projects ← group (collapsible)
|
|
📄 Fabrique ← file bookmark
|
|
📄 Haro dashboard ← file bookmark
|
|
📁 References ← group
|
|
📄 CSS grid notes
|
|
📄 Inbox ← ungrouped bookmark
|
|
```
|
|
|
|
**Behavior:**
|
|
- **Click file bookmark** → opens the file
|
|
- **Click group** → toggles collapse/expand
|
|
- **Drag to reorder** (stretch goal — use Obsidian's drag system)
|
|
- **Right-click on file bookmark** → context menu:
|
|
- Rename bookmark
|
|
- Change icon (opens icon picker)
|
|
- Remove bookmark
|
|
- Move to group → submenu of existing groups
|
|
- **Right-click on group** → context menu:
|
|
- Rename group
|
|
- Change icon
|
|
- Remove group (and all children)
|
|
- Add bookmark to this group
|
|
- New sub-group
|
|
- **Right-click on empty space** → context menu:
|
|
- New group
|
|
- Add current file
|
|
|
|
**Add current file command:**
|
|
- Command ID: `waypoint:add-bookmark`
|
|
- Picks up the active file
|
|
- Shows a prompt to set the display name (defaults to file's basename)
|
|
- Shows an icon picker (default: file icon)
|
|
- Adds to root level (or active group if one is right-clicked)
|
|
|
|
### 4. Settings Tab (`settings.ts`)
|
|
|
|
```
|
|
┌── Waypoint Settings ──────────────────┐
|
|
│ │
|
|
│ 📅 Calendar │
|
|
│ ┌─ General ─────────────────────────┐ │
|
|
│ │ First day of week [Monday ▼] │ │
|
|
│ │ Week number standard [ISO ▼] │ │
|
|
│ │ Show note indicators [toggle] │ │
|
|
│ └──────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─ Periodic Note Paths ─────────────┐ │
|
|
│ │ Daily note │ │
|
|
│ │ Folder: [periodic/daily ] │ │
|
|
│ │ Template: [templates/daily ] │ │
|
|
│ │ Name: [yyyy-MM-dd ] │ │
|
|
│ │ Type: [daily-note ] │ │
|
|
│ │ │ │
|
|
│ │ Weekly note │ │
|
|
│ │ Folder: [periodic/weekly ] │ │
|
|
│ │ Template: [templates/weekly ] │ │
|
|
│ │ Name: [GGGG-[W]WW ] │ │
|
|
│ │ Type: [weekly-note ] │ │
|
|
│ │ │ │
|
|
│ │ Monthly note │ │
|
|
│ │ Folder: [periodic/monthly ] │ │
|
|
│ │ ... │ │
|
|
│ │ │ │
|
|
│ │ Quarterly note │ │
|
|
│ │ ... │ │
|
|
│ │ │ │
|
|
│ │ Yearly note │ │
|
|
│ │ ... │ │
|
|
│ └──────────────────────────────────┘ │
|
|
│ │
|
|
│ 📄 Recent Files │
|
|
│ ┌──────────────────────────────────┐ │
|
|
│ │ Max items: [50 ] │ │
|
|
│ │ Update on: [File opened ▼] │ │
|
|
│ │ Omitted paths: [textarea ] │ │
|
|
│ │ Omitted tags: [textarea ] │ │
|
|
│ └──────────────────────────────────┘ │
|
|
│ │
|
|
│ ⭐ Favorites │
|
|
│ ┌──────────────────────────────────┐ │
|
|
│ │ (No specific settings yet) │ │
|
|
│ └──────────────────────────────────┘ │
|
|
└────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Steps
|
|
|
|
### Step 1: Project scaffolding
|
|
- Create `manifest.json`, `package.json`, `tsconfig.json`, `esbuild.config.mjs`
|
|
- Install deps: `obsidian`, `builtin-modules`, typescript
|
|
- Create folder structure
|
|
|
|
### Step 2: Settings
|
|
- Define interfaces: `WaypointSettings`, `PeriodNoteSettings`
|
|
- Defaults matching O-vault convention:
|
|
- Daily: `periodic/daily/`, template `Templates/Daily note`, name `yyyy-MM-dd`
|
|
- Weekly: `periodic/weekly/`, template `Templates/Weekly note`, name `GGGG-[W]WW`
|
|
- Monthly: `periodic/monthly/`, template `Templates/Monthly note`, name `yyyy-MM`
|
|
- Quarterly: `periodic/quarterly/`, template `Templates/Quarterly note`, name `yyyy-[Q]Q`
|
|
- Yearly: `periodic/yearly/`, template `Templates/Yearly note`, name `yyyy`
|
|
- Implement `WaypointSettingTab` with sections for each period + recent files
|
|
|
|
### Step 3: Calendar Service
|
|
- `getMonthGrid(year, month, firstDayOfWeek)` → array of weeks with day objects
|
|
- `getPeriodFromDate(date)` → { day, week, month, quarter, year }
|
|
- `findOrCreateNote(period, settings)` → checks vault, creates if missing
|
|
- Date formatting using `moment()` (bundled in Obsidian)
|
|
|
|
### Step 4: Calendar Panel (ItemView section)
|
|
- Render the calendar grid using Obsidian DOM (`createDiv`, `createSpan`)
|
|
- Period header row (year/quarter/week)
|
|
- Month header with nav arrows
|
|
- Day-of-week header row
|
|
- Day cells (with today highlight, note indicators, other-month dimming)
|
|
- Arrow navigation for months
|
|
- Click handlers for each period type
|
|
|
|
### Step 5: Recent Files Service
|
|
- Track `file-open` events
|
|
- Maintain ordered list (max length, dedup)
|
|
- Support omitted paths/tags filtering
|
|
|
|
### Step 6: Recent Files Panel
|
|
- Render the list in `nav-folder` style
|
|
- Click, hover, drag, right-click, middle-click, delete button
|
|
|
|
### Step 7: Favorites Service
|
|
- CRUD operations on `BookmarkItem[]`
|
|
- Persist via `Plugin.loadData/saveData`
|
|
- Add current file command
|
|
- Rename, icon change, remove, grouping
|
|
|
|
### Step 8: Favorites Panel
|
|
- Render tree with collapsible groups
|
|
- Context menus for items and groups
|
|
- Icon display using `setIcon()`
|
|
- Drag support (stretch)
|
|
|
|
### Step 9: Waypoint View (main orchestrator)
|
|
- `ItemView` subclass
|
|
- On open: create three sections inside `contentEl`
|
|
- Redraw on data changes
|
|
- Register as sidebar view type `waypoint-view`
|
|
|
|
### Step 10: Main Plugin
|
|
- `onload()`: load data, register view, register commands, register events, add settings tab
|
|
- Auto-open on right sidebar on first load
|
|
- Commands:
|
|
- `waypoint:open-view` → reveal the waypoint sidebar
|
|
- `waypoint:add-bookmark` → add current file as bookmark
|
|
- `waypoint:go-to-today` → open today's daily note
|
|
- Events:
|
|
- `file-open` → update recent files + redraw
|
|
- `create`, `delete`, `rename` → update calendar note indicators + redraw
|
|
- Midnight check → refresh today highlight
|
|
|
|
---
|
|
|
|
## Key Design Decisions
|
|
|
|
1. **No React** — Using native Obsidian DOM APIs avoids build complexity and keeps the bundle small. The calendar grid is straightforward enough to render imperatively.
|
|
|
|
2. **Flat data storage** — Bookmarks and recent files stored via Plugin.loadData/saveData as JSON. This makes them portable and compatible with Obsidian sync.
|
|
|
|
3. **Separate from native bookmarks** — We never touch `app.internalPlugins.getEnabledPluginById('bookmarks')`. These are entirely custom bookmarks with their own data store.
|
|
|
|
4. **Icon picker** — We'll build a simple icon picker modal using Obsidian's `SuggestModal` or a grid of Lucide icons that are commonly used (file, folder, star, heart, bookmark, flag, pin, etc.). We won't implement a searchable full icon browser initially — a curated selection of ~40 icons plus a text input for any Lucide icon name.
|
|
|
|
5. **O-vault aware** — Default paths match Olivier's vault structure. All settings are overridable.
|
|
|
|
---
|
|
|
|
## Stretch Goals (after MVP)
|
|
|
|
- Drag-and-drop reordering in favorites panel
|
|
- Search/filter within bookmarks
|
|
- Multiple bookmark lists / profiles
|
|
- Sync bookmarks with Obsidian Sync
|
|
- Show note previews on calendar hover
|
|
- Custom CSS classes per bookmark (color coding)
|
|
- Nested groups beyond 2 levels
|
|
- Collapse/expand all in favorites
|
|
- Keyboard shortcuts for bookmark navigation
|