16 Commits

Author SHA1 Message Date
9e35a2603c Updated README.md 2025-11-22 20:48:35 +01:00
f5e4a16aff Bug Fixes - Updated Version v1.0.4 2025-11-22 20:31:57 +01:00
9014f086d6 Bug Fixes 2025-11-22 20:26:48 +01:00
d4f2af179f Bug Fixes 2025-11-22 20:26:18 +01:00
aeb1d62895 Bug fixes 2025-11-22 18:05:01 +01:00
9de2b00a2f Bug Fixes 2025-11-22 18:03:37 +01:00
66652afc90 Bug Fixes 2025-11-22 17:53:41 +01:00
ad5b5e81bb Updated README.md 2025-11-22 17:10:27 +01:00
951e9c5406 Added Feature - Daily note summary 2025-11-22 17:01:41 +01:00
6dc4d2952d Added Feature - Daily note summary 2025-11-22 17:01:12 +01:00
8e10724206 Updated Version 2025-11-22 16:57:32 +01:00
d661466c81 Added Feature - Daily note summary 2025-11-22 16:56:25 +01:00
f634993637 Updated README.md 2025-11-22 16:54:14 +01:00
1a0c37d642 Updated Version 2025-11-22 16:31:15 +01:00
5a33e641b1 Fixed Focus time not adding time properly 2025-11-22 16:28:44 +01:00
7b4e2e674a Updated README.md 2025-11-22 14:59:53 +01:00
7 changed files with 356 additions and 25 deletions

View File

@@ -4,7 +4,7 @@ A powerful task management and focus timer plugin for [Obsidian](https://obsidia
![Focus Task Banner](https://img.shields.io/badge/Obsidian-Plugin-7c3aed?style=for-the-badge&logo=obsidian&logoColor=white) ![Focus Task Banner](https://img.shields.io/badge/Obsidian-Plugin-7c3aed?style=for-the-badge&logo=obsidian&logoColor=white)
![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge) ![License](https://img.shields.io/badge/License-MIT-green?style=for-the-badge)
![Version](https://img.shields.io/badge/Version-1.0.0-blue?style=for-the-badge) ![Version](https://img.shields.io/badge/Version-1.0.4-blue?style=for-the-badge)
## 🎯 Overview ## 🎯 Overview
@@ -224,17 +224,6 @@ npm run dev
ln -s $(pwd) /path/to/vault/.obsidian/plugins/focus-task ln -s $(pwd) /path/to/vault/.obsidian/plugins/focus-task
``` ```
## 📝 Roadmap
- [ ] Integration with Obsidian Tasks plugin
- [ ] Calendar view for scheduled tasks
- [ ] Weekly/Monthly reports
- [ ] Task templates
- [ ] Sync with external task managers
- [ ] Mobile optimizations
- [ ] Task dependencies
- [ ] Time blocking in daily notes
## 📜 License ## 📜 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

144
main.js
View File

@@ -2,7 +2,6 @@
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin if you want to view the source, please visit the github repository of this plugin
*/ */
/* Build version 1.0.1 */
var __defProp = Object.defineProperty; var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -45,7 +44,9 @@ var DEFAULT_SETTINGS = {
{ id: "learning", name: "Learning", color: "#f59e0b", icon: "\u{1F4DA}" } { id: "learning", name: "Learning", color: "#f59e0b", icon: "\u{1F4DA}" }
], ],
autoStartBreak: false, autoStartBreak: false,
tickSoundEnabled: false tickSoundEnabled: false,
// Daily note logging
logToDaily: false
}; };
var DEFAULT_DATA = { var DEFAULT_DATA = {
tasks: [], tasks: [],
@@ -496,6 +497,8 @@ var FocusTaskPlugin = class extends import_obsidian3.Plugin {
this.isBreakMode = false; this.isBreakMode = false;
this.activeTaskId = null; this.activeTaskId = null;
this.pomodoroCount = 0; this.pomodoroCount = 0;
// Focus time tracking (in seconds for accuracy)
this.focusSecondsToday = 0;
// Status bar element // Status bar element
this.statusBarEl = null; this.statusBarEl = null;
} }
@@ -544,8 +547,10 @@ var FocusTaskPlugin = class extends import_obsidian3.Plugin {
const loaded = await this.loadData(); const loaded = await this.loadData();
this.data = Object.assign({}, DEFAULT_DATA, (loaded == null ? void 0 : loaded.data) || {}); this.data = Object.assign({}, DEFAULT_DATA, (loaded == null ? void 0 : loaded.data) || {});
this.settings = Object.assign({}, DEFAULT_SETTINGS, (loaded == null ? void 0 : loaded.settings) || {}); this.settings = Object.assign({}, DEFAULT_SETTINGS, (loaded == null ? void 0 : loaded.settings) || {});
this.focusSecondsToday = (this.data.totalFocusMinutesToday || 0) * 60;
} }
async saveAllData() { async saveAllData() {
this.data.totalFocusMinutesToday = Math.floor(this.focusSecondsToday / 60);
await this.saveData({ await this.saveData({
settings: this.settings, settings: this.settings,
data: this.data data: this.data
@@ -563,6 +568,7 @@ var FocusTaskPlugin = class extends import_obsidian3.Plugin {
} }
this.data.completedToday = 0; this.data.completedToday = 0;
this.data.totalFocusMinutesToday = 0; this.data.totalFocusMinutesToday = 0;
this.focusSecondsToday = 0;
this.data.lastActiveDate = today; this.data.lastActiveDate = today;
this.saveAllData(); this.saveAllData();
} }
@@ -636,6 +642,9 @@ var FocusTaskPlugin = class extends import_obsidian3.Plugin {
if (this.settings.enableSounds) { if (this.settings.enableSounds) {
this.playCompletionSound(); this.playCompletionSound();
} }
if (this.settings.logToDaily) {
this.logTaskToDailyNote(task);
}
if (this.activeTaskId === taskId) { if (this.activeTaskId === taskId) {
this.stopTimer(); this.stopTimer();
this.activeTaskId = null; this.activeTaskId = null;
@@ -667,6 +676,7 @@ var FocusTaskPlugin = class extends import_obsidian3.Plugin {
this.timerInterval = window.setInterval(() => { this.timerInterval = window.setInterval(() => {
this.currentTimerSeconds++; this.currentTimerSeconds++;
task.actualMinutes = Math.floor(this.currentTimerSeconds / 60); task.actualMinutes = Math.floor(this.currentTimerSeconds / 60);
this.focusSecondsToday++;
this.updateStatusBar(); this.updateStatusBar();
this.updateTimerDisplay(); this.updateTimerDisplay();
if (this.currentTimerSeconds === task.estimatedMinutes * 60) { if (this.currentTimerSeconds === task.estimatedMinutes * 60) {
@@ -690,11 +700,13 @@ var FocusTaskPlugin = class extends import_obsidian3.Plugin {
this.isTimerRunning = true; this.isTimerRunning = true;
this.refreshView(); this.refreshView();
this.updateStatusBar(); this.updateStatusBar();
let secondsWorked = 0;
this.timerInterval = window.setInterval(() => { this.timerInterval = window.setInterval(() => {
this.currentTimerSeconds--; this.currentTimerSeconds--;
if (!this.isBreakMode) { if (!this.isBreakMode) {
task.actualMinutes = Math.floor((this.settings.pomodoroWorkMinutes * 60 - this.currentTimerSeconds) / 60); secondsWorked++;
this.data.totalFocusMinutesToday = Math.floor(this.data.totalFocusMinutesToday + 1 / 60); task.actualMinutes = Math.floor(secondsWorked / 60);
this.focusSecondsToday++;
} }
this.updateStatusBar(); this.updateStatusBar();
this.updateTimerDisplay(); this.updateTimerDisplay();
@@ -756,6 +768,7 @@ var FocusTaskPlugin = class extends import_obsidian3.Plugin {
this.currentTimerSeconds--; this.currentTimerSeconds--;
if (task && !this.isBreakMode) { if (task && !this.isBreakMode) {
task.actualMinutes = Math.floor((this.settings.pomodoroWorkMinutes * 60 - this.currentTimerSeconds) / 60); task.actualMinutes = Math.floor((this.settings.pomodoroWorkMinutes * 60 - this.currentTimerSeconds) / 60);
this.focusSecondsToday++;
} }
this.updateStatusBar(); this.updateStatusBar();
this.updateTimerDisplay(); this.updateTimerDisplay();
@@ -870,6 +883,112 @@ var FocusTaskPlugin = class extends import_obsidian3.Plugin {
console.log("Audio not available"); console.log("Audio not available");
} }
} }
// ============ Daily Note Logging ============
async logTaskToDailyNote(task) {
try {
const list = this.settings.lists.find((l) => l.id === task.list);
const timeDiff = task.actualMinutes - task.estimatedMinutes;
let timeComparison = "";
if (timeDiff < 0) {
timeComparison = `${Math.abs(timeDiff)}min under estimate \u2728`;
} else if (timeDiff > 0) {
timeComparison = `${timeDiff}min over estimate`;
} else {
timeComparison = `exactly on target \u{1F3AF}`;
}
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 == null ? void 0 : list.icon) || "\u{1F4CB}"} ${(list == null ? void 0 : list.name) || "Task"} | \u23F1\uFE0F ${this.formatTimeHuman(task.actualMinutes)} / ${this.formatTimeHuman(task.estimatedMinutes)} (${timeComparison}) | \u2705 ${completedTime}`;
await this.appendToDailyNote(taskEntry);
} catch (e) {
console.error("Failed to log task to daily note:", e);
new import_obsidian3.Notice("Failed to log task to daily note. Make sure Daily Notes core plugin is enabled.");
}
}
getDailyNoteSettings() {
var _a, _b, _c;
const dailyNotesPlugin = (_b = (_a = this.app.internalPlugins) == null ? void 0 : _a.plugins) == null ? void 0 : _b["daily-notes"];
if (!(dailyNotesPlugin == null ? void 0 : dailyNotesPlugin.enabled)) {
return null;
}
const settings = (_c = dailyNotesPlugin.instance) == null ? void 0 : _c.options;
return {
folder: (settings == null ? void 0 : settings.folder) || "",
format: (settings == null ? void 0 : settings.format) || "YYYY-MM-DD",
template: (settings == null ? void 0 : settings.template) || ""
};
}
formatDailyNoteDate(format) {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
const day = now.getDate();
let result = format;
result = result.replace(/YYYY/g, year.toString());
result = result.replace(/YY/g, year.toString().slice(-2));
result = result.replace(/MMMM/g, now.toLocaleDateString("en-US", { month: "long" }));
result = result.replace(/MMM/g, now.toLocaleDateString("en-US", { month: "short" }));
result = result.replace(/MM/g, month.toString().padStart(2, "0"));
result = result.replace(/(?<![A-Za-z])M(?![A-Za-z])/g, month.toString());
result = result.replace(/dddd/g, now.toLocaleDateString("en-US", { weekday: "long" }));
result = result.replace(/ddd/g, now.toLocaleDateString("en-US", { weekday: "short" }));
result = result.replace(/DD/g, day.toString().padStart(2, "0"));
result = result.replace(/(?<![A-Za-z])D(?![A-Za-z])/g, day.toString());
return result;
}
async getOrCreateDailyNote() {
const { vault } = this.app;
const dailySettings = this.getDailyNoteSettings();
if (!dailySettings) {
new import_obsidian3.Notice("Daily Notes core plugin is not enabled. Please enable it in Settings \u2192 Core plugins.");
return null;
}
const filename = this.formatDailyNoteDate(dailySettings.format);
const folder = dailySettings.folder ? `${dailySettings.folder}/` : "";
const path = `${folder}${filename}.md`;
let file = vault.getAbstractFileByPath(path);
if (file && file instanceof import_obsidian3.TFile) {
return file;
}
try {
if (dailySettings.folder) {
const folderExists = vault.getAbstractFileByPath(dailySettings.folder);
if (!folderExists) {
await vault.createFolder(dailySettings.folder);
}
}
let content = "";
if (dailySettings.template) {
const templatePath = dailySettings.template.endsWith(".md") ? dailySettings.template : `${dailySettings.template}.md`;
const templateFile = vault.getAbstractFileByPath(templatePath);
if (templateFile && templateFile instanceof import_obsidian3.TFile) {
content = await vault.read(templateFile);
content = content.replace(/{{date}}/g, filename).replace(/{{time}}/g, new Date().toLocaleTimeString()).replace(/{{title}}/g, filename);
}
}
const newFile = await vault.create(path, content);
new import_obsidian3.Notice(`\u{1F4DD} Created daily note: ${filename}`);
return newFile;
} catch (e) {
console.error("Failed to create daily note:", e);
new import_obsidian3.Notice("Failed to create daily note");
return null;
}
}
async appendToDailyNote(content) {
const file = await this.getOrCreateDailyNote();
if (!file) {
return;
}
const { vault } = this.app;
const existingContent = await vault.read(file);
const newContent = existingContent.trimEnd() + "\n" + content + "\n";
await vault.modify(file, newContent);
new import_obsidian3.Notice("\u{1F4DD} Task logged to daily note");
}
// ============ Utilities ============ // ============ Utilities ============
formatTime(seconds) { formatTime(seconds) {
const absSeconds = Math.abs(seconds); const absSeconds = Math.abs(seconds);
@@ -927,7 +1046,7 @@ var FocusTaskPlugin = class extends import_obsidian3.Plugin {
pendingCount: pending.length, pendingCount: pending.length,
completedToday: this.data.completedToday, completedToday: this.data.completedToday,
totalEstimatedMinutes: totalEstimate, totalEstimatedMinutes: totalEstimate,
totalFocusMinutesToday: Math.floor(this.data.totalFocusMinutesToday), totalFocusMinutesToday: Math.floor(this.focusSecondsToday / 60),
streak: this.data.streak, streak: this.data.streak,
pomodorosCompleted: this.data.pomodorosCompleted, pomodorosCompleted: this.data.pomodorosCompleted,
avgAccuracy: Math.round(avgAccuracy * 100) avgAccuracy: Math.round(avgAccuracy * 100)
@@ -977,6 +1096,21 @@ var FocusTaskSettingTab = class extends import_obsidian3.PluginSettingTab {
this.plugin.settings.enableCelebrations = value; this.plugin.settings.enableCelebrations = value;
await this.plugin.saveAllData(); await this.plugin.saveAllData();
})); }));
containerEl.createEl("h2", { text: "\u{1F4DD} Daily Note Integration" });
new import_obsidian3.Setting(containerEl).setName("Log completed tasks to daily note").setDesc("When you complete a task, add an entry to your daily note. Uses the core Daily Notes plugin settings.").addToggle((toggle) => toggle.setValue(this.plugin.settings.logToDaily).onChange(async (value) => {
this.plugin.settings.logToDaily = value;
await this.plugin.saveAllData();
}));
const infoEl = containerEl.createEl("div", { cls: "setting-item-description" });
infoEl.style.marginTop = "-10px";
infoEl.style.marginBottom = "20px";
infoEl.innerHTML = `
<small>
This feature uses the <strong>Daily Notes</strong> core plugin.
Configure your daily note folder, date format, and template in
<em>Settings \u2192 Core plugins \u2192 Daily notes</em>.
</small>
`;
containerEl.createEl("h2", { text: "\u{1F4CB} Lists" }); containerEl.createEl("h2", { text: "\u{1F4CB} Lists" });
this.plugin.settings.lists.forEach((list, index) => { this.plugin.settings.lists.forEach((list, index) => {
new import_obsidian3.Setting(containerEl).setName(`${list.icon} ${list.name}`).addText((text) => text.setValue(list.name).setPlaceholder("List name").onChange(async (value) => { new import_obsidian3.Setting(containerEl).setName(`${list.icon} ${list.name}`).addText((text) => text.setValue(list.name).setPlaceholder("List name").onChange(async (value) => {

View File

@@ -1,7 +1,7 @@
{ {
"id": "focus-task", "id": "focus-task",
"name": "Focus Task", "name": "Focus Task",
"version": "1.0.1", "version": "1.0.4",
"minAppVersion": "0.15.0", "minAppVersion": "0.15.0",
"description": "A Blitzit-inspired task management and focus timer plugin. Plan your day, track time with Pomodoro technique, and crush your tasks with satisfying checkoffs.", "description": "A Blitzit-inspired task management and focus timer plugin. Plan your day, track time with Pomodoro technique, and crush your tasks with satisfying checkoffs.",
"author": "Crib", "author": "Crib",

View File

@@ -1,6 +1,6 @@
{ {
"name": "focus-task", "name": "focus-task",
"version": "1.0.1", "version": "1.0.4",
"description": "A Blitzit-inspired task management and focus timer plugin for Obsidian", "description": "A Blitzit-inspired task management and focus timer plugin for Obsidian",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View File

@@ -4,6 +4,7 @@ import {
Plugin, Plugin,
PluginSettingTab, PluginSettingTab,
Setting, Setting,
TFile,
WorkspaceLeaf, WorkspaceLeaf,
} from 'obsidian'; } from 'obsidian';
@@ -36,6 +37,9 @@ export default class FocusTaskPlugin extends Plugin {
activeTaskId: string | null = null; activeTaskId: string | null = null;
pomodoroCount: number = 0; pomodoroCount: number = 0;
// Focus time tracking (in seconds for accuracy)
private focusSecondsToday: number = 0;
// Status bar element // Status bar element
statusBarEl: HTMLElement | null = null; statusBarEl: HTMLElement | null = null;
@@ -102,9 +106,13 @@ export default class FocusTaskPlugin extends Plugin {
const loaded = await this.loadData(); const loaded = await this.loadData();
this.data = Object.assign({}, DEFAULT_DATA, loaded?.data || {}); this.data = Object.assign({}, DEFAULT_DATA, loaded?.data || {});
this.settings = Object.assign({}, DEFAULT_SETTINGS, loaded?.settings || {}); this.settings = Object.assign({}, DEFAULT_SETTINGS, loaded?.settings || {});
// Initialize seconds from stored minutes
this.focusSecondsToday = (this.data.totalFocusMinutesToday || 0) * 60;
} }
async saveAllData() { async saveAllData() {
// Sync minutes from seconds before saving
this.data.totalFocusMinutesToday = Math.floor(this.focusSecondsToday / 60);
await this.saveData({ await this.saveData({
settings: this.settings, settings: this.settings,
data: this.data, data: this.data,
@@ -126,6 +134,7 @@ export default class FocusTaskPlugin extends Plugin {
// Reset daily stats // Reset daily stats
this.data.completedToday = 0; this.data.completedToday = 0;
this.data.totalFocusMinutesToday = 0; this.data.totalFocusMinutesToday = 0;
this.focusSecondsToday = 0;
this.data.lastActiveDate = today; this.data.lastActiveDate = today;
this.saveAllData(); this.saveAllData();
} }
@@ -216,6 +225,11 @@ export default class FocusTaskPlugin extends Plugin {
this.playCompletionSound(); this.playCompletionSound();
} }
// Log to daily note
if (this.settings.logToDaily) {
this.logTaskToDailyNote(task);
}
if (this.activeTaskId === taskId) { if (this.activeTaskId === taskId) {
this.stopTimer(); this.stopTimer();
this.activeTaskId = null; this.activeTaskId = null;
@@ -259,6 +273,9 @@ export default class FocusTaskPlugin extends Plugin {
this.currentTimerSeconds++; this.currentTimerSeconds++;
task.actualMinutes = Math.floor(this.currentTimerSeconds / 60); task.actualMinutes = Math.floor(this.currentTimerSeconds / 60);
// Track focus time
this.focusSecondsToday++;
// Light update - only timer display, no full refresh // Light update - only timer display, no full refresh
this.updateStatusBar(); this.updateStatusBar();
this.updateTimerDisplay(); this.updateTimerDisplay();
@@ -290,12 +307,17 @@ export default class FocusTaskPlugin extends Plugin {
this.refreshView(); this.refreshView();
this.updateStatusBar(); this.updateStatusBar();
// Track seconds worked for accurate focus time
let secondsWorked = 0;
this.timerInterval = window.setInterval(() => { this.timerInterval = window.setInterval(() => {
this.currentTimerSeconds--; this.currentTimerSeconds--;
if (!this.isBreakMode) { if (!this.isBreakMode) {
task.actualMinutes = Math.floor((this.settings.pomodoroWorkMinutes * 60 - this.currentTimerSeconds) / 60); secondsWorked++;
this.data.totalFocusMinutesToday = Math.floor(this.data.totalFocusMinutesToday + 1/60); task.actualMinutes = Math.floor(secondsWorked / 60);
// Increment focus time by 1 second
this.focusSecondsToday++;
} }
// Light update - only timer display, no full refresh // Light update - only timer display, no full refresh
@@ -379,6 +401,8 @@ export default class FocusTaskPlugin extends Plugin {
this.currentTimerSeconds--; this.currentTimerSeconds--;
if (task && !this.isBreakMode) { if (task && !this.isBreakMode) {
task.actualMinutes = Math.floor((this.settings.pomodoroWorkMinutes * 60 - this.currentTimerSeconds) / 60); task.actualMinutes = Math.floor((this.settings.pomodoroWorkMinutes * 60 - this.currentTimerSeconds) / 60);
// Track focus time
this.focusSecondsToday++;
} }
// Light update - only timer display // Light update - only timer display
this.updateStatusBar(); this.updateStatusBar();
@@ -523,6 +547,161 @@ export default class FocusTaskPlugin extends Plugin {
} }
} }
// ============ Daily Note Logging ============
async logTaskToDailyNote(task: FocusTask) {
try {
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(taskEntry);
} catch (e) {
console.error('Failed to log task to daily note:', e);
new Notice('Failed to log task to daily note. Make sure Daily Notes core plugin is enabled.');
}
}
getDailyNoteSettings(): { folder: string; format: string; template: string } | null {
// Access the core Daily Notes plugin settings
const dailyNotesPlugin = (this.app as any).internalPlugins?.plugins?.['daily-notes'];
if (!dailyNotesPlugin?.enabled) {
return null;
}
const settings = dailyNotesPlugin.instance?.options;
return {
folder: settings?.folder || '',
format: settings?.format || 'YYYY-MM-DD',
template: settings?.template || '',
};
}
formatDailyNoteDate(format: string): string {
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth() + 1;
const day = now.getDate();
// Use placeholders to avoid replacement conflicts
// Replace longer tokens first, use unique placeholders
let result = format;
// Year tokens
result = result.replace(/YYYY/g, year.toString());
result = result.replace(/YY/g, year.toString().slice(-2));
// Month tokens (longer first)
result = result.replace(/MMMM/g, now.toLocaleDateString('en-US', { month: 'long' }));
result = result.replace(/MMM/g, now.toLocaleDateString('en-US', { month: 'short' }));
result = result.replace(/MM/g, month.toString().padStart(2, '0'));
// Only replace standalone M, not part of other tokens
result = result.replace(/(?<![A-Za-z])M(?![A-Za-z])/g, month.toString());
// Day tokens (longer first)
result = result.replace(/dddd/g, now.toLocaleDateString('en-US', { weekday: 'long' }));
result = result.replace(/ddd/g, now.toLocaleDateString('en-US', { weekday: 'short' }));
result = result.replace(/DD/g, day.toString().padStart(2, '0'));
// Only replace standalone D, not part of other tokens
result = result.replace(/(?<![A-Za-z])D(?![A-Za-z])/g, day.toString());
return result;
}
async getOrCreateDailyNote(): Promise<TFile | null> {
const { vault } = this.app;
const dailySettings = this.getDailyNoteSettings();
if (!dailySettings) {
new Notice('Daily Notes core plugin is not enabled. Please enable it in Settings → Core plugins.');
return null;
}
const filename = this.formatDailyNoteDate(dailySettings.format);
const folder = dailySettings.folder ? `${dailySettings.folder}/` : '';
const path = `${folder}${filename}.md`;
// Check if daily note exists
let file = vault.getAbstractFileByPath(path);
if (file && file instanceof TFile) {
return file;
}
// Create the daily note
try {
// Ensure folder exists
if (dailySettings.folder) {
const folderExists = vault.getAbstractFileByPath(dailySettings.folder);
if (!folderExists) {
await vault.createFolder(dailySettings.folder);
}
}
// Check if template exists and use it
let content = '';
if (dailySettings.template) {
const templatePath = dailySettings.template.endsWith('.md')
? dailySettings.template
: `${dailySettings.template}.md`;
const templateFile = vault.getAbstractFileByPath(templatePath);
if (templateFile && templateFile instanceof TFile) {
content = await vault.read(templateFile);
// Replace template variables
content = content
.replace(/{{date}}/g, filename)
.replace(/{{time}}/g, new Date().toLocaleTimeString())
.replace(/{{title}}/g, filename);
}
}
// Create the file
const newFile = await vault.create(path, content);
new Notice(`📝 Created daily note: ${filename}`);
return newFile;
} catch (e) {
console.error('Failed to create daily note:', e);
new Notice('Failed to create daily note');
return null;
}
}
async appendToDailyNote(content: string) {
const file = await this.getOrCreateDailyNote();
if (!file) {
return;
}
const { vault } = this.app;
// Read existing content and append to the end
const existingContent = await vault.read(file);
const newContent = existingContent.trimEnd() + '\n' + content + '\n';
await vault.modify(file, newContent);
new Notice('📝 Task logged to daily note');
}
// ============ Utilities ============ // ============ Utilities ============
formatTime(seconds: number): string { formatTime(seconds: number): string {
@@ -588,7 +767,7 @@ export default class FocusTaskPlugin extends Plugin {
pendingCount: pending.length, pendingCount: pending.length,
completedToday: this.data.completedToday, completedToday: this.data.completedToday,
totalEstimatedMinutes: totalEstimate, totalEstimatedMinutes: totalEstimate,
totalFocusMinutesToday: Math.floor(this.data.totalFocusMinutesToday), totalFocusMinutesToday: Math.floor(this.focusSecondsToday / 60),
streak: this.data.streak, streak: this.data.streak,
pomodorosCompleted: this.data.pomodorosCompleted, pomodorosCompleted: this.data.pomodorosCompleted,
avgAccuracy: Math.round(avgAccuracy * 100), avgAccuracy: Math.round(avgAccuracy * 100),
@@ -708,6 +887,31 @@ class FocusTaskSettingTab extends PluginSettingTab {
await this.plugin.saveAllData(); 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. Uses the core Daily Notes plugin settings.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.logToDaily)
.onChange(async value => {
this.plugin.settings.logToDaily = value;
await this.plugin.saveAllData();
}));
// Show info about Daily Notes plugin
const infoEl = containerEl.createEl('div', { cls: 'setting-item-description' });
infoEl.style.marginTop = '-10px';
infoEl.style.marginBottom = '20px';
infoEl.innerHTML = `
<small>
This feature uses the <strong>Daily Notes</strong> core plugin.
Configure your daily note folder, date format, and template in
<em>Settings → Core plugins → Daily notes</em>.
</small>
`;
// Lists Management // Lists Management
containerEl.createEl('h2', { text: '📋 Lists' }); containerEl.createEl('h2', { text: '📋 Lists' });
@@ -772,4 +976,4 @@ class FocusTaskSettingTab extends PluginSettingTab {
</p> </p>
`; `;
} }
} }

View File

@@ -32,6 +32,8 @@ export interface FocusTaskSettings {
lists: TaskList[]; lists: TaskList[];
autoStartBreak: boolean; autoStartBreak: boolean;
tickSoundEnabled: boolean; tickSoundEnabled: boolean;
// Daily note logging
logToDaily: boolean;
} }
export interface FocusTaskData { export interface FocusTaskData {
@@ -58,6 +60,8 @@ export const DEFAULT_SETTINGS: FocusTaskSettings = {
], ],
autoStartBreak: false, autoStartBreak: false,
tickSoundEnabled: false, tickSoundEnabled: false,
// Daily note logging
logToDaily: false,
}; };
export const DEFAULT_DATA: FocusTaskData = { export const DEFAULT_DATA: FocusTaskData = {
@@ -96,4 +100,4 @@ export const OVERTIME_MESSAGES = [
{ emoji: '💪', message: 'Persistence pays off!' }, { emoji: '💪', message: 'Persistence pays off!' },
{ emoji: '🏃', message: 'Marathon runner!' }, { emoji: '🏃', message: 'Marathon runner!' },
{ emoji: '🔥', message: 'The grind is real!' }, { emoji: '🔥', message: 'The grind is real!' },
]; ];

View File

@@ -1,3 +1,3 @@
{ {
"1.0.1": "0.15.0" "1.0.4": "0.15.0"
} }