fix: folder renames no longer destroy bookmarks, plus data.json save race

- onRename remapped paths by exact equality only, so files inside a moved
  folder kept stale paths and the view then permanently deleted their
  bookmarks on next click. Add pure remapRenamedPath() (path-utils.ts,
  obsidian-free so it is unit-testable) and remap recentFiles + the whole
  bookmark tree, folder moves included.
- saveSettings/saveWaypointData each did loadData -> mutate one key ->
  saveData, so two concurrent saves carried a stale snapshot of the other
  key and one silently reverted the other. Both now delegate to persistAll(),
  which writes both keys from memory through a promise-chain mutex.
- data.json is read once in onload instead of twice; period sub-objects are
  deep-merged so old configs pick up new fields.
- detectPeriodType now derives from the configured nameFormat with strict
  moment parsing instead of hardcoded regexes, so custom formats work.
- Merge openPeriodNoteInLeaf into openPeriodNote(period, date, leaf?); stop
  double-appending .md to templateFile; type the caught error as unknown.
- hasNoteForDate is synchronous and O(1) over a maintained basename Set;
  delete dead getNotesForDate.
- Implement the previously dead omittedTags filter and the no-op
  updateOn: 'file-edit' mode (vault modify event).
This commit is contained in:
2026-09-07 20:03:36 -04:00
parent 3c7b6741f5
commit 6a1a044566
2 changed files with 292 additions and 184 deletions
+21
View File
@@ -0,0 +1,21 @@
// ── Path helpers ──
// Pure functions only: no 'obsidian' imports, so this stays unit-testable in plain node.
/**
* Remap a stored vault path after a rename.
*
* Obsidian's `vault.on('rename')` fires for folders as well as files, so a
* stored path can be affected either because it *is* the renamed item or
* because it lives inside a renamed folder.
*
* The nested check requires a `/` boundary, so renaming `notes/foo` leaves
* `notes/foobar.md` untouched.
*
* @returns the updated path, or `null` when `path` is unaffected.
*/
export function remapRenamedPath(path: string, oldPath: string, newPath: string): string | null {
if (!path || !oldPath) return null;
if (path === oldPath) return newPath;
if (path.startsWith(oldPath + '/')) return newPath + path.slice(oldPath.length);
return null;
}