From d661466c8109672545b2065efccc49c51f7b3ae7 Mon Sep 17 00:00:00 2001 From: Sayuop Date: Sat, 22 Nov 2025 16:56:25 +0100 Subject: [PATCH] Added Feature - Daily note summary --- src/main.ts | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/src/main.ts b/src/main.ts index c3b6863..48d6d03 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import { Plugin, PluginSettingTab, Setting, + TFile, WorkspaceLeaf, } from 'obsidian'; @@ -224,6 +225,11 @@ export default class FocusTaskPlugin extends Plugin { this.playCompletionSound(); } + // Log to daily note + if (this.settings.logToDaily) { + this.logTaskToDailyNote(task); + } + if (this.activeTaskId === taskId) { this.stopTimer(); this.activeTaskId = null; @@ -541,6 +547,112 @@ export default class FocusTaskPlugin extends Plugin { } } + // ============ Daily Note Logging ============ + + async logTaskToDailyNote(task: FocusTask) { + try { + const dailyNotePath = this.getDailyNotePath(); + const list = this.settings.lists.find(l => l.id === task.list); + + // Format the task entry + const timeDiff = task.actualMinutes - task.estimatedMinutes; + let timeComparison = ''; + if (timeDiff < 0) { + timeComparison = `${Math.abs(timeDiff)}min under estimate ✨`; + } else if (timeDiff > 0) { + timeComparison = `${timeDiff}min over estimate`; + } else { + timeComparison = `exactly on target 🎯`; + } + + const completedTime = new Date(task.completedAt || Date.now()).toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit', + hour12: false + }); + + const taskEntry = `- [x] ${task.text} | ${list?.icon || '📋'} ${list?.name || 'Task'} | ⏱️ ${this.formatTimeHuman(task.actualMinutes)} / ${this.formatTimeHuman(task.estimatedMinutes)} (${timeComparison}) | ✅ ${completedTime}`; + + await this.appendToDailyNote(dailyNotePath, taskEntry); + } catch (e) { + console.error('Failed to log task to daily note:', e); + new Notice('Failed to log task to daily note'); + } + } + + getDailyNotePath(): string { + const now = new Date(); + const format = this.settings.dailyNoteFormat; + + // Simple date formatting + const year = now.getFullYear(); + const month = (now.getMonth() + 1).toString().padStart(2, '0'); + const day = now.getDate().toString().padStart(2, '0'); + + let filename = format + .replace('YYYY', year.toString()) + .replace('MM', month) + .replace('DD', day); + + return `${filename}.md`; + } + + async appendToDailyNote(path: string, content: string) { + const { vault } = this.app; + const heading = this.settings.dailyNoteHeading; + + let file = vault.getAbstractFileByPath(path); + + if (!file) { + // Create the daily note if it doesn't exist + await vault.create(path, `# ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}\n\n${heading}\n\n${content}\n`); + new Notice('📝 Task logged to new daily note'); + return; + } + + if (!(file instanceof TFile)) { + new Notice('Daily note path is not a file'); + return; + } + + // Read existing content + let existingContent = await vault.read(file); + + // Check if heading exists + if (existingContent.includes(heading)) { + // Find the heading and append after it + const headingIndex = existingContent.indexOf(heading); + const afterHeading = headingIndex + heading.length; + + // Find the next heading or end of file + const nextHeadingMatch = existingContent.slice(afterHeading).match(/\n##? /); + let insertPosition: number; + + if (nextHeadingMatch && nextHeadingMatch.index !== undefined) { + // Insert before the next heading + insertPosition = afterHeading + nextHeadingMatch.index; + } else { + // Append to end of file + insertPosition = existingContent.length; + } + + // Insert the new task entry + const before = existingContent.slice(0, insertPosition); + const after = existingContent.slice(insertPosition); + + // Make sure there's a newline before our content + const newContent = before.trimEnd() + '\n' + content + '\n' + after.trimStart(); + + await vault.modify(file, newContent); + } else { + // Add the heading and content at the end + const newContent = existingContent.trimEnd() + '\n\n' + heading + '\n\n' + content + '\n'; + await vault.modify(file, newContent); + } + + new Notice('📝 Task logged to daily note'); + } + // ============ Utilities ============ formatTime(seconds: number): string { @@ -726,6 +838,41 @@ class FocusTaskSettingTab extends PluginSettingTab { await this.plugin.saveAllData(); })); + // Daily Note Integration + containerEl.createEl('h2', { text: '📝 Daily Note Integration' }); + + new Setting(containerEl) + .setName('Log completed tasks to daily note') + .setDesc('When you complete a task, add an entry to your daily note') + .addToggle(toggle => toggle + .setValue(this.plugin.settings.logToDaily) + .onChange(async value => { + this.plugin.settings.logToDaily = value; + await this.plugin.saveAllData(); + })); + + new Setting(containerEl) + .setName('Daily note filename format') + .setDesc('Format for daily note filename (YYYY = year, MM = month, DD = day)') + .addText(text => text + .setPlaceholder('YYYY-MM-DD') + .setValue(this.plugin.settings.dailyNoteFormat) + .onChange(async value => { + this.plugin.settings.dailyNoteFormat = value || 'YYYY-MM-DD'; + await this.plugin.saveAllData(); + })); + + new Setting(containerEl) + .setName('Section heading') + .setDesc('Heading under which completed tasks will be logged') + .addText(text => text + .setPlaceholder('## ⚡ Completed Tasks') + .setValue(this.plugin.settings.dailyNoteHeading) + .onChange(async value => { + this.plugin.settings.dailyNoteHeading = value || '## ⚡ Completed Tasks'; + await this.plugin.saveAllData(); + })); + // Lists Management containerEl.createEl('h2', { text: '📋 Lists' });