mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
refactor(date-parsing): migrate to date-fns and add custom date format support
- Replace complex regex-based date parsing with date-fns library - Add support for configurable custom date formats in settings - Implement safe error handling to prevent parsing failures from throwing - Support additional formats including timestamps (yyyyMMddHHmmss) - Maintain backward compatibility with existing date parsing behavior - Add UI for managing custom date formats in View Settings tab The migration to date-fns simplifies the codebase by removing manual regex patterns while providing more robust and standardized date parsing. Custom formats can now be configured per-vault through the settings interface.
This commit is contained in:
parent
ca3f7058db
commit
feaa74a963
11 changed files with 558 additions and 59 deletions
|
|
@ -630,6 +630,10 @@ export interface TaskProgressBarSettings {
|
|||
enablePriorityPicker: boolean;
|
||||
enablePriorityKeyboardShortcuts: boolean;
|
||||
enableDatePicker: boolean;
|
||||
|
||||
// Date Parsing Settings
|
||||
customDateFormats: string[];
|
||||
enableCustomDateFormats: boolean;
|
||||
recurrenceDateBase: "due" | "scheduled" | "current"; // Base date for calculating next recurrence
|
||||
|
||||
// Task Filter Settings
|
||||
|
|
@ -1425,6 +1429,10 @@ export const DEFAULT_SETTINGS: TaskProgressBarSettings = {
|
|||
blockRefPrefix: "timer"
|
||||
},
|
||||
|
||||
// Custom Date Format Defaults
|
||||
enableCustomDateFormats: false,
|
||||
customDateFormats: [],
|
||||
|
||||
// Onboarding Defaults
|
||||
onboarding: {
|
||||
completed: false,
|
||||
|
|
|
|||
|
|
@ -94,6 +94,11 @@ export const getConfig = (
|
|||
|
||||
// File Metadata Inheritance
|
||||
fileMetadataInheritance: plugin?.settings?.fileMetadataInheritance,
|
||||
|
||||
// Custom date formats for parsing
|
||||
customDateFormats: plugin?.settings?.enableCustomDateFormats
|
||||
? plugin?.settings?.customDateFormats
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return config;
|
||||
|
|
|
|||
|
|
@ -161,6 +161,154 @@ export function renderViewSettingsTab(
|
|||
.setDesc(t("Configure how task metadata is parsed and recognized."))
|
||||
.setHeading();
|
||||
|
||||
// Date Format Configuration
|
||||
new Setting(containerEl)
|
||||
.setName(t("Enable custom date formats"))
|
||||
.setDesc(
|
||||
t(
|
||||
"Enable custom date format patterns for parsing dates. When enabled, the parser will try your custom formats before falling back to default formats."
|
||||
)
|
||||
)
|
||||
.addToggle((toggle) => {
|
||||
toggle
|
||||
.setValue(settingTab.plugin.settings.enableCustomDateFormats ?? false)
|
||||
.onChange((value) => {
|
||||
settingTab.plugin.settings.enableCustomDateFormats = value;
|
||||
settingTab.applySettingsUpdate();
|
||||
settingTab.display(); // Refresh to show/hide custom formats settings
|
||||
});
|
||||
});
|
||||
|
||||
if (settingTab.plugin.settings.enableCustomDateFormats) {
|
||||
// Container for custom date formats
|
||||
const dateFormatsContainer = containerEl.createDiv({
|
||||
cls: "task-genius-date-formats-container",
|
||||
});
|
||||
|
||||
// Header with description
|
||||
dateFormatsContainer.createEl("h3", {
|
||||
text: t("Custom date formats"),
|
||||
cls: "task-genius-formats-header"
|
||||
});
|
||||
|
||||
dateFormatsContainer.createEl("p", {
|
||||
text: t("Add custom date format patterns. Date patterns: yyyy (4-digit year), yy (2-digit year), MM (2-digit month), M (1-2 digit month), dd (2-digit day), d (1-2 digit day), MMM (short month name), MMMM (full month name). Time patterns: HH (2-digit hour), mm (2-digit minute), ss (2-digit second). Use single quotes for literals (e.g., 'T' for ISO format)."),
|
||||
cls: "setting-item-description"
|
||||
});
|
||||
|
||||
// Container for format list
|
||||
const formatListContainer = dateFormatsContainer.createDiv({
|
||||
cls: "task-genius-format-list",
|
||||
});
|
||||
|
||||
// Function to render the format list
|
||||
const renderFormatList = () => {
|
||||
formatListContainer.empty();
|
||||
|
||||
const formats = settingTab.plugin.settings.customDateFormats ?? [];
|
||||
|
||||
// Render existing formats
|
||||
formats.forEach((format, index) => {
|
||||
const formatItem = formatListContainer.createDiv({
|
||||
cls: "task-genius-format-item",
|
||||
});
|
||||
|
||||
// Format input
|
||||
const formatInput = formatItem.createEl("input", {
|
||||
type: "text",
|
||||
value: format,
|
||||
cls: "task-genius-format-input",
|
||||
placeholder: t("Enter date format (e.g., yyyy-MM-dd or yyyyMMdd_HHmmss)"),
|
||||
});
|
||||
|
||||
formatInput.addEventListener("input", (e) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
settingTab.plugin.settings.customDateFormats![index] = target.value.trim();
|
||||
settingTab.applySettingsUpdate();
|
||||
});
|
||||
|
||||
// Delete button
|
||||
const deleteBtn = formatItem.createEl("button", {
|
||||
cls: "task-genius-format-delete-btn",
|
||||
text: "×",
|
||||
attr: {
|
||||
"aria-label": t("Delete format"),
|
||||
"title": t("Delete this format"),
|
||||
}
|
||||
});
|
||||
|
||||
deleteBtn.addEventListener("click", () => {
|
||||
settingTab.plugin.settings.customDateFormats!.splice(index, 1);
|
||||
settingTab.applySettingsUpdate();
|
||||
renderFormatList();
|
||||
});
|
||||
});
|
||||
|
||||
// Add new format button
|
||||
const addFormatBtn = formatListContainer.createEl("button", {
|
||||
cls: "task-genius-add-format-btn",
|
||||
text: t("+ Add Date Format"),
|
||||
});
|
||||
|
||||
addFormatBtn.addEventListener("click", () => {
|
||||
if (!settingTab.plugin.settings.customDateFormats) {
|
||||
settingTab.plugin.settings.customDateFormats = [];
|
||||
}
|
||||
settingTab.plugin.settings.customDateFormats.push("");
|
||||
settingTab.applySettingsUpdate();
|
||||
renderFormatList();
|
||||
|
||||
// Focus on the new input
|
||||
const inputs = formatListContainer.querySelectorAll(".task-genius-format-input");
|
||||
const lastInput = inputs[inputs.length - 1] as HTMLInputElement;
|
||||
if (lastInput) {
|
||||
lastInput.focus();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Initial render
|
||||
renderFormatList();
|
||||
|
||||
// Add example dates section
|
||||
const examplesContainer = containerEl.createDiv({
|
||||
cls: "task-genius-date-examples",
|
||||
});
|
||||
|
||||
examplesContainer.createEl("h4", {
|
||||
text: t("Format Examples:"),
|
||||
cls: "task-genius-examples-header"
|
||||
});
|
||||
|
||||
const exampleFormats = [
|
||||
{ format: "yyyy-MM-dd", example: "2025-08-16" },
|
||||
{ format: "dd/MM/yyyy", example: "16/08/2025" },
|
||||
{ format: "MM-dd-yyyy", example: "08-16-2025" },
|
||||
{ format: "yyyy.MM.dd", example: "2025.08.16" },
|
||||
{ format: "yyyyMMdd", example: "20250816" },
|
||||
{ format: "yyyyMMdd_HHmmss", example: "20250816_144403" },
|
||||
{ format: "yyyyMMddHHmmss", example: "20250816144403" },
|
||||
{ format: "yyyy-MM-dd'T'HH:mm", example: "2025-08-16T14:44" },
|
||||
{ format: "dd MMM yyyy", example: "16 Aug 2025" },
|
||||
{ format: "MMM dd, yyyy", example: "Aug 16, 2025" },
|
||||
{ format: "yyyy年MM月dd日", example: "2025年08月16日" },
|
||||
];
|
||||
|
||||
const table = examplesContainer.createEl("table", {
|
||||
cls: "task-genius-date-examples-table",
|
||||
});
|
||||
|
||||
const headerRow = table.createEl("tr");
|
||||
headerRow.createEl("th", { text: t("Format Pattern") });
|
||||
headerRow.createEl("th", { text: t("Example") });
|
||||
|
||||
exampleFormats.forEach(({ format, example }) => {
|
||||
const row = table.createEl("tr");
|
||||
row.createEl("td", { text: format });
|
||||
row.createEl("td", { text: example });
|
||||
});
|
||||
}
|
||||
|
||||
// Get current metadata format to show appropriate settings
|
||||
const isDataviewFormat =
|
||||
settingTab.plugin.settings.preferMetadataFormat === "dataview";
|
||||
|
|
|
|||
|
|
@ -1339,3 +1339,160 @@
|
|||
font-style: italic;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* Date format configuration styles */
|
||||
.task-genius-date-formats-container {
|
||||
margin-top: 15px;
|
||||
padding: 15px;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.task-genius-formats-header {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.task-genius-format-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.task-genius-format-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px;
|
||||
background-color: var(--background-primary);
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.task-genius-format-item:hover {
|
||||
border-color: var(--interactive-accent);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.task-genius-format-input {
|
||||
flex: 1;
|
||||
padding: 6px 10px;
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
color: var(--text-normal);
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 0.95em;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.task-genius-format-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--interactive-accent);
|
||||
box-shadow: 0 0 0 2px var(--interactive-accent-hover);
|
||||
}
|
||||
|
||||
.task-genius-format-input::placeholder {
|
||||
color: var(--text-faint);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.task-genius-format-delete-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--background-modifier-error);
|
||||
color: var(--text-on-accent);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.task-genius-format-delete-btn:hover {
|
||||
background-color: var(--background-modifier-error-hover);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.task-genius-add-format-btn {
|
||||
padding: 10px 16px;
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
align-self: flex-start;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.task-genius-add-format-btn:hover {
|
||||
background-color: var(--interactive-accent-hover);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.task-genius-add-format-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Date format examples section */
|
||||
.task-genius-date-examples {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.task-genius-examples-header {
|
||||
margin-top: 0;
|
||||
margin-bottom: 12px;
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.task-genius-date-examples-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.task-genius-date-examples-table th,
|
||||
.task-genius-date-examples-table td {
|
||||
padding: 8px 12px;
|
||||
text-align: left;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.task-genius-date-examples-table th {
|
||||
background-color: var(--background-secondary-alt);
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.task-genius-date-examples-table td {
|
||||
background-color: var(--background-primary);
|
||||
font-family: var(--font-monospace);
|
||||
}
|
||||
|
||||
.task-genius-date-examples-table tr:nth-child(even) td {
|
||||
background-color: var(--background-primary-alt);
|
||||
}
|
||||
|
||||
.task-genius-date-examples-table tr:hover td {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export interface TaskParserConfig {
|
|||
emojiMapping: Record<string, string>; // Emoji to metadata key mapping, e.g. "📅" -> "due"
|
||||
metadataParseMode: MetadataParseMode; // Metadata parsing mode
|
||||
specialTagPrefixes: Record<string, string>; // Special tag prefix mapping, e.g. "project" -> "project"
|
||||
customDateFormats?: string[]; // Custom date format patterns for parsing dates
|
||||
|
||||
// File Metadata Inheritance
|
||||
fileMetadataInheritance?: {
|
||||
|
|
|
|||
|
|
@ -1,76 +1,111 @@
|
|||
import {
|
||||
format,
|
||||
isToday,
|
||||
isTomorrow,
|
||||
isThisYear,
|
||||
parse,
|
||||
parseISO,
|
||||
isValid,
|
||||
startOfDay,
|
||||
} from "date-fns";
|
||||
import { enUS } from "date-fns/locale";
|
||||
|
||||
/**
|
||||
* Format a date in a human-readable format
|
||||
* @param date Date to format
|
||||
* @returns Formatted date string
|
||||
*/
|
||||
export function formatDate(date: Date): string {
|
||||
const now = new Date();
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
// Check if date is today or tomorrow
|
||||
if (date.getTime() === today.getTime()) {
|
||||
if (isToday(date)) {
|
||||
return "Today";
|
||||
} else if (date.getTime() === tomorrow.getTime()) {
|
||||
} else if (isTomorrow(date)) {
|
||||
return "Tomorrow";
|
||||
}
|
||||
|
||||
// Format as Month Day, Year for other dates
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
};
|
||||
|
||||
// Only add year if it's not the current year
|
||||
if (date.getFullYear() !== now.getFullYear()) {
|
||||
options.year = "numeric";
|
||||
if (isThisYear(date)) {
|
||||
return format(date, "MMM d");
|
||||
} else {
|
||||
return format(date, "MMM d, yyyy");
|
||||
}
|
||||
|
||||
return date.toLocaleDateString(undefined, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a date string in the format YYYY-MM-DD
|
||||
* Parse a date string in various formats
|
||||
* @param dateString Date string to parse
|
||||
* @param customFormats Optional array of custom date format patterns to try
|
||||
* @returns Parsed date as a number or undefined if invalid
|
||||
*/
|
||||
export function parseLocalDate(dateString: string): number | undefined {
|
||||
export function parseLocalDate(
|
||||
dateString: string,
|
||||
customFormats?: string[]
|
||||
): number | undefined {
|
||||
if (!dateString) return undefined;
|
||||
// Basic regex check for YYYY-MM-DD format
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
|
||||
console.warn(`Worker: Invalid date format encountered: ${dateString}`);
|
||||
|
||||
// Trim whitespace
|
||||
dateString = dateString.trim();
|
||||
|
||||
// Skip template strings
|
||||
if (dateString.includes("{{") || dateString.includes("}}")) {
|
||||
return undefined;
|
||||
}
|
||||
const parts = dateString.split("-");
|
||||
if (parts.length === 3) {
|
||||
const year = parseInt(parts[0], 10);
|
||||
const month = parseInt(parts[1], 10); // 1-based month
|
||||
const day = parseInt(parts[2], 10);
|
||||
// Validate date parts
|
||||
if (
|
||||
!isNaN(year) &&
|
||||
!isNaN(month) &&
|
||||
month >= 1 &&
|
||||
month <= 12 &&
|
||||
!isNaN(day) &&
|
||||
day >= 1 &&
|
||||
day <= 31
|
||||
) {
|
||||
// Use local time to create date object
|
||||
const date = new Date(year, month - 1, day);
|
||||
// Check if constructed date is valid (e.g., handle 2/30 case)
|
||||
if (
|
||||
date.getFullYear() === year &&
|
||||
date.getMonth() === month - 1 &&
|
||||
date.getDate() === day
|
||||
) {
|
||||
date.setHours(0, 0, 0, 0); // Standardize time part for date comparison
|
||||
return date.getTime();
|
||||
|
||||
// Define default format patterns to try with date-fns
|
||||
const defaultFormats = [
|
||||
"yyyy-MM-dd", // ISO format
|
||||
"yyyy/MM/dd", // YYYY/MM/DD
|
||||
"dd-MM-yyyy", // DD-MM-YYYY
|
||||
"dd/MM/yyyy", // DD/MM/YYYY
|
||||
"MM-dd-yyyy", // MM-DD-YYYY
|
||||
"MM/dd/yyyy", // MM/DD/YYYY
|
||||
"yyyy.MM.dd", // YYYY.MM.DD
|
||||
"dd.MM.yyyy", // DD.MM.YYYY
|
||||
"yyyy年M月d日", // Chinese/Japanese format
|
||||
"MMM d, yyyy", // MMM DD, YYYY (e.g., Jan 15, 2025)
|
||||
"MMM dd, yyyy", // MMM DD, YYYY with leading zero
|
||||
"d MMM yyyy", // DD MMM YYYY (e.g., 15 Jan 2025)
|
||||
"dd MMM yyyy", // DD MMM YYYY with leading zero
|
||||
"yyyyMMddHHmmss",
|
||||
"yyyyMMdd_HHmmss",
|
||||
];
|
||||
|
||||
// Combine custom formats with default formats
|
||||
const allFormats = customFormats
|
||||
? [...customFormats, ...defaultFormats]
|
||||
: defaultFormats;
|
||||
|
||||
// Try each format with date-fns parse
|
||||
for (const formatString of allFormats) {
|
||||
try {
|
||||
const parsedDate = parse(dateString, formatString, new Date(), {
|
||||
locale: enUS,
|
||||
});
|
||||
|
||||
// Check if the parsed date is valid
|
||||
if (isValid(parsedDate)) {
|
||||
// Set to start of day to match original behavior
|
||||
const normalizedDate = startOfDay(parsedDate);
|
||||
return normalizedDate.getTime();
|
||||
}
|
||||
} catch (e) {
|
||||
// Silently continue to next format
|
||||
continue;
|
||||
}
|
||||
}
|
||||
console.warn(`Worker: Invalid date values after parsing: ${dateString}`);
|
||||
|
||||
// Try parseISO as a fallback for ISO strings
|
||||
try {
|
||||
const isoDate = parseISO(dateString);
|
||||
if (isValid(isoDate)) {
|
||||
const normalizedDate = startOfDay(isoDate);
|
||||
return normalizedDate.getTime();
|
||||
}
|
||||
} catch (e) {
|
||||
// Silently continue
|
||||
}
|
||||
|
||||
// If all parsing attempts fail, log a warning
|
||||
console.warn(`Worker: Could not parse date: ${dateString}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
@ -83,8 +118,8 @@ export function parseLocalDate(dateString: string): number | undefined {
|
|||
export function getTodayLocalDateString(): string {
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(today.getDate()).padStart(2, '0');
|
||||
const month = String(today.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(today.getDate()).padStart(2, "0");
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
|
|
@ -97,8 +132,8 @@ export function getTodayLocalDateString(): string {
|
|||
*/
|
||||
export function getLocalDateString(date: Date): string {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export class MarkdownTaskParser {
|
|||
private currentHeadingLevel?: number;
|
||||
private fileMetadata?: Record<string, any>; // Store file frontmatter metadata
|
||||
private projectConfigCache?: Record<string, any>; // Cache for project config files
|
||||
private customDateFormats?: string[]; // Store custom date formats from settings
|
||||
|
||||
// Date parsing cache to improve performance for large-scale parsing
|
||||
private static dateCache = new Map<string, number | undefined>();
|
||||
|
|
@ -33,6 +34,8 @@ export class MarkdownTaskParser {
|
|||
|
||||
constructor(config: TaskParserConfig) {
|
||||
this.config = config;
|
||||
// Extract custom date formats if available
|
||||
this.customDateFormats = config.customDateFormats;
|
||||
}
|
||||
|
||||
// Public alias for extractMetadataAndTags
|
||||
|
|
@ -1111,13 +1114,14 @@ export class MarkdownTaskParser {
|
|||
if (!dateStr) return undefined;
|
||||
|
||||
// Check cache first to avoid repeated date parsing
|
||||
const cachedDate = MarkdownTaskParser.dateCache.get(dateStr);
|
||||
const cacheKey = `${dateStr}_${(this.customDateFormats || []).join(',')}`;
|
||||
const cachedDate = MarkdownTaskParser.dateCache.get(cacheKey);
|
||||
if (cachedDate !== undefined) {
|
||||
return cachedDate;
|
||||
}
|
||||
|
||||
// Parse date and cache the result
|
||||
const date = parseLocalDate(dateStr);
|
||||
// Parse date with custom formats and cache the result
|
||||
const date = parseLocalDate(dateStr, this.customDateFormats);
|
||||
|
||||
// Implement cache size limit to prevent memory issues
|
||||
if (
|
||||
|
|
@ -1131,7 +1135,7 @@ export class MarkdownTaskParser {
|
|||
}
|
||||
}
|
||||
|
||||
MarkdownTaskParser.dateCache.set(dateStr, date);
|
||||
MarkdownTaskParser.dateCache.set(cacheKey, date);
|
||||
return date;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { getConfig } from "../../common/task-parser-config";
|
|||
import { FileMetadataTaskParser } from "./FileMetadataTaskParser";
|
||||
import { CanvasParser } from "../parsing/CanvasParser";
|
||||
import { SupportedFileType } from "../fileTypeUtils";
|
||||
import { TgProject } from "../../types/task";
|
||||
|
||||
/**
|
||||
* Enhanced task parsing using configurable parser
|
||||
|
|
@ -46,7 +47,7 @@ function parseTasksWithConfigurableParser(
|
|||
// Enhanced parsing: use pre-computed data if available
|
||||
let enhancedFileMetadata = fileMetadata;
|
||||
let projectConfigData: Record<string, any> | undefined;
|
||||
let tgProject: import("../../types/task").TgProject | undefined;
|
||||
let tgProject: TgProject | undefined;
|
||||
|
||||
// Only process enhanced project data if enhanced project is enabled
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -179,6 +179,10 @@ export type TaskWorkerSettings = {
|
|||
projectTagPrefix?: Record<MetadataFormat, string>;
|
||||
contextTagPrefix?: Record<MetadataFormat, string>;
|
||||
areaTagPrefix?: Record<MetadataFormat, string>;
|
||||
|
||||
// Custom date format settings
|
||||
enableCustomDateFormats?: boolean;
|
||||
customDateFormats?: string[];
|
||||
|
||||
// Enhanced project configuration (basic config for fallback)
|
||||
projectConfig?: {
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ export interface WorkerPoolOptions {
|
|||
focusHeading: string;
|
||||
fileParsingConfig?: FileParsingConfiguration;
|
||||
fileMetadataInheritance?: FileMetadataInheritanceConfig;
|
||||
enableCustomDateFormats?: boolean;
|
||||
customDateFormats?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
136
styles.css
136
styles.css
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue