feat: support daily note path as due date/start date

This commit is contained in:
Quorafind 2025-04-28 12:02:48 +08:00
parent e64d56cd1b
commit edce8bf1b6
11 changed files with 229 additions and 42 deletions

View file

@ -52,6 +52,7 @@
"typescript": "4.7.3"
},
"dependencies": {
"date-fns": "^4.1.0",
"localforage": "^1.10.0"
}
}

View file

@ -8,6 +8,9 @@ importers:
.:
dependencies:
date-fns:
specifier: ^4.1.0
version: 4.1.0
localforage:
specifier: ^1.10.0
version: 1.10.0
@ -778,6 +781,9 @@ packages:
resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==}
engines: {node: '>=12'}
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
debug@4.4.0:
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'}
@ -2959,6 +2965,8 @@ snapshots:
whatwg-mimetype: 3.0.0
whatwg-url: 11.0.0
date-fns@4.1.0: {}
debug@4.4.0:
dependencies:
ms: 2.1.3

View file

@ -267,8 +267,9 @@ export interface TaskProgressBarSettings {
// Index Related
useDailyNotePathAsDate: boolean;
dailyNotePathFormat: string;
dailyNoteFormat: string;
useAsDateType: "due" | "start" | "scheduled";
dailyNotePath: string;
preferMetadataFormat: "dataview" | "tasks";
// View Settings (Updated Structure)
@ -443,8 +444,9 @@ export const DEFAULT_SETTINGS: TaskProgressBarSettings = {
// Index Related Defaults
useDailyNotePathAsDate: false,
dailyNotePathFormat: "YYYY-MM-DD",
dailyNoteFormat: "yyyy-MM-dd",
useAsDateType: "due",
dailyNotePath: "",
preferMetadataFormat: "tasks",
// View Defaults (Updated Structure)

View file

@ -140,6 +140,17 @@ export class TagSuggest extends CustomSuggest {
}
}
export class SingleFolderSuggest extends CustomSuggest {
constructor(
app: App,
inputEl: HTMLInputElement,
plugin: TaskProgressBarPlugin
) {
const folders = app.vault.getAllFolders();
const paths = folders.map((file) => file.path);
super(app, inputEl, ["/", ...paths]);
}
}
/**
* PathSuggest - Provides autocomplete for file paths
*/

View file

@ -27,7 +27,11 @@ import {
import { formatProgressText } from "./editor-ext/progressBarWidget";
import "./styles/setting.css";
import { ViewConfigModal } from "./components/ViewConfigModal";
import { ImageSuggest } from "./components/AutoComplete";
import {
FolderSuggest,
ImageSuggest,
SingleFolderSuggest,
} from "./components/AutoComplete";
export class TaskProgressBarSettingTab extends PluginSettingTab {
plugin: TaskProgressBarPlugin;
@ -2620,17 +2624,40 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
});
if (this.plugin.settings.useDailyNotePathAsDate) {
const descFragment = document.createDocumentFragment();
descFragment.createEl("div", {
text: t(
"Task Genius will use moment.js and also this format to parse the daily note path."
),
});
descFragment.createEl("div", {
text: t(
"You need to set `yyyy` instead of `YYYY` in the format string. And `dd` instead of `DD`."
),
});
new Setting(containerEl)
.setName(t("Daily note path format"))
.setDesc(
t(
"Task Genius will use moment.js and also this format to parse the daily note path."
)
)
.setName(t("Daily note format"))
.setDesc(descFragment)
.addText((text) => {
text.setValue(this.plugin.settings.dailyNotePathFormat);
text.setValue(this.plugin.settings.dailyNoteFormat);
text.onChange((value) => {
this.plugin.settings.dailyNotePathFormat = value;
this.plugin.settings.dailyNoteFormat = value;
this.applySettingsUpdate();
});
});
new Setting(containerEl)
.setName(t("Daily note path"))
.setDesc(t("Select the folder that contains the daily note."))
.addText((text) => {
new SingleFolderSuggest(
this.app,
text.inputEl,
this.plugin
);
text.setValue(this.plugin.settings.dailyNotePath);
text.onChange((value) => {
this.plugin.settings.dailyNotePath = value;
this.applySettingsUpdate();
});
});

View file

@ -12,6 +12,12 @@ interface FilterOptions {
// Add other potential options needed by specific views later
// selectedProject?: string;
// selectedTags?: string[];
settings?: {
useDailyNotePathAsDate: boolean;
dailyNoteFormat: string;
useAsDateType: "due" | "start" | "scheduled";
};
}
/**

View file

@ -1199,29 +1199,55 @@ export class TaskManager extends Component {
// 6. Start Date
if (formattedStartDate) {
metadata.push(
useDataviewFormat
? `[start:: ${formattedStartDate}]`
: `🛫 ${formattedStartDate}`
);
// Check if this date should be skipped based on useAsDateType
if (
!(
updatedTask.useAsDateType === "start" &&
formatDate(originalTask.startDate) ===
formattedStartDate
)
) {
metadata.push(
useDataviewFormat
? `[start:: ${formattedStartDate}]`
: `🛫 ${formattedStartDate}`
);
}
}
// 7. Scheduled Date
if (formattedScheduledDate) {
metadata.push(
useDataviewFormat
? `[scheduled:: ${formattedScheduledDate}]`
: `${formattedScheduledDate}`
);
// Check if this date should be skipped based on useAsDateType
if (
!(
updatedTask.useAsDateType === "scheduled" &&
formatDate(originalTask.scheduledDate) ===
formattedScheduledDate
)
) {
metadata.push(
useDataviewFormat
? `[scheduled:: ${formattedScheduledDate}]`
: `${formattedScheduledDate}`
);
}
}
// 8. Due Date
if (formattedDueDate) {
metadata.push(
useDataviewFormat
? `[due:: ${formattedDueDate}]`
: `📅 ${formattedDueDate}`
);
// Check if this date should be skipped based on useAsDateType
if (
!(
updatedTask.useAsDateType === "due" &&
formatDate(originalTask.dueDate) === formattedDueDate
)
) {
metadata.push(
useDataviewFormat
? `[due:: ${formattedDueDate}]`
: `📅 ${formattedDueDate}`
);
}
}
// 9. Completion Date (only if completed)

View file

@ -54,20 +54,7 @@ export interface Task {
actualTime?: number;
/** File statistics and metadata for auto-date extraction */
fileStats?: {
/** File name without extension */
fileName?: string;
/** File creation timestamp */
created?: number;
/** File last modified timestamp */
modified?: number;
/** Extracted date from file name (for daily notes) */
fileDate?: number;
/** Whether this file is a daily note */
isDailyNote?: boolean;
/** Custom date format used in the file */
dateFormat?: string;
};
useAsDateType?: "due" | "start" | "scheduled";
}
/** High-performance cache structure for tasks */

View file

@ -12,6 +12,7 @@ import {
ErrorResult,
BatchIndexResult, // Keep if batch processing is still used
} from "./TaskIndexWorkerMessage";
import { parse } from "date-fns/parse";
// --- Define Regexes similar to TaskParser ---
@ -544,6 +545,55 @@ function parseTasksFromContent(
buildTaskHierarchy(tasks); // Call hierarchy builder if needed
return tasks;
}
/**
* Extract date from file path
*/
function extractDateFromPath(
filePath: string,
settings: {
useDailyNotePathAsDate: boolean;
dailyNoteFormat: string;
dailyNotePath: string;
}
): number | undefined {
if (!settings.useDailyNotePathAsDate) return undefined;
// Remove file extension first
let pathToMatch = filePath.replace(/\.[^/.]+$/, "");
// If dailyNotePath is specified, remove it from the path
if (
settings.dailyNotePath &&
pathToMatch.startsWith(settings.dailyNotePath)
) {
pathToMatch = pathToMatch.substring(settings.dailyNotePath.length);
// Remove leading slash if present
if (pathToMatch.startsWith("/")) {
pathToMatch = pathToMatch.substring(1);
}
}
// Try to match with the current path
let dateFromPath = parse(pathToMatch, settings.dailyNoteFormat, new Date());
// If no match, recursively try with subpaths
if (isNaN(dateFromPath.getTime()) && pathToMatch.includes("/")) {
return extractDateFromPath(
pathToMatch.substring(pathToMatch.indexOf("/") + 1),
{
...settings,
dailyNotePath: "", // Clear dailyNotePath for recursive calls
}
);
}
// Return the timestamp if we found a valid date
if (!isNaN(dateFromPath.getTime())) {
return dateFromPath.getTime();
}
return undefined;
}
/**
* Process a single file - NOW ACCEPTS METADATA FORMAT
@ -554,6 +604,10 @@ function processFile(
stats: FileStats,
settings: {
preferMetadataFormat: MetadataFormat;
useDailyNotePathAsDate: boolean;
dailyNoteFormat: string;
useAsDateType: "due" | "start" | "scheduled";
dailyNotePath: string;
}
): TaskParseResult {
const startTime = performance.now();
@ -564,6 +618,49 @@ function processFile(
settings.preferMetadataFormat
);
const completedTasks = tasks.filter((t) => t.completed).length;
try {
console.log(
filePath.startsWith(settings.dailyNotePath),
settings.dailyNotePath,
settings.useDailyNotePathAsDate
);
if (
(filePath.startsWith(settings.dailyNotePath) ||
("/" + filePath).startsWith(settings.dailyNotePath)) &&
settings.dailyNotePath &&
settings.useDailyNotePathAsDate
) {
for (const task of tasks) {
const dateFromPath = extractDateFromPath(filePath, {
useDailyNotePathAsDate: settings.useDailyNotePathAsDate,
dailyNoteFormat: settings.dailyNoteFormat
.replace(/Y/g, "y")
.replace(/D/g, "d"),
dailyNotePath: settings.dailyNotePath,
});
if (dateFromPath) {
if (settings.useAsDateType === "due" && !task.dueDate) {
task.dueDate = dateFromPath;
} else if (
settings.useAsDateType === "start" &&
!task.startDate
) {
task.startDate = dateFromPath;
} else if (
settings.useAsDateType === "scheduled" &&
!task.scheduledDate
) {
task.scheduledDate = dateFromPath;
}
task.useAsDateType = settings.useAsDateType;
}
}
}
} catch (error) {
console.error(`Worker: Error processing file ${filePath}:`, error);
}
return {
type: "parseResult",
filePath,
@ -585,6 +682,10 @@ function processBatch(
files: { path: string; content: string; stats: FileStats }[],
settings: {
preferMetadataFormat: MetadataFormat;
useDailyNotePathAsDate: boolean;
dailyNoteFormat: string;
useAsDateType: "due" | "start" | "scheduled";
dailyNotePath: string;
}
): BatchIndexResult {
// Ensure return type matches definition
@ -636,6 +737,10 @@ self.onmessage = async (event) => {
// Provide default 'tasks' if missing
const settings = message.settings || {
preferMetadataFormat: "tasks",
useDailyNotePathAsDate: false,
dailyNoteFormat: "yyyy-MM-dd",
useAsDateType: "due",
dailyNotePath: "",
};
// Using 'as any' here because I cannot modify IndexerCommand type directly,

View file

@ -27,6 +27,10 @@ export interface ParseTasksCommand {
/** Settings for the task indexer */
settings: {
preferMetadataFormat: "dataview" | "tasks";
useDailyNotePathAsDate: boolean;
dailyNoteFormat: string;
useAsDateType: "due" | "start" | "scheduled";
dailyNotePath: string;
};
}
@ -53,6 +57,10 @@ export interface BatchIndexCommand {
/** Settings for the task indexer */
settings: {
preferMetadataFormat: "dataview" | "tasks";
useDailyNotePathAsDate: boolean;
dailyNoteFormat: string;
useAsDateType: "due" | "start" | "scheduled";
dailyNotePath: string;
};
}

View file

@ -39,8 +39,9 @@ export interface WorkerPoolOptions {
settings?: {
preferMetadataFormat: "dataview" | "tasks";
useDailyNotePathAsDate: boolean;
dailyNotePathFormat: string;
dailyNoteFormat: string;
useAsDateType: "due" | "start" | "scheduled";
dailyNotePath: string;
};
}
@ -54,8 +55,9 @@ export const DEFAULT_WORKER_OPTIONS: WorkerPoolOptions = {
settings: {
preferMetadataFormat: "tasks",
useDailyNotePathAsDate: false,
dailyNotePathFormat: "YYYY-MM-DD",
dailyNoteFormat: "yyyy-MM-dd",
useAsDateType: "due",
dailyNotePath: "",
},
};
@ -449,6 +451,10 @@ export class TaskWorkerManager extends Component {
},
settings: this.options.settings || {
preferMetadataFormat: "tasks",
useDailyNotePathAsDate: false,
dailyNoteFormat: "yyyy-MM-dd",
useAsDateType: "due",
dailyNotePath: "",
},
};