fix: resolve {{parentNote}} YAML parsing issues and update documentation

## Template Processing Fixes
- Add YAML-specific template processing with automatic quoting for special characters
- Fix {{parentNote}} variable causing nested arrays in YAML frontmatter
- Implement needsYamlQuoting() and escapeYamlString() for proper YAML safety
- Create processTemplateVariablesForYaml() to handle frontmatter vs body content differently

## Root Cause Resolution
The YAML parser was interpreting [[markdown links]] as nested array syntax, causing:
- `parent: [[test-13]]` → parsed as `[["test-13"]]`
- When serialized back: `parent:\n  - - test-13` (double nesting)
- YAML collection warnings and malformed frontmatter

## Solution Implementation
- YAML frontmatter: {{parentNote}} → "[[Parent Note]]" (quoted string)
- Markdown body: {{parentNote}} → [[Parent Note]] (unquoted link)
- Proper string handling prevents YAML parser misinterpretation

## Documentation Updates
- Update template variable documentation in task-defaults.md and task-management.md
- Remove references to old broken behavior (newline and dash prefix)
- Add recommended best practice: use `project:\n  - {{parentNote}}` for project alignment
- Provide clear examples of basic usage vs recommended project usage
- Explain benefits of list format for consistency with projects system

Fixes instant task conversion where parentNote was getting "way too nested"
and showing YAML collection warnings. Now produces clean, properly formatted
YAML with preserved markdown links.
This commit is contained in:
Callum Alpass 2025-07-06 10:17:16 +10:00
parent baf8510239
commit 442318eb0a
3 changed files with 110 additions and 10 deletions

View file

@ -46,10 +46,10 @@ Tasks can be filtered and grouped by their associated projects in all task views
### Template Integration
Projects support template variables for automated workflows. The `{{parentNote}}` variable will format the parent note link as a YAML list item (with newline and dash prefix, e.g., `\n- [[Note Name]]`) when creating tasks from project notes through instant conversion.
Projects support template variables for automated workflows. The `{{parentNote}}` variable inserts the parent note as a properly formatted markdown link. For project organization, it's recommended to use it as a YAML list item (e.g., `project:\n - {{parentNote}}`) to align with the projects system behavior when creating tasks from project notes through instant conversion.
## File Management and Templates
TaskNotes provides a system for managing your task files. You can specify a **Default Tasks Folder** where all new tasks will be created, and you can choose from a variety of **Filename Generation** patterns, including title-based, timestamp-based, and Zettelkasten-style.
TaskNotes also supports **Templates** for both the YAML frontmatter and the body of your task notes. You can use templates to pre-fill common values, add boilerplate text, and create a consistent structure for your tasks. Templates can also include variables, such as `{{title}}`, `{{date}}`, and `{{parentNote}}` (which formats as a YAML list item with newline and dash prefix), which will be automatically replaced with the appropriate values when a new task is created.
TaskNotes also supports **Templates** for both the YAML frontmatter and the body of your task notes. You can use templates to pre-fill common values, add boilerplate text, and create a consistent structure for your tasks. Templates can also include variables, such as `{{title}}`, `{{date}}`, and `{{parentNote}}` (which inserts the parent note as a properly formatted markdown link), which will be automatically replaced with the appropriate values when a new task is created.

View file

@ -28,17 +28,36 @@ You can set the **Default Status** and **Default Priority** for new tasks, as we
TaskNotes supports **Templates** for both the YAML frontmatter and the body of your task notes. You can use templates to pre-fill common values, add boilerplate text, and create a consistent structure for your tasks. Templates can also include variables, such as `{{title}}`, `{{date}}`, and `{{parentNote}}`, which will be automatically replaced with the appropriate values when a new task is created.
The `{{parentNote}}` variable is particularly useful for project organization. When used in a template like:
The `{{parentNote}}` variable is particularly useful for project organization. It inserts the parent note as a properly formatted markdown link.
### Basic Usage
When used in a template like:
```yaml
projects: {{parentNote}}
parent: {{parentNote}}
```
It will resolve to:
```yaml
projects:
- [[Project Name]]
parent: "[[Project Name]]"
```
This formatting makes it easy to automatically assign tasks to the project note they were created from during instant conversion.
### Recommended Usage for Projects
For better alignment with the projects system behavior, it's recommended to use `{{parentNote}}` as a list item in YAML frontmatter:
```yaml
project:
- {{parentNote}}
```
This will resolve to:
```yaml
project:
- "[[Project Name]]"
```
This formatting ensures consistency with how the projects system handles multiple project assignments and makes it easy to automatically assign tasks to the project note they were created from during instant conversion.

View file

@ -83,8 +83,8 @@ function parseTemplateSections(templateContent: string): { frontmatter: string |
*/
function processTemplateFrontmatter(frontmatterContent: string, taskData: TemplateData): Record<string, any> {
try {
// First, process template variables in the raw YAML text
const processedYamlText = processTemplateVariables(frontmatterContent, taskData);
// First, process template variables in the raw YAML text with YAML-safe replacements
const processedYamlText = processTemplateVariablesForYaml(frontmatterContent, taskData);
// Then parse the processed YAML
const parsedFrontmatter = parseYaml(processedYamlText);
@ -109,6 +109,87 @@ function processTemplateBody(bodyContent: string, taskData: TemplateData): strin
return processTemplateVariables(bodyContent, taskData);
}
/**
* Process template variables for YAML frontmatter with proper quoting
* This version ensures that values that could break YAML parsing are properly quoted
*/
function processTemplateVariablesForYaml(template: string, taskData: TemplateData): string {
let result = template;
const now = new Date();
// {{title}} - Task title (quote if contains special characters)
const title = taskData.title || '';
const quotedTitle = needsYamlQuoting(title) ? `"${escapeYamlString(title)}"` : title;
result = result.replace(/\{\{title\}\}/g, quotedTitle);
// {{priority}} - Task priority
result = result.replace(/\{\{priority\}\}/g, taskData.priority || '');
// {{status}} - Task status
result = result.replace(/\{\{status\}\}/g, taskData.status || '');
// {{contexts}} - Task contexts (comma-separated)
const contexts = Array.isArray(taskData.contexts) ? taskData.contexts.join(', ') : '';
result = result.replace(/\{\{contexts\}\}/g, contexts);
// {{tags}} - Task tags (comma-separated)
const tags = Array.isArray(taskData.tags) ? taskData.tags.join(', ') : '';
result = result.replace(/\{\{tags\}\}/g, tags);
// {{timeEstimate}} - Time estimate in minutes
result = result.replace(/\{\{timeEstimate\}\}/g, taskData.timeEstimate?.toString() || '');
// {{dueDate}} - Due date
result = result.replace(/\{\{dueDate\}\}/g, taskData.dueDate || '');
// {{scheduledDate}} - Scheduled date
result = result.replace(/\{\{scheduledDate\}\}/g, taskData.scheduledDate || '');
// {{details}} - User-provided details/description
result = result.replace(/\{\{details\}\}/g, taskData.details || '');
// {{parentNote}} - Parent note name/path - ALWAYS quote for YAML safety
const parentNote = taskData.parentNote || '';
const quotedParentNote = parentNote ? `"${escapeYamlString(parentNote)}"` : '';
result = result.replace(/\{\{parentNote\}\}/g, quotedParentNote);
// {{date}} - Current date (basic format only)
result = result.replace(/\{\{date\}\}/g, format(now, 'yyyy-MM-dd'));
// {{time}} - Current time (basic format only)
result = result.replace(/\{\{time\}\}/g, format(now, 'HH:mm'));
return result;
}
/**
* Check if a string needs YAML quoting
*/
function needsYamlQuoting(str: string): boolean {
if (!str) return false;
// Check for characters that have special meaning in YAML
const yamlSpecialChars = /[\[\]{}:>|*&!%#`@,]/;
const startsWithSpecial = /^[-?]/;
const looksLikeNumber = /^\d+\.?\d*$/;
const looksLikeBoolean = /^(true|false|yes|no|on|off)$/i;
return yamlSpecialChars.test(str) ||
startsWithSpecial.test(str) ||
looksLikeNumber.test(str) ||
looksLikeBoolean.test(str);
}
/**
* Escape a string for safe use in YAML quotes
*/
function escapeYamlString(str: string): string {
if (!str) return '';
// Escape backslashes and double quotes
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
}
/**
* Process task template variables like {{title}}, {{priority}}, {{status}}, etc.
* This is the core variable replacement function used by both frontmatter and body processing
@ -147,7 +228,7 @@ function processTemplateVariables(template: string, taskData: TemplateData): str
result = result.replace(/\{\{details\}\}/g, taskData.details || '');
// {{parentNote}} - Parent note name/path where task was created
result = result.replace(/\{\{parentNote\}\}/g, taskData.parentNote ? `\n- ${taskData.parentNote}` : '');
result = result.replace(/\{\{parentNote\}\}/g, taskData.parentNote || '');
// {{date}} - Current date (basic format only)
result = result.replace(/\{\{date\}\}/g, format(now, 'yyyy-MM-dd'));