mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
feat: support daily note path as due date/start date
This commit is contained in:
parent
e64d56cd1b
commit
edce8bf1b6
11 changed files with 229 additions and 42 deletions
|
|
@ -52,6 +52,7 @@
|
|||
"typescript": "4.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"date-fns": "^4.1.0",
|
||||
"localforage": "^1.10.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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: "",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue