Initial commit: Waypoint Obsidian plugin with calendar, recent files, and bookmarks

This commit is contained in:
2026-06-03 20:26:23 -04:00
commit fbdc42b4a3
16 changed files with 3400 additions and 0 deletions
+178
View File
@@ -0,0 +1,178 @@
import { Setting, PluginSettingTab, App, Plugin } from 'obsidian';
import { WaypointSettings, DEFAULT_SETTINGS, PeriodNoteSettings } from 'src/settings';
export class WaypointSettingTab extends PluginSettingTab {
private plugin: Plugin;
private settings: WaypointSettings;
private onSettingsChange: () => void;
constructor(app: App, plugin: Plugin, settings: WaypointSettings, onSettingsChange: () => void) {
super(app, plugin);
this.plugin = plugin;
this.settings = settings;
this.onSettingsChange = onSettingsChange;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
// ── Calendar Section ──
new Setting(containerEl).setHeading().setName('Calendar');
new Setting(containerEl)
.setName('First day of week')
.setDesc('Which day the calendar week starts on.')
.addDropdown((dropdown) => {
dropdown
.addOption('0', 'Sunday')
.addOption('1', 'Monday')
.setValue(String(this.settings.calendar.firstDayOfWeek))
.onChange((value) => {
this.settings.calendar.firstDayOfWeek = parseInt(value, 10);
this.saveAndRefresh();
});
});
new Setting(containerEl)
.setName('Show note indicators')
.setDesc('Show a dot on days that have existing notes.')
.addToggle((toggle) => {
toggle
.setValue(this.settings.calendar.showNoteIndicators)
.onChange((value) => {
this.settings.calendar.showNoteIndicators = value;
this.saveAndRefresh();
});
});
// ── Periodic Note Settings ──
new Setting(containerEl).setHeading().setName('Periodic Note Paths');
this.addPeriodNoteSettings('Daily', this.settings.daily);
this.addPeriodNoteSettings('Weekly', this.settings.weekly);
this.addPeriodNoteSettings('Monthly', this.settings.monthly);
this.addPeriodNoteSettings('Quarterly', this.settings.quarterly);
this.addPeriodNoteSettings('Yearly', this.settings.yearly);
// ── Recent Files Section ──
new Setting(containerEl).setHeading().setName('Recent Files');
new Setting(containerEl)
.setName('Max items')
.setDesc('Maximum number of recent files to track.')
.addText((text) => {
text.inputEl.setAttr('type', 'number');
text.inputEl.setAttr('placeholder', '50');
text.setValue(String(this.settings.recentFiles.maxItems));
text.inputEl.onblur = () => {
const val = parseInt(text.getValue(), 10);
if (!isNaN(val) && val > 0) {
this.settings.recentFiles.maxItems = val;
this.saveAndRefresh();
}
};
});
new Setting(containerEl)
.setName('Update on')
.setDesc('When to add a file to the recent list.')
.addDropdown((dropdown) => {
dropdown
.addOption('file-open', 'File opened')
.addOption('file-edit', 'File changed')
.setValue(this.settings.recentFiles.updateOn)
.onChange((value: 'file-open' | 'file-edit') => {
this.settings.recentFiles.updateOn = value;
this.saveAndRefresh();
});
});
const pathDesc = new DocumentFragment();
pathDesc.appendText('Regex patterns for paths to exclude. One per line.');
new Setting(containerEl)
.setName('Omitted paths')
.setDesc(pathDesc)
.addTextArea((text) => {
text.inputEl.setAttr('rows', 4);
text.setPlaceholder('^archives/\n\\.png$');
text.setValue(this.settings.recentFiles.omittedPaths.join('\n'));
text.inputEl.onblur = () => {
this.settings.recentFiles.omittedPaths = text.getValue().split('\n').filter(p => p.trim());
this.saveAndRefresh();
};
});
const tagDesc = new DocumentFragment();
tagDesc.appendText('Regex patterns for frontmatter tags to exclude. One per line.');
new Setting(containerEl)
.setName('Omitted tags')
.setDesc(tagDesc)
.addTextArea((text) => {
text.inputEl.setAttr('rows', 4);
text.setPlaceholder('ignore\narchive');
text.setValue(this.settings.recentFiles.omittedTags.join('\n'));
text.inputEl.onblur = () => {
this.settings.recentFiles.omittedTags = text.getValue().split('\n').filter(t => t.trim());
this.saveAndRefresh();
};
});
}
private addPeriodNoteSettings(label: string, period: PeriodNoteSettings): void {
new Setting(this.containerEl).setHeading().setName(label);
new Setting(this.containerEl)
.setName('Folder')
.setDesc(`Folder path for ${label.toLowerCase()} notes.`)
.addText((text) => {
text.setPlaceholder('periodic/daily');
text.setValue(period.folder);
text.onChange((value) => {
period.folder = value;
this.saveAndRefresh();
});
});
new Setting(this.containerEl)
.setName('Name format')
.setDesc(`Date format for ${label.toLowerCase()} note filenames (moment.js format).`)
.addText((text) => {
text.setPlaceholder('yyyy-MM-dd');
text.setValue(period.nameFormat);
text.onChange((value) => {
period.nameFormat = value;
this.saveAndRefresh();
});
});
new Setting(this.containerEl)
.setName('Template file')
.setDesc(`Path to the template file (without .md extension).`)
.addText((text) => {
text.setPlaceholder('Templates/Daily note');
text.setValue(period.templateFile);
text.onChange((value) => {
period.templateFile = value;
this.saveAndRefresh();
});
});
new Setting(this.containerEl)
.setName('Type property')
.setDesc(`Value for the 'type' frontmatter property.`)
.addText((text) => {
text.setPlaceholder('daily-note');
text.setValue(period.typeProperty);
text.onChange((value) => {
period.typeProperty = value;
this.saveAndRefresh();
});
});
}
private async saveAndRefresh(): Promise<void> {
await this.plugin.saveData(this.settings);
this.onSettingsChange();
}
}