chore: update tasks

This commit is contained in:
Quorafind 2025-06-19 12:57:24 +08:00
parent 96fd09ee8e
commit a71f26e6eb
9 changed files with 2073 additions and 1742 deletions

View file

@ -34,18 +34,40 @@ interface TaskMetadata {
/**
* Sanitize filename by replacing unsafe characters with safe alternatives
* This function only sanitizes the filename part, not directory separators
* @param filename - The filename to sanitize
* @returns The sanitized filename
*/
function sanitizeFilename(filename: string): string {
// Replace unsafe characters with safe alternatives
// Replace unsafe characters with safe alternatives, but keep forward slashes for paths
return filename
.replace(/[<>:"|*?\\]/g, "-") // Replace unsafe chars with dash
.replace(/\//g, "-") // Replace forward slash with dash
.replace(/\s+/g, " ") // Normalize whitespace
.trim(); // Remove leading/trailing whitespace
}
/**
* Sanitize a file path by sanitizing only the filename part while preserving directory structure
* @param filePath - The file path to sanitize
* @returns The sanitized file path
*/
function sanitizeFilePath(filePath: string): string {
const pathParts = filePath.split("/");
// Sanitize each part of the path except preserve the directory structure
const sanitizedParts = pathParts.map((part, index) => {
// For the last part (filename), we can be more restrictive
if (index === pathParts.length - 1) {
return sanitizeFilename(part);
}
// For directory names, we still need to avoid problematic characters but can be less restrictive
return part
.replace(/[<>:"|*?\\]/g, "-")
.replace(/\s+/g, " ")
.trim();
});
return sanitizedParts.join("/");
}
export class QuickCaptureModal extends Modal {
plugin: TaskProgressBarPlugin;
markdownEditor: EmbeddableMarkdownEditor | null = null;
@ -74,12 +96,13 @@ export class QuickCaptureModal extends Modal {
const dateStr = moment().format(
this.plugin.settings.quickCapture.dailyNoteSettings.format
);
const sanitizedDateStr = sanitizeFilename(dateStr);
const fileName = `${sanitizedDateStr}.md`;
this.tempTargetFilePath = this.plugin.settings.quickCapture
// For daily notes, the format might include path separators (e.g., YYYY-MM/YYYY-MM-DD)
// We need to preserve the path structure and only sanitize the final filename
const pathWithDate = this.plugin.settings.quickCapture
.dailyNoteSettings.folder
? `${this.plugin.settings.quickCapture.dailyNoteSettings.folder}/${fileName}`
: fileName;
? `${this.plugin.settings.quickCapture.dailyNoteSettings.folder}/${dateStr}.md`
: `${dateStr}.md`;
this.tempTargetFilePath = sanitizeFilePath(pathWithDate);
} else {
this.tempTargetFilePath =
this.plugin.settings.quickCapture.targetFile;

View file

@ -24,18 +24,40 @@ import "../styles/quick-capture.css";
/**
* Sanitize filename by replacing unsafe characters with safe alternatives
* This function only sanitizes the filename part, not directory separators
* @param filename - The filename to sanitize
* @returns The sanitized filename
*/
function sanitizeFilename(filename: string): string {
// Replace unsafe characters with safe alternatives
// Replace unsafe characters with safe alternatives, but keep forward slashes for paths
return filename
.replace(/[<>:"|*?\\]/g, "-") // Replace unsafe chars with dash
.replace(/\//g, "-") // Replace forward slash with dash
.replace(/\s+/g, " ") // Normalize whitespace
.trim(); // Remove leading/trailing whitespace
}
/**
* Sanitize a file path by sanitizing only the filename part while preserving directory structure
* @param filePath - The file path to sanitize
* @returns The sanitized file path
*/
function sanitizeFilePath(filePath: string): string {
const pathParts = filePath.split("/");
// Sanitize each part of the path except preserve the directory structure
const sanitizedParts = pathParts.map((part, index) => {
// For the last part (filename), we can be more restrictive
if (index === pathParts.length - 1) {
return sanitizeFilename(part);
}
// For directory names, we still need to avoid problematic characters but can be less restrictive
return part
.replace(/[<>:"|*?\\]/g, "-")
.replace(/\s+/g, " ")
.trim();
});
return sanitizedParts.join("/");
}
// Effect to toggle the quick capture panel
export const toggleQuickCapture = StateEffect.define<boolean>();
@ -200,11 +222,12 @@ const handleSubmit = async (
let displayPath = selectedTargetPath;
if (options.targetType === "daily-note" && options.dailyNoteSettings) {
const dateStr = moment().format(options.dailyNoteSettings.format);
const sanitizedDateStr = sanitizeFilename(dateStr);
const fileName = `${sanitizedDateStr}.md`;
displayPath = options.dailyNoteSettings.folder
? `${options.dailyNoteSettings.folder}/${fileName}`
: fileName;
// For daily notes, the format might include path separators (e.g., YYYY-MM/YYYY-MM-DD)
// We need to preserve the path structure and only sanitize the final filename
const pathWithDate = options.dailyNoteSettings.folder
? `${options.dailyNoteSettings.folder}/${dateStr}.md`
: `${dateStr}.md`;
displayPath = sanitizeFilePath(pathWithDate);
}
new Notice(`${t("Captured successfully to")} ${displayPath}`);
@ -255,11 +278,12 @@ function createQuickCapturePanel(view: EditorView): Panel {
let selectedTargetPath: string;
if (options.targetType === "daily-note" && options.dailyNoteSettings) {
const dateStr = moment().format(options.dailyNoteSettings.format);
const sanitizedDateStr = sanitizeFilename(dateStr);
const fileName = `${sanitizedDateStr}.md`;
selectedTargetPath = options.dailyNoteSettings.folder
? `${options.dailyNoteSettings.folder}/${fileName}`
: fileName;
// For daily notes, the format might include path separators (e.g., YYYY-MM/YYYY-MM-DD)
// We need to preserve the path structure and only sanitize the final filename
const pathWithDate = options.dailyNoteSettings.folder
? `${options.dailyNoteSettings.folder}/${dateStr}.md`
: `${dateStr}.md`;
selectedTargetPath = sanitizeFilePath(pathWithDate);
} else {
selectedTargetPath = options.targetFile || "Quick Capture.md";
}

View file

@ -210,11 +210,7 @@ export default class TaskProgressBarPlugin extends Plugin {
this.addChild(this.versionManager);
// Initialize rebuild progress manager
this.rebuildProgressManager = new RebuildProgressManager(
this.app,
this
);
this.addChild(this.rebuildProgressManager);
this.rebuildProgressManager = new RebuildProgressManager();
// Initialize task manager
if (this.settings.enableView) {
@ -643,11 +639,7 @@ export default class TaskProgressBarPlugin extends Plugin {
name: t("Force reindex all tasks"),
callback: async () => {
try {
new Notice(
t("Clearing task cache and rebuilding index...")
);
await this.taskManager.forceReindex();
new Notice(t("Task index completely rebuilt"));
} catch (error) {
console.error("Failed to force reindex tasks:", error);
new Notice(t("Failed to force reindex tasks"));
@ -1203,12 +1195,10 @@ export default class TaskProgressBarPlugin extends Plugin {
}
}
// Add progress callback to track rebuild
this.rebuildProgressManager.addCallback((progress) => {
console.log(
`Rebuild progress: ${progress.processedFiles}/${progress.totalFiles} files processed`
);
});
// Set progress manager for the task manager
this.taskManager.setProgressManager(
this.rebuildProgressManager
);
// Initialize task manager (this will trigger the rebuild)
await this.taskManager.initialize();
@ -1266,6 +1256,11 @@ export default class TaskProgressBarPlugin extends Plugin {
await this.taskManager.persister.recreate();
}
// Set progress manager for the task manager
this.taskManager.setProgressManager(
this.rebuildProgressManager
);
// Initialize with minimal error handling
await this.taskManager.initialize();

View file

@ -741,13 +741,7 @@ export class TaskView extends ItemView {
onConfirm: async (confirmed) => {
if (!confirmed) return;
try {
new Notice(
t(
"Clearing task cache and rebuilding index..."
)
);
await this.plugin.taskManager.forceReindex();
new Notice(t("Task index completely rebuilt"));
} catch (error) {
console.error(
"Failed to force reindex tasks:",

View file

@ -185,7 +185,7 @@ const translations = {
"Failed to sync daily notes settings",
"Daily note format": "Daily note format",
"Date format for daily notes (e.g., YYYY-MM-DD)":
"Date format for daily notes (e.g., YYYY-MM-DD)",
"Date format for daily notes (e.g., YYYY-MM-DD, supports nested formats like YYYY-MM/YYYY-MM-DD)",
"Daily note folder": "Daily note folder",
"Folder path for daily notes (leave empty for root)":
"Folder path for daily notes (leave empty for root)",

File diff suppressed because it is too large Load diff

View file

@ -1,261 +1,132 @@
/**
* Progress Manager for handling index rebuild progress notifications
* Simple rebuild progress tracker using Obsidian's Notice component
*/
import { App, Component, Notice } from "obsidian";
import TaskProgressBarPlugin from "../index";
export interface RebuildProgress {
/** Current step being processed */
currentStep: string;
/** Current file being processed */
currentFile?: string;
/** Number of files processed */
processedFiles: number;
/** Total number of files to process */
totalFiles: number;
/** Number of tasks found so far */
tasksFound: number;
/** Whether the rebuild is complete */
isComplete: boolean;
/** Any error that occurred */
error?: string;
}
export interface RebuildProgressCallback {
(progress: RebuildProgress): void;
}
import { Notice } from "obsidian";
/**
* Manages progress notifications during index rebuild operations
* Manages rebuild progress notifications using a single persistent Notice
*/
export class RebuildProgressManager extends Component {
private currentProgress: RebuildProgress;
private callbacks: Set<RebuildProgressCallback> = new Set();
private currentNotice: Notice | null = null;
export class RebuildProgressManager {
private notice: Notice | null = null;
private startTime: number = 0;
constructor(
private app: App,
private plugin: TaskProgressBarPlugin
) {
super();
this.currentProgress = this.createInitialProgress();
}
private processedFiles: number = 0;
private totalFiles: number = 0;
private tasksFound: number = 0;
/**
* Create initial progress state
*/
private createInitialProgress(): RebuildProgress {
return {
currentStep: "Initializing",
processedFiles: 0,
totalFiles: 0,
tasksFound: 0,
isComplete: false
};
}
/**
* Start a new rebuild progress session
* Start tracking rebuild progress
*/
public startRebuild(totalFiles: number, reason?: string): void {
this.startTime = Date.now();
this.currentProgress = {
currentStep: "Starting rebuild",
processedFiles: 0,
totalFiles,
tasksFound: 0,
isComplete: false
};
this.processedFiles = 0;
this.totalFiles = totalFiles;
this.tasksFound = 0;
// Show initial notice
// Create persistent notice (duration: 0 means it won't auto-hide)
const reasonText = reason ? ` (${reason})` : "";
this.showNotice(`Task Genius: Rebuilding index${reasonText}...`, 0);
this.notifyCallbacks();
this.notice = new Notice(
`Task Genius: Starting rebuild${reasonText}...`,
0
);
}
/**
* Update progress for current step
*/
public updateProgress(updates: Partial<RebuildProgress>): void {
this.currentProgress = {
...this.currentProgress,
...updates
};
// Update notice if it exists
if (this.currentNotice && !this.currentProgress.isComplete) {
const percentage = this.currentProgress.totalFiles > 0
? Math.round((this.currentProgress.processedFiles / this.currentProgress.totalFiles) * 100)
: 0;
const progressText = this.formatProgressText(percentage);
this.updateNotice(progressText);
}
this.notifyCallbacks();
}
/**
* Update step information
* Update progress with current step information
*/
public updateStep(step: string, currentFile?: string): void {
this.updateProgress({
currentStep: step,
currentFile
});
if (!this.notice) return;
let message = `Task Genius: ${step}`;
if (this.totalFiles > 0) {
const percentage = Math.round(
(this.processedFiles / this.totalFiles) * 100
);
message += ` (${this.processedFiles}/${this.totalFiles} - ${percentage}%)`;
}
if (this.tasksFound > 0) {
message += ` - ${this.tasksFound} tasks found`;
}
if (currentFile) {
const fileName = currentFile.split("/").pop() || currentFile;
message += ` - ${fileName}`;
}
this.notice.setMessage(message);
}
/**
* Increment processed files count
* Increment processed files count and update progress
*/
public incrementProcessedFiles(tasksFound: number = 0): void {
this.updateProgress({
processedFiles: this.currentProgress.processedFiles + 1,
tasksFound: this.currentProgress.tasksFound + tasksFound
});
this.processedFiles++;
this.tasksFound += tasksFound;
if (!this.notice) return;
const percentage =
this.totalFiles > 0
? Math.round((this.processedFiles / this.totalFiles) * 100)
: 0;
const message = `Task Genius: Processing files (${this.processedFiles}/${this.totalFiles} - ${percentage}%) - ${this.tasksFound} tasks found`;
this.notice.setMessage(message);
}
/**
* Mark rebuild as complete
* Mark rebuild as complete and show final statistics
*/
public completeRebuild(tasksFound?: number): void {
const duration = Date.now() - this.startTime;
const finalTasksFound = tasksFound ?? this.currentProgress.tasksFound;
this.currentProgress = {
...this.currentProgress,
currentStep: "Complete",
isComplete: true,
tasksFound: finalTasksFound
};
public completeRebuild(finalTaskCount?: number): void {
if (!this.notice) return;
// Show completion notice
this.showCompletionNotice(finalTasksFound, duration);
this.notifyCallbacks();
const duration = Date.now() - this.startTime;
const durationText =
duration > 1000
? `${Math.round(duration / 1000)}s`
: `${duration}ms`;
const taskCount = finalTaskCount ?? this.tasksFound;
const message = `Task Genius: Rebuild complete! Found ${taskCount} tasks in ${durationText}`;
this.notice.setMessage(message);
// Auto-hide the completion notice after 3 seconds
setTimeout(() => {
if (this.notice) {
this.notice.hide();
this.notice = null;
}
}, 3000);
}
/**
* Mark rebuild as failed
* Mark rebuild as failed and show error
*/
public failRebuild(error: string): void {
this.currentProgress = {
...this.currentProgress,
currentStep: "Failed",
isComplete: true,
error
};
if (!this.notice) return;
// Show error notice
this.showNotice(`Task Genius: Index rebuild failed - ${error}`, 5000);
this.notifyCallbacks();
}
const message = `Task Genius: Rebuild failed - ${error}`;
this.notice.setMessage(message);
/**
* Add a progress callback
*/
public addCallback(callback: RebuildProgressCallback): void {
this.callbacks.add(callback);
}
/**
* Remove a progress callback
*/
public removeCallback(callback: RebuildProgressCallback): void {
this.callbacks.delete(callback);
}
/**
* Get current progress
*/
public getProgress(): RebuildProgress {
return { ...this.currentProgress };
}
/**
* Notify all registered callbacks
*/
private notifyCallbacks(): void {
for (const callback of this.callbacks) {
try {
callback(this.currentProgress);
} catch (error) {
console.error("Error in rebuild progress callback:", error);
// Auto-hide the error notice after 5 seconds
setTimeout(() => {
if (this.notice) {
this.notice.hide();
this.notice = null;
}
}
}, 5000);
}
/**
* Format progress text for display
* Clean up and hide any active notice
*/
private formatProgressText(percentage: number): string {
const { currentStep, processedFiles, totalFiles, tasksFound, currentFile } = this.currentProgress;
let text = `Task Genius: ${currentStep}`;
if (totalFiles > 0) {
text += ` (${processedFiles}/${totalFiles} - ${percentage}%)`;
public cleanup(): void {
if (this.notice) {
this.notice.hide();
this.notice = null;
}
if (tasksFound > 0) {
text += ` - ${tasksFound} tasks found`;
}
if (currentFile) {
const fileName = currentFile.split('/').pop() || currentFile;
text += ` - ${fileName}`;
}
return text;
}
/**
* Show or update a notice
*/
private showNotice(message: string, timeout: number = 0): void {
// Hide existing notice
if (this.currentNotice) {
this.currentNotice.hide();
}
// Show new notice
this.currentNotice = new Notice(message, timeout);
}
/**
* Update existing notice text
*/
private updateNotice(message: string): void {
if (this.currentNotice && this.currentNotice.noticeEl) {
this.currentNotice.setMessage(message);
}
}
/**
* Show completion notice
*/
private showCompletionNotice(tasksFound: number, duration: number): void {
const durationText = duration > 1000
? `${Math.round(duration / 1000)}s`
: `${duration}ms`;
const message = `Task Genius: Index rebuilt successfully! Found ${tasksFound} tasks in ${durationText}`;
this.showNotice(message, 3000);
}
/**
* Clean up resources
*/
public onunload(): void {
if (this.currentNotice) {
this.currentNotice.hide();
this.currentNotice = null;
}
this.callbacks.clear();
super.onunload();
}
}

View file

@ -28,6 +28,7 @@ import {
import { CanvasParser } from "./parsing/CanvasParser";
import { CanvasTaskUpdater } from "./parsing/CanvasTaskUpdater";
import { FileMetadataTaskUpdater } from "./workers/FileMetadataTaskUpdater";
import { RebuildProgressManager } from "./RebuildProgressManager";
/**
* TaskManager options
@ -726,6 +727,14 @@ export class TaskManager extends Component {
)} (${batch.length} files)`
);
// Update progress
if (this.progressManager) {
this.progressManager.updateStep(
"Processing files",
batch[0]?.path
);
}
// Process each file in the batch
for (const file of batch) {
// Try to load from cache
@ -747,6 +756,13 @@ export class TaskManager extends Component {
`Loaded ${cached.data.length} tasks from cache for ${file.path}`
);
cachedCount++;
// Report progress
if (this.progressManager) {
this.progressManager.incrementProcessedFiles(
cached.data.length
);
}
} else {
// Cache doesn't exist, is outdated, or version incompatible - process with worker
if (
@ -763,11 +779,19 @@ export class TaskManager extends Component {
);
}
// Don't trigger events - we'll trigger once when initialization is complete
await this.processFileWithoutEvents(
file,
enhancedProjectData
);
const processedTasks =
await this.processFileWithoutEvents(
file,
enhancedProjectData
);
importedCount++;
// Report progress
if (this.progressManager) {
this.progressManager.incrementProcessedFiles(
processedTasks.length
);
}
}
} catch (error) {
console.error(
@ -777,6 +801,13 @@ export class TaskManager extends Component {
// Fall back to main thread processing
await this.indexer.indexFile(file);
importedCount++;
// Report progress
if (this.progressManager) {
this.progressManager.incrementProcessedFiles(
0
);
}
}
}
@ -807,6 +838,9 @@ export class TaskManager extends Component {
const totalTasks = this.indexer.getCache().tasks.size;
this.log(`Task manager initialized with ${totalTasks} tasks`);
// Clear progress manager reference after initialization
this.progressManager = undefined;
// Store the consolidated cache after we've finished processing everything
await this.storeConsolidatedCache();
@ -832,7 +866,7 @@ export class TaskManager extends Component {
private async processFileWithoutEvents(
file: TFile,
enhancedProjectData?: import("./workers/TaskIndexWorkerMessage").EnhancedProjectData
): Promise<void> {
): Promise<Task[]> {
if (!this.workerManager) {
// If worker manager is not available, use main thread processing
await this.indexer.indexFile(file);
@ -841,7 +875,7 @@ export class TaskManager extends Component {
if (tasks.length > 0) {
await this.persister.storeFile(file.path, tasks);
}
return;
return tasks;
}
try {
@ -863,6 +897,7 @@ export class TaskManager extends Component {
}
// No event triggering in this version
return tasks;
} catch (error) {
console.error(`Worker error processing ${file.path}:`, error);
// Fall back to main thread indexing
@ -874,6 +909,7 @@ export class TaskManager extends Component {
}
// No event triggering in this version
return tasks;
}
}
@ -961,6 +997,14 @@ export class TaskManager extends Component {
for (let i = 0; i < files.length; i += batchSize) {
const batch = files.slice(i, i + batchSize);
// Update progress
if (this.progressManager) {
this.progressManager.updateStep(
"Processing files (main thread)",
batch[0]?.path
);
}
// Process each file in the batch
for (const file of batch) {
// Try to load from cache
@ -982,6 +1026,13 @@ export class TaskManager extends Component {
`Loaded ${cached.data.length} tasks from cache for ${file.path}`
);
cachedCount++;
// Report progress
if (this.progressManager) {
this.progressManager.incrementProcessedFiles(
cached.data.length
);
}
} else {
// Remove incompatible cache if it exists
if (
@ -1016,6 +1067,13 @@ export class TaskManager extends Component {
}
}
importedCount++;
// Report progress
if (this.progressManager) {
this.progressManager.incrementProcessedFiles(
tasks.length
);
}
}
} catch (error) {
console.error(`Error processing file ${file.path}:`, error);
@ -1031,11 +1089,22 @@ export class TaskManager extends Component {
if (tasks.length > 0) {
await this.persister.storeFile(file.path, tasks);
}
// Report progress
if (this.progressManager) {
this.progressManager.incrementProcessedFiles(
tasks.length
);
}
} catch (fallbackError) {
console.error(
`Fallback parsing also failed for ${file.path}:`,
fallbackError
);
// Report progress even on failure
if (this.progressManager) {
this.progressManager.incrementProcessedFiles(0);
}
}
importedCount++;
}
@ -1216,6 +1285,16 @@ export class TaskManager extends Component {
// 添加初始化节流标志
private initializationPending: boolean = false;
/** Optional progress manager for rebuild operations */
private progressManager?: RebuildProgressManager;
/**
* Set the progress manager for rebuild operations
*/
public setProgressManager(progressManager: RebuildProgressManager): void {
this.progressManager = progressManager;
}
/**
* Query tasks based on filters and sorting criteria
*/
@ -2618,9 +2697,30 @@ export class TaskManager extends Component {
console.error("Error clearing cache:", error);
}
// Get all supported files for progress tracking
const allFiles = this.app.vault
.getFiles()
.filter(
(file) => file.extension === "md" || file.extension === "canvas"
);
// Create and start progress manager for force reindex
const progressManager = new RebuildProgressManager();
progressManager.startRebuild(
allFiles.length,
"Force reindex requested"
);
// Set progress manager for the task manager
this.setProgressManager(progressManager);
// Re-initialize everything
await this.initialize();
// Mark rebuild as complete
const finalTaskCount = this.getAllTasks().length;
progressManager.completeRebuild(finalTaskCount);
// Trigger an update event
this.app.workspace.trigger(
"task-genius:task-cache-updated",

View file

@ -4,18 +4,40 @@ import { moment } from "obsidian";
/**
* Sanitize filename by replacing unsafe characters with safe alternatives
* This function only sanitizes the filename part, not directory separators
* @param filename - The filename to sanitize
* @returns The sanitized filename
*/
function sanitizeFilename(filename: string): string {
// Replace unsafe characters with safe alternatives
// Replace unsafe characters with safe alternatives, but keep forward slashes for paths
return filename
.replace(/[<>:"|*?\\]/g, "-") // Replace unsafe chars with dash
.replace(/\//g, "-") // Replace forward slash with dash
.replace(/\s+/g, " ") // Normalize whitespace
.trim(); // Remove leading/trailing whitespace
}
/**
* Sanitize a file path by sanitizing only the filename part while preserving directory structure
* @param filePath - The file path to sanitize
* @returns The sanitized file path
*/
function sanitizeFilePath(filePath: string): string {
const pathParts = filePath.split("/");
// Sanitize each part of the path except preserve the directory structure
const sanitizedParts = pathParts.map((part, index) => {
// For the last part (filename), we can be more restrictive
if (index === pathParts.length - 1) {
return sanitizeFilename(part);
}
// For directory names, we still need to avoid problematic characters but can be less restrictive
return part
.replace(/[<>:"|*?\\]/g, "-")
.replace(/\s+/g, " ")
.trim();
});
return sanitizedParts.join("/");
}
/**
* Process file path with date templates
* Replaces {{DATE:format}} patterns with current date formatted using moment.js
@ -27,23 +49,32 @@ export function processDateTemplates(filePath: string): string {
// Match patterns like {{DATE:YYYY-MM-DD}} or {{date:YYYY-MM-DD-HHmm}}
const dateTemplateRegex = /\{\{DATE?:([^}]+)\}\}/gi;
return filePath.replace(dateTemplateRegex, (match, format) => {
try {
// Check if format is empty or only whitespace
if (!format || format.trim() === "") {
return match; // Return original match for empty formats
}
const processedPath = filePath.replace(
dateTemplateRegex,
(match, format) => {
try {
// Check if format is empty or only whitespace
if (!format || format.trim() === "") {
return match; // Return original match for empty formats
}
// Use moment to format the current date with the specified format
const formattedDate = moment().format(format);
// Sanitize the result to ensure it's safe for file systems
return sanitizeFilename(formattedDate);
} catch (error) {
console.warn(`Invalid date format in template: ${format}`, error);
// Return the original match if formatting fails
return match;
// Use moment to format the current date with the specified format
const formattedDate = moment().format(format);
// Return the formatted date without sanitizing here to preserve path structure
return formattedDate;
} catch (error) {
console.warn(
`Invalid date format in template: ${format}`,
error
);
// Return the original match if formatting fails
return match;
}
}
});
);
// Sanitize the entire path while preserving directory structure
return sanitizeFilePath(processedPath);
}
// Save the captured content to the target file
@ -66,11 +97,12 @@ export async function saveCapture(
if (targetType === "daily-note" && dailyNoteSettings) {
// Generate daily note file path
const dateStr = moment().format(dailyNoteSettings.format);
const sanitizedDateStr = sanitizeFilename(dateStr);
const fileName = `${sanitizedDateStr}.md`;
filePath = dailyNoteSettings.folder
? `${dailyNoteSettings.folder}/${fileName}`
: fileName;
// For daily notes, the format might include path separators (e.g., YYYY-MM/YYYY-MM-DD)
// We need to preserve the path structure and only sanitize the final filename
const pathWithDate = dailyNoteSettings.folder
? `${dailyNoteSettings.folder}/${dateStr}.md`
: `${dateStr}.md`;
filePath = sanitizeFilePath(pathWithDate);
} else {
// Use fixed file path
const rawFilePath = targetFile || "Quick Capture.md";