import { App, Notice, TFile } from 'obsidian'; import type { Task } from './types'; import { DAILY_NOTE_TASKS_HEADING } from './constants'; // obsidian-daily-notes-interface re-exports moment from Obsidian's bundle. // We import it this way so esbuild treats moment as external. declare const moment: (date?: unknown) => { format(fmt: string): string; }; interface DailyNotesInterface { appHasDailyNotesPluginLoaded(app: App): boolean; getDailyNote( date: ReturnType, notes: Record ): TFile | null; createDailyNote(date: ReturnType): Promise; getAllDailyNotes(): Record; } let dni: DailyNotesInterface | null = null; async function getDni(): Promise { if (!dni) { dni = await import('obsidian-daily-notes-interface') as unknown as DailyNotesInterface; } return dni; } /** * Append a completed task (and its notes) to the `#### Tasks` section * of today's daily note, creating the section and/or the note if needed. */ export async function appendTaskToDailyNote(task: Task, app: App): Promise { const lib = await getDni(); if (!lib.appHasDailyNotesPluginLoaded(app)) { new Notice( 'YAOTP: Daily Notes plugin is not enabled. ' + 'Please enable it in Settings → Core plugins.' ); return; } const all = lib.getAllDailyNotes(); // moment is available as a global in Obsidian's runtime // eslint-disable-next-line @typescript-eslint/no-explicit-any const today = (window as any).moment(); let file: TFile | null = lib.getDailyNote(today, all); if (!file) { file = await lib.createDailyNote(today); } const existing = await app.vault.read(file); const appended = buildAppendedContent(existing, task); await app.vault.modify(file, appended); } function buildAppendedContent(existing: string, task: Task): string { const taskLine = `- [x] ${task.text}`; const notesBlock = task.notes.length > 0 ? '\n\n' + task.notes.join('\n') : ''; const entry = taskLine + notesBlock; const headingIndex = existing.lastIndexOf(DAILY_NOTE_TASKS_HEADING); if (headingIndex === -1) { // Section doesn't exist — append it at the end const base = existing.trimEnd(); return base + (base.length > 0 ? '\n\n' : '') + DAILY_NOTE_TASKS_HEADING + '\n\n' + entry + '\n'; } // Section exists — insert after heading and any existing items const afterHeading = existing.slice(headingIndex + DAILY_NOTE_TASKS_HEADING.length); // Find where the next same-or-higher-level heading starts (if any) const nextHeadingMatch = afterHeading.match(/\n#{1,4} /); const insertionRelative = nextHeadingMatch?.index ?? afterHeading.length; const before = existing.slice(0, headingIndex + DAILY_NOTE_TASKS_HEADING.length); const sectionContent = afterHeading.slice(0, insertionRelative).trimEnd(); const after = afterHeading.slice(insertionRelative); return before + sectionContent + '\n\n' + entry + (after.length > 0 ? '\n' + after : '\n'); }