feat: persistent recent files history + fix limit enforcement + fix settings data loss

- Recent files are now persisted to disk and survive restarts
- Max items limit is applied on load (in case setting was reduced)
- Omitted paths regex filtering is now wired up in addToRecentFiles
- Fixed saveAndRefresh() overwriting entire plugin data on settings change
- Debounced disk writes (300ms) to avoid excessive saves on rapid opens
This commit is contained in:
2026-06-21 22:36:03 -04:00
parent 993017f669
commit 49c8da9863
7 changed files with 49 additions and 11 deletions
+5 -5
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"id": "waypoint-sidebar", "id": "waypoint-sidebar",
"name": "Waypoint Sidebar", "name": "Waypoint Sidebar",
"version": "1.3.0", "version": "1.4.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
View File
@@ -1,6 +1,6 @@
{ {
"name": "waypoint", "name": "waypoint",
"version": "1.3.0", "version": "1.4.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": {
+38 -1
View File
@@ -16,12 +16,14 @@ import { BookmarkItem, WaypointData } from 'src/models/bookmark';
const DEFAULT_DATA: WaypointData = { const DEFAULT_DATA: WaypointData = {
bookmarks: [], bookmarks: [],
recentFiles: [],
}; };
export default class WaypointPlugin extends Plugin { export default class WaypointPlugin extends Plugin {
public settings: WaypointSettings; public settings: WaypointSettings;
public waypointData: WaypointData; public waypointData: WaypointData;
public recentFiles: { path: string; basename: string }[] = []; public recentFiles: { path: string; basename: string }[] = [];
private recentFilesSaveTimer: ReturnType<typeof setTimeout> | null = null;
async onload(): Promise<void> { async onload(): Promise<void> {
console.debug('Waypoint: loading plugin v' + this.manifest.version); console.debug('Waypoint: loading plugin v' + this.manifest.version);
@@ -206,14 +208,36 @@ 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 d = (saved?.waypointData || {}) as Partial<WaypointData>; const d = (saved?.waypointData || {}) as Partial<WaypointData>;
this.waypointData = Object.assign({}, DEFAULT_DATA, d); this.waypointData = Object.assign({}, DEFAULT_DATA, d);
// Load persisted recent files
this.recentFiles = this.waypointData.recentFiles || [];
// Apply current limit (in case maxItems was reduced since last save)
if (this.recentFiles.length > this.settings.recentFiles.maxItems) {
this.recentFiles = this.recentFiles.slice(0, this.settings.recentFiles.maxItems);
this.waypointData.recentFiles = this.recentFiles;
}
} }
async saveWaypointData(): Promise<void> { async saveWaypointData(): Promise<void> {
// Sync recentFiles into waypointData before saving
this.waypointData.recentFiles = this.recentFiles;
const all = (await this.loadData()) as Record<string, unknown> || {}; const all = (await this.loadData()) as Record<string, unknown> || {};
all.waypointData = this.waypointData; all.waypointData = this.waypointData;
await this.saveData(all); await this.saveData(all);
} }
/**
* Persist recent files to disk (debounced to avoid excessive writes on rapid opens).
*/
persistRecentFiles(): void {
this.waypointData.recentFiles = this.recentFiles;
if (this.recentFilesSaveTimer) clearTimeout(this.recentFilesSaveTimer);
this.recentFilesSaveTimer = setTimeout(() => {
this.saveWaypointData();
}, 300);
}
// ── Recent Files ── // ── Recent Files ──
private onFileOpen(file: TFile): void { private onFileOpen(file: TFile): void {
@@ -225,14 +249,26 @@ export default class WaypointPlugin extends Plugin {
} }
addToRecentFiles(file: TFile): void { addToRecentFiles(file: TFile): void {
// Apply omitted paths filter
if (this.settings.recentFiles.omittedPaths.length > 0) {
for (const pattern of this.settings.recentFiles.omittedPaths) {
try {
if (new RegExp(pattern).test(file.path)) return;
} catch {
// Invalid regex, skip
}
}
}
this.recentFiles = this.recentFiles.filter(f => f.path !== file.path); this.recentFiles = this.recentFiles.filter(f => f.path !== file.path);
this.recentFiles.unshift({ path: file.path, basename: file.basename }); this.recentFiles.unshift({ path: file.path, basename: file.basename });
// Prune // Apply max items limit
if (this.recentFiles.length > this.settings.recentFiles.maxItems) { if (this.recentFiles.length > this.settings.recentFiles.maxItems) {
this.recentFiles = this.recentFiles.slice(0, this.settings.recentFiles.maxItems); this.recentFiles = this.recentFiles.slice(0, this.settings.recentFiles.maxItems);
} }
this.persistRecentFiles();
this.broadcastRedraw(); this.broadcastRedraw();
} }
@@ -241,6 +277,7 @@ export default class WaypointPlugin extends Plugin {
if (entry) { if (entry) {
entry.path = file.path; entry.path = file.path;
entry.basename = (file as TFile).basename || file.name.replace(/\.[^/.]+$/, ''); entry.basename = (file as TFile).basename || file.name.replace(/\.[^/.]+$/, '');
this.persistRecentFiles();
this.broadcastRedraw(); this.broadcastRedraw();
} }
+1
View File
@@ -13,4 +13,5 @@ export interface BookmarkItem {
export interface WaypointData { export interface WaypointData {
bookmarks: BookmarkItem[]; bookmarks: BookmarkItem[];
recentFiles: { path: string; basename: string }[];
} }
+1 -1
View File
@@ -294,7 +294,7 @@ export class WaypointSettingTab extends PluginSettingTab {
} }
private async saveAndRefresh(): Promise<void> { private async saveAndRefresh(): Promise<void> {
await this.plugin.saveData(this.settings); await (this.plugin as any).saveSettings();
this.onSettingsChange(); this.onSettingsChange();
} }
} }
+2 -2
View File
@@ -304,7 +304,7 @@ export class WaypointView extends ItemView {
removeBtn.addEventListener('click', (e) => { removeBtn.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
this.plugin.recentFiles = this.plugin.recentFiles.filter(f => f.path !== file.path); this.plugin.recentFiles = this.plugin.recentFiles.filter(f => f.path !== file.path);
this.plugin.saveSettings(); this.plugin.persistRecentFiles();
this.redraw(); this.redraw();
}); });
setTooltip(navFile, file.path); setTooltip(navFile, file.path);
@@ -384,7 +384,7 @@ export class WaypointView extends ItemView {
} else { } else {
new Notice('Cannot find file'); new Notice('Cannot find file');
this.plugin.recentFiles = this.plugin.recentFiles.filter(f => f.path !== file.path); this.plugin.recentFiles = this.plugin.recentFiles.filter(f => f.path !== file.path);
this.plugin.saveSettings(); this.plugin.persistRecentFiles();
this.redraw(); this.redraw();
} }
} }