mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
feat: Add option to store task title exclusively in filename (#106)
This commit introduces a new setting that allows users to store the task
title exclusively in the filename, removing the 'title' property from
the frontmatter. This simplifies the frontmatter but disables filename
templating.
Key Changes:
- **Settings:** Added a boolean option to the plugin settings with a UI
toggle in the Task Defaults tab.
- **Filename Generation:** Updated to use the task title as the
filename when the new setting is enabled, bypassing other filename
generation logic.
- **Field Mapping:**
- Modified in to prevent writing the title to the frontmatter when
the new setting is enabled.
- Updated to extract the title from the filename if it's not
present in the frontmatter, ensuring backward compatibility with
existing tasks.
- **Services:** Updated and to pass the setting to the field mapper.
This change provides users with more control over their data storage and
simplifies the frontmatter for new tasks created with this option
enabled.
This commit is contained in:
parent
e3c4bb18d2
commit
d061be4f37
7 changed files with 47 additions and 11 deletions
|
|
@ -138,7 +138,8 @@ export default class TaskNotesPlugin extends Plugin {
|
|||
this.settings.taskTag,
|
||||
this.settings.excludedFolders,
|
||||
this.fieldMapper,
|
||||
this.settings.disableNoteIndexing
|
||||
this.settings.disableNoteIndexing,
|
||||
this.settings.storeTitleInFilename
|
||||
);
|
||||
|
||||
// Use same instance for event emitting
|
||||
|
|
@ -578,7 +579,8 @@ export default class TaskNotesPlugin extends Plugin {
|
|||
this.settings.taskTag,
|
||||
this.settings.excludedFolders,
|
||||
this.fieldMapper,
|
||||
this.settings.disableNoteIndexing
|
||||
this.settings.disableNoteIndexing,
|
||||
this.settings.storeTitleInFilename
|
||||
);
|
||||
|
||||
// Update custom styles
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export class FieldMapper {
|
|||
/**
|
||||
* Convert frontmatter object using mapping to internal task data
|
||||
*/
|
||||
mapFromFrontmatter(frontmatter: any, filePath: string): Partial<TaskInfo> {
|
||||
mapFromFrontmatter(frontmatter: any, filePath: string, storeTitleInFilename?: boolean): Partial<TaskInfo> {
|
||||
if (!frontmatter) return {};
|
||||
|
||||
const mapped: Partial<TaskInfo> = {
|
||||
|
|
@ -26,6 +26,11 @@ export class FieldMapper {
|
|||
// Map each field if it exists in frontmatter
|
||||
if (frontmatter[this.mapping.title] !== undefined) {
|
||||
mapped.title = frontmatter[this.mapping.title];
|
||||
} else if (storeTitleInFilename) {
|
||||
const filename = filePath.split('/').pop()?.replace('.md', '');
|
||||
if (filename) {
|
||||
mapped.title = filename;
|
||||
}
|
||||
}
|
||||
|
||||
if (frontmatter[this.mapping.status] !== undefined) {
|
||||
|
|
@ -95,7 +100,7 @@ export class FieldMapper {
|
|||
/**
|
||||
* Convert internal task data to frontmatter using mapping
|
||||
*/
|
||||
mapToFrontmatter(taskData: Partial<TaskInfo>, taskTag?: string): any {
|
||||
mapToFrontmatter(taskData: Partial<TaskInfo>, taskTag?: string, storeTitleInFilename?: boolean): any {
|
||||
const frontmatter: any = {};
|
||||
|
||||
// Map each field if it exists in task data
|
||||
|
|
@ -103,6 +108,10 @@ export class FieldMapper {
|
|||
frontmatter[this.mapping.title] = taskData.title;
|
||||
}
|
||||
|
||||
if (storeTitleInFilename) {
|
||||
delete frontmatter[this.mapping.title];
|
||||
}
|
||||
|
||||
if (taskData.status !== undefined) {
|
||||
frontmatter[this.mapping.status] = taskData.status;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export class TaskService {
|
|||
};
|
||||
|
||||
// Use field mapper to convert to frontmatter with proper field mapping
|
||||
const frontmatter = this.plugin.fieldMapper.mapToFrontmatter(completeTaskData, this.plugin.settings.taskTag);
|
||||
const frontmatter = this.plugin.fieldMapper.mapToFrontmatter(completeTaskData, this.plugin.settings.taskTag, this.plugin.settings.storeTitleInFilename);
|
||||
|
||||
// Tags are handled separately (not via field mapper)
|
||||
frontmatter.tags = tagsArray;
|
||||
|
|
@ -593,7 +593,7 @@ export class TaskService {
|
|||
}
|
||||
|
||||
// Use field mapper to convert ALL task data to frontmatter with proper field mapping
|
||||
const mappedFrontmatter = this.plugin.fieldMapper.mapToFrontmatter(completeTaskData, this.plugin.settings.taskTag);
|
||||
const mappedFrontmatter = this.plugin.fieldMapper.mapToFrontmatter(completeTaskData, this.plugin.settings.taskTag, this.plugin.settings.storeTitleInFilename);
|
||||
|
||||
// Apply mapped frontmatter properties, preserving any existing non-task properties
|
||||
// The FieldMapper only includes task-related fields, so this is safe
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export interface TaskNotesSettings {
|
|||
taskOrgFiltersCollapsed: boolean; // Save collapse state of task organization filters
|
||||
// Task filename settings
|
||||
taskFilenameFormat: 'title' | 'zettel' | 'timestamp' | 'custom';
|
||||
storeTitleInFilename: boolean;
|
||||
customFilenameTemplate: string; // Template for custom format
|
||||
// Task creation defaults
|
||||
taskCreationDefaults: TaskCreationDefaults;
|
||||
|
|
@ -232,6 +233,7 @@ export const DEFAULT_SETTINGS: TaskNotesSettings = {
|
|||
taskOrgFiltersCollapsed: false, // Default to expanded
|
||||
// Task filename defaults
|
||||
taskFilenameFormat: 'zettel', // Keep existing behavior as default
|
||||
storeTitleInFilename: false,
|
||||
customFilenameTemplate: '{title}', // Simple title template
|
||||
// Task creation defaults
|
||||
taskCreationDefaults: DEFAULT_TASK_CREATION_DEFAULTS,
|
||||
|
|
@ -522,6 +524,20 @@ export class TaskNotesSettingTab extends PluginSettingTab {
|
|||
// Task filename settings
|
||||
new Setting(container).setName('Task filenames').setHeading();
|
||||
|
||||
new Setting(container)
|
||||
.setName('Store title exclusively in filename')
|
||||
.setDesc("When enabled, the task's title will be used as the filename, and the 'title' property will be removed from the frontmatter. This is a significant data storage change that simplifies frontmatter but disables all other filename templating options. Existing tasks will not be affected, but new tasks will follow this rule.")
|
||||
.addToggle(toggle => {
|
||||
toggle.toggleEl.setAttribute('aria-label', 'Store title exclusively in filename');
|
||||
return toggle
|
||||
.setValue(this.plugin.settings.storeTitleInFilename)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.storeTitleInFilename = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.renderActiveTab(); // Re-render to show/hide other options
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(container)
|
||||
.setName('Filename format')
|
||||
.setDesc('How task filenames should be generated')
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export class MinimalNativeCache extends Events {
|
|||
private excludedFolders: string[];
|
||||
private fieldMapper?: FieldMapper;
|
||||
private disableNoteIndexing: boolean;
|
||||
private storeTitleInFilename: boolean;
|
||||
|
||||
// Only essential indexes - everything else computed on-demand
|
||||
private tasksByDate: Map<string, Set<string>> = new Map(); // YYYY-MM-DD -> task paths
|
||||
|
|
@ -39,7 +40,8 @@ export class MinimalNativeCache extends Events {
|
|||
taskTag: string,
|
||||
excludedFolders = '',
|
||||
fieldMapper?: FieldMapper,
|
||||
disableNoteIndexing = false
|
||||
disableNoteIndexing = false,
|
||||
storeTitleInFilename = false
|
||||
) {
|
||||
super();
|
||||
this.app = app;
|
||||
|
|
@ -49,6 +51,7 @@ export class MinimalNativeCache extends Events {
|
|||
: [];
|
||||
this.fieldMapper = fieldMapper;
|
||||
this.disableNoteIndexing = disableNoteIndexing;
|
||||
this.storeTitleInFilename = storeTitleInFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -660,7 +663,7 @@ export class MinimalNativeCache extends Events {
|
|||
if (!this.fieldMapper) return null;
|
||||
|
||||
try {
|
||||
const mappedTask = this.fieldMapper.mapFromFrontmatter(frontmatter, path);
|
||||
const mappedTask = this.fieldMapper.mapFromFrontmatter(frontmatter, path, this.storeTitleInFilename);
|
||||
|
||||
return {
|
||||
title: mappedTask.title || 'Untitled task',
|
||||
|
|
@ -719,7 +722,8 @@ export class MinimalNativeCache extends Events {
|
|||
taskTag: string,
|
||||
excludedFolders: string,
|
||||
fieldMapper?: FieldMapper,
|
||||
disableNoteIndexing = false
|
||||
disableNoteIndexing = false,
|
||||
storeTitleInFilename = false
|
||||
): void {
|
||||
this.taskTag = taskTag;
|
||||
this.excludedFolders = excludedFolders
|
||||
|
|
@ -727,6 +731,7 @@ export class MinimalNativeCache extends Events {
|
|||
: [];
|
||||
this.fieldMapper = fieldMapper;
|
||||
this.disableNoteIndexing = disableNoteIndexing;
|
||||
this.storeTitleInFilename = storeTitleInFilename;
|
||||
|
||||
if (this.initialized) {
|
||||
this.clearAllIndexes();
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ export function generateTaskFilename(
|
|||
throw new Error('Invalid date provided in context');
|
||||
}
|
||||
|
||||
if (settings.storeTitleInFilename) {
|
||||
return sanitizeForFilename(context.title);
|
||||
}
|
||||
|
||||
try {
|
||||
switch (settings.taskFilenameFormat) {
|
||||
case 'title':
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ export function extractTaskInfo(
|
|||
if (yaml) {
|
||||
if (fieldMapper) {
|
||||
// Use field mapper to extract task info
|
||||
const mappedTask = fieldMapper.mapFromFrontmatter(yaml, path);
|
||||
const mappedTask = fieldMapper.mapFromFrontmatter(yaml, path, this.plugin.settings.storeTitleInFilename);
|
||||
|
||||
// Ensure required fields have defaults
|
||||
const taskInfo: TaskInfo = {
|
||||
|
|
@ -324,7 +324,7 @@ export function extractTaskInfo(
|
|||
} else {
|
||||
// Fallback to default field mapping
|
||||
const defaultMapper = new FieldMapper(DEFAULT_FIELD_MAPPING);
|
||||
const mappedTask = defaultMapper.mapFromFrontmatter(yaml, path);
|
||||
const mappedTask = defaultMapper.mapFromFrontmatter(yaml, path, this.plugin.settings.storeTitleInFilename);
|
||||
|
||||
return {
|
||||
title: mappedTask.title || 'Untitled task',
|
||||
|
|
|
|||
Loading…
Reference in a new issue