Architecture
Module map
graph TD Plugin["DailyNotesNGPlugin<br/><i>src/main.ts</i>"] Journals["settings.journals: JournalDefinition[]<br/>name, periodicity, folder, template, scope, owner"]
Plugin --> Journals Plugin --> PM["PeriodicNoteManager<br/>Creates/opens notes per journal"] Plugin --> CV["CalendarView<br/>Month grid sidebar"] Plugin --> Nav["NavigationCommands<br/>Journal picker when multiple match"] PM --> JR["JournalResolver<br/>Filters journals by scope for current device/user"] JR --> DIM["DeviceIdentityManager"] JR --> PNS["PersonNoteService"] JR --> UR["UserRegistry<br/>device-to-user mapping"] JR --> GR["GroupRegistry<br/>group members"] Plugin --> TE["TemplateEngine<br/>built-in vars"] Plugin --> TB["TemplaterBridge<br/>delegates when trigger is on"] Plugin --> TR["TodoRollover"] Plugin --> FT["FrontmatterDateTracker"] Plugin --> DS["DateSuggest<br/>EditorSuggest"]
style Plugin fill:#7c3aed,color:#fff style Journals fill:#2d1854,color:#c4b5fd style JR fill:#5b21b6,color:#fffData flow: creating a periodic note
When a user triggers “Open today’s daily note”:
sequenceDiagram actor User participant Nav as NavigationCommands participant JR as JournalResolver participant Picker as JournalPickerModal participant PM as PeriodicNoteManager participant TE as Template Engine
User->>Nav: Open today's daily note Nav->>JR: getJournalsForPeriodicity('daily') JR-->>Nav: available journals
alt 1 journal Nav->>PM: openPeriodicNote(date, journal) else 2+ journals Nav->>Picker: show picker Picker-->>Nav: user picks one Nav->>PM: openPeriodicNote(date, journal) end
PM->>JR: resolveFolder(journal) Note over JR: Interpolates person in path JR-->>PM: resolved folder path
alt Note exists PM->>PM: ensure .base MOC PM->>User: open note else New note PM->>PM: create folder PM->>TE: process template PM->>PM: add creator + UUID PM->>User: open note endJournal resolution flow
flowchart TD Input["All journals in settings"] --> Enabled{"Enabled?"} Enabled -->|No| Skip["Skip"] Enabled -->|Yes| Scope{"Scope?"} Scope -->|Global| Include["Include"] Scope -->|Person| PersonCheck{"ownerPath matches<br/>current user?"} Scope -->|Group| GroupCheck{"Current user in<br/>group members?"} PersonCheck -->|Yes| Include PersonCheck -->|No| Skip GroupCheck -->|Yes| Include GroupCheck -->|No| Skip Scope -->|Identity disabled| GlobalOnly["Only global journals"] Include --> Output["Available journals<br/>for this device"] GlobalOnly --> Output
style Input fill:#2d1854,color:#c4b5fd style Output fill:#7c3aed,color:#fff style Skip fill:#991b1b,color:#fffStorage layers
| Layer | Where | Synced? | Contains |
|---|---|---|---|
| localStorage | Browser | No | Device ID, device preferences |
| Plugin settings | data.json | Yes | Journal definitions, device-to-user mappings, all config |
| Person note frontmatter | People/Alice.md | Yes | Person preferences, timezone |
| Group note frontmatter | People/Dev Team.md | Yes | Group membership list |
| Note frontmatter | Any .md file | Yes | Note UUID, creator, dates |
.base MOC | Journal folder | Yes | Portable Bases query for folder contents |
Key design decisions
Why named journals instead of one-per-periodicity? Users need multiple daily journals (personal, work, team). The old model forced everything into one folder. Named journals let each context have its own destination.
Why scope-based visibility? In a shared vault, Alice shouldn’t see Bob’s personal journal in her command palette. Scopes ensure each device only shows relevant journals.
Why {{person}} interpolation? For the common case where journals share the same structure per person. Journal/{{person}}/Daily avoids duplicating journal definitions for each user.
Why auto-migration? Users upgrading from the old per-periodicity config shouldn’t lose their settings. The migration converts each enabled periodicity to a global journal.
Why a journal picker instead of per-journal commands? Dynamically registering a command for each journal would clutter the command palette. The picker appears only when disambiguation is needed.