Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c7b6741f5 | |||
| abf4d6ccf7 | |||
| 141121878f | |||
| 6a89660cf4 |
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "waypoint-sidebar",
|
"id": "waypoint-sidebar",
|
||||||
"name": "Waypoint Sidebar",
|
"name": "Waypoint Sidebar",
|
||||||
"version": "1.4.0",
|
"version": "1.5.1",
|
||||||
"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.4.0",
|
"version": "1.5.1",
|
||||||
"description": "Calendar, recent files, and custom bookmarks sidebar.",
|
"description": "Calendar, recent files, and custom bookmarks sidebar.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -199,6 +199,10 @@ export default class WaypointPlugin extends Plugin {
|
|||||||
const saved = await this.loadData() as Record<string, unknown> | null;
|
const saved = await this.loadData() as Record<string, unknown> | null;
|
||||||
const s = (saved?.settings || {}) as Partial<WaypointSettings>;
|
const s = (saved?.settings || {}) as Partial<WaypointSettings>;
|
||||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, s);
|
this.settings = Object.assign({}, DEFAULT_SETTINGS, s);
|
||||||
|
// Deep merge nested objects that might be missing new fields
|
||||||
|
this.settings.recentFiles = Object.assign({}, DEFAULT_SETTINGS.recentFiles, s.recentFiles || {});
|
||||||
|
this.settings.calendar = Object.assign({}, DEFAULT_SETTINGS.calendar, s.calendar || {});
|
||||||
|
this.settings.display = Object.assign({}, DEFAULT_SETTINGS.display, s.display || {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveSettings(): Promise<void> {
|
async saveSettings(): Promise<void> {
|
||||||
|
|||||||
+69
-2
@@ -1,11 +1,11 @@
|
|||||||
import { Setting, PluginSettingTab, App, Plugin } from 'obsidian';
|
import { Setting, PluginSettingTab, App, Plugin, setIcon } from 'obsidian';
|
||||||
import { WaypointSettings, DEFAULT_SETTINGS, PeriodNoteSettings } from 'src/settings';
|
import { WaypointSettings, DEFAULT_SETTINGS, PeriodNoteSettings } from 'src/settings';
|
||||||
|
|
||||||
export class WaypointSettingTab extends PluginSettingTab {
|
export class WaypointSettingTab extends PluginSettingTab {
|
||||||
private plugin: Plugin;
|
private plugin: Plugin;
|
||||||
private settings: WaypointSettings;
|
private settings: WaypointSettings;
|
||||||
private onSettingsChange: () => void;
|
private onSettingsChange: () => void;
|
||||||
private activeTab: 'calendar' | 'periodic' | 'recent' | 'display' = 'calendar';
|
private activeTab: 'calendar' | 'periodic' | 'recent' | 'display' | 'about' = 'calendar';
|
||||||
|
|
||||||
constructor(app: App, plugin: Plugin, settings: WaypointSettings, onSettingsChange: () => void) {
|
constructor(app: App, plugin: Plugin, settings: WaypointSettings, onSettingsChange: () => void) {
|
||||||
super(app, plugin);
|
super(app, plugin);
|
||||||
@@ -25,6 +25,7 @@ export class WaypointSettingTab extends PluginSettingTab {
|
|||||||
{ key: 'periodic' as const, label: 'Periodic Notes' },
|
{ key: 'periodic' as const, label: 'Periodic Notes' },
|
||||||
{ key: 'recent' as const, label: 'Recent Files' },
|
{ key: 'recent' as const, label: 'Recent Files' },
|
||||||
{ key: 'display' as const, label: 'Display' },
|
{ key: 'display' as const, label: 'Display' },
|
||||||
|
{ key: 'about' as const, label: 'About' },
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const tab of tabs) {
|
for (const tab of tabs) {
|
||||||
@@ -53,6 +54,9 @@ export class WaypointSettingTab extends PluginSettingTab {
|
|||||||
case 'display':
|
case 'display':
|
||||||
this.renderDisplayTab(tabContent);
|
this.renderDisplayTab(tabContent);
|
||||||
break;
|
break;
|
||||||
|
case 'about':
|
||||||
|
this.renderAboutTab(tabContent);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,6 +220,21 @@ export class WaypointSettingTab extends PluginSettingTab {
|
|||||||
this.saveAndRefresh();
|
this.saveAndRefresh();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const filterDesc = new DocumentFragment();
|
||||||
|
filterDesc.appendText('Tags to show as filter pills above the file list. One per line. Leave empty to auto-detect from frontmatter `type` property.');
|
||||||
|
new Setting(container)
|
||||||
|
.setName('Filter tags')
|
||||||
|
.setDesc(filterDesc)
|
||||||
|
.addTextArea((text) => {
|
||||||
|
text.inputEl.setAttr('rows', 4);
|
||||||
|
text.setPlaceholder('meeting\nperson\nproject');
|
||||||
|
text.setValue(this.settings.recentFiles.filterTags.join('\n'));
|
||||||
|
text.inputEl.onblur = () => {
|
||||||
|
this.settings.recentFiles.filterTags = text.getValue().split('\n').filter(t => t.trim());
|
||||||
|
this.saveAndRefresh();
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ═══════════════════════════════
|
// ═══════════════════════════════
|
||||||
@@ -297,4 +316,52 @@ export class WaypointSettingTab extends PluginSettingTab {
|
|||||||
await (this.plugin as any).saveSettings();
|
await (this.plugin as any).saveSettings();
|
||||||
this.onSettingsChange();
|
this.onSettingsChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════
|
||||||
|
// About tab
|
||||||
|
// ═══════════════════════════════
|
||||||
|
|
||||||
|
private renderAboutTab(container: HTMLElement): void {
|
||||||
|
const version = this.plugin.manifest.version;
|
||||||
|
|
||||||
|
const header = container.createDiv();
|
||||||
|
header.style.display = 'flex';
|
||||||
|
header.style.alignItems = 'center';
|
||||||
|
header.style.gap = '12px';
|
||||||
|
header.style.marginBottom = '16px';
|
||||||
|
|
||||||
|
const icon = header.createDiv();
|
||||||
|
icon.style.display = 'flex';
|
||||||
|
icon.style.alignItems = 'center';
|
||||||
|
icon.style.justifyContent = 'center';
|
||||||
|
icon.style.width = '48px';
|
||||||
|
icon.style.height = '48px';
|
||||||
|
icon.style.borderRadius = '12px';
|
||||||
|
icon.style.background = 'var(--interactive-accent)';
|
||||||
|
icon.style.color = 'var(--text-on-accent)';
|
||||||
|
icon.style.fontSize = '24px';
|
||||||
|
setIcon(icon, 'compass');
|
||||||
|
|
||||||
|
const titleGroup = header.createDiv();
|
||||||
|
const title = titleGroup.createEl('h2', { text: 'Waypoint Sidebar' });
|
||||||
|
title.style.margin = '0';
|
||||||
|
title.style.lineHeight = '1.2';
|
||||||
|
const subtitle = titleGroup.createDiv({ text: `v${version}` });
|
||||||
|
subtitle.style.color = 'var(--text-muted)';
|
||||||
|
subtitle.style.fontSize = 'var(--font-ui-small)';
|
||||||
|
|
||||||
|
const desc = container.createDiv();
|
||||||
|
desc.style.marginBottom = '20px';
|
||||||
|
desc.style.lineHeight = '1.6';
|
||||||
|
desc.style.color = 'var(--text-normal)';
|
||||||
|
desc.innerHTML = [
|
||||||
|
`<p><strong>Waypoint</strong> is a sidebar plugin that brings three essential panels into one view:</p>`,
|
||||||
|
`<ul style="padding-left: 20px; margin: 8px 0;">`,
|
||||||
|
`<li><strong>Calendar</strong> — a monthly grid for your periodic notes (daily, weekly, monthly, quarterly, yearly). Click any day, week, or month to open or create the corresponding note.</li>`,
|
||||||
|
`<li><strong>Recent Files</strong> — a list of recently opened files with type filtering, drag-and-drop, and right-click actions.</li>`,
|
||||||
|
`<li><strong>Bookmarks</strong> — custom bookmarks with icons, groups, nesting, and drag-and-drop reordering. Separate from Obsidian's native bookmarks.</li>`,
|
||||||
|
`</ul>`,
|
||||||
|
`<p style="color: var(--text-muted); font-size: var(--font-ui-small);">Made by Olivier. Licensed under MIT.</p>`,
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export interface RecentFilesSettings {
|
|||||||
updateOn: 'file-open' | 'file-edit';
|
updateOn: 'file-open' | 'file-edit';
|
||||||
omittedPaths: string[];
|
omittedPaths: string[];
|
||||||
omittedTags: string[];
|
omittedTags: string[];
|
||||||
|
filterTags: string[]; // tags to show as filter pills (empty = auto-detect from frontmatter)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DisplaySettings {
|
export interface DisplaySettings {
|
||||||
@@ -79,6 +80,7 @@ export const DEFAULT_SETTINGS: WaypointSettings = {
|
|||||||
updateOn: 'file-open',
|
updateOn: 'file-open',
|
||||||
omittedPaths: [],
|
omittedPaths: [],
|
||||||
omittedTags: [],
|
omittedTags: [],
|
||||||
|
filterTags: [],
|
||||||
},
|
},
|
||||||
display: {
|
display: {
|
||||||
rowSize: 26,
|
rowSize: 26,
|
||||||
|
|||||||
@@ -241,14 +241,34 @@ export class WaypointView extends ItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Type filter bar ──
|
// ── Type filter bar ──
|
||||||
const typeCounts: Record<string, number> = {};
|
const configuredTags = this.plugin.settings.recentFiles.filterTags || [];
|
||||||
for (const file of this.plugin.recentFiles) {
|
let typeCounts: Record<string, number> = {};
|
||||||
const tfile = this.app.vault.getAbstractFileByPath(file.path);
|
|
||||||
if (tfile instanceof TFile) {
|
if (configuredTags.length > 0) {
|
||||||
const cache = this.app.metadataCache.getFileCache(tfile);
|
// Use configured tags — count occurrences in recent files
|
||||||
const type = cache?.frontmatter?.type;
|
for (const tag of configuredTags) {
|
||||||
if (type && typeof type === 'string') {
|
typeCounts[tag] = 0;
|
||||||
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
}
|
||||||
|
for (const file of this.plugin.recentFiles) {
|
||||||
|
const tfile = this.app.vault.getAbstractFileByPath(file.path);
|
||||||
|
if (tfile instanceof TFile) {
|
||||||
|
const cache = this.app.metadataCache.getFileCache(tfile);
|
||||||
|
const type = cache?.frontmatter?.type;
|
||||||
|
if (type && typeof type === 'string' && typeCounts.hasOwnProperty(type)) {
|
||||||
|
typeCounts[type]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Auto-detect from frontmatter `type` property
|
||||||
|
for (const file of this.plugin.recentFiles) {
|
||||||
|
const tfile = this.app.vault.getAbstractFileByPath(file.path);
|
||||||
|
if (tfile instanceof TFile) {
|
||||||
|
const cache = this.app.metadataCache.getFileCache(tfile);
|
||||||
|
const type = cache?.frontmatter?.type;
|
||||||
|
if (type && typeof type === 'string') {
|
||||||
|
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +282,9 @@ export class WaypointView extends ItemView {
|
|||||||
this.redraw();
|
this.redraw();
|
||||||
});
|
});
|
||||||
// Type pills
|
// Type pills
|
||||||
const sortedTypes = Object.entries(typeCounts).sort(([,a], [,b]) => b - a);
|
const sortedTypes = configuredTags.length > 0
|
||||||
|
? Object.entries(typeCounts) // preserve configured order
|
||||||
|
: Object.entries(typeCounts).sort(([,a], [,b]) => b - a); // sort by count
|
||||||
for (const [type, count] of sortedTypes) {
|
for (const [type, count] of sortedTypes) {
|
||||||
const pill = filterBar.createSpan({ cls: `waypoint-recent-pill${this.recentFilesFilter === type ? ' is-active' : ''}` });
|
const pill = filterBar.createSpan({ cls: `waypoint-recent-pill${this.recentFilesFilter === type ? ' is-active' : ''}` });
|
||||||
pill.setText(`${type} ${count}`);
|
pill.setText(`${type} ${count}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user