( app: App, plugin: QuickAdd, params: ApplyTemplateToNoteParams, )
| 114 | * and "bottom" otherwise. |
| 115 | */ |
| 116 | export async function applyTemplateToNote( |
| 117 | app: App, |
| 118 | plugin: QuickAdd, |
| 119 | params: ApplyTemplateToNoteParams, |
| 120 | ): Promise<TFile | null> { |
| 121 | const file = params.file ?? app.workspace.getActiveFile(); |
| 122 | if (!file || file.extension !== "md") { |
| 123 | new Notice("QuickAdd: No active markdown note to apply a template to."); |
| 124 | return null; |
| 125 | } |
| 126 | |
| 127 | // This path runs a template through the executor WITHOUT going through |
| 128 | // ChoiceExecutor.execute(), so beginExecutionContext() never captures the |
| 129 | // trigger context. Seed it here so {{FIELD:<field>|default-from:active}} in |
| 130 | // the applied template can read the note being applied to (issue #1429): the |
| 131 | // active note for the command, or the explicit params.file for the API path. |
| 132 | // Seed only when absent (never clobber a caller-supplied context) and restore |
| 133 | // the prior value in `finally`, so a reused executor — e.g. a script applying |
| 134 | // templates to several notes in turn, or a later `format()` on the same API |
| 135 | // object — never inherits this call's note for a subsequent default-from:active. |
| 136 | const previousTriggerContext = params.choiceExecutor.triggerContext; |
| 137 | if (previousTriggerContext == null) { |
| 138 | params.choiceExecutor.triggerContext = { activeFile: file }; |
| 139 | } |
| 140 | |
| 141 | try { |
| 142 | const interactive = !params.templatePath; |
| 143 | let source: TemplatePickerItem; |
| 144 | if (params.templatePath) { |
| 145 | source = { kind: "file", path: params.templatePath }; |
| 146 | } else { |
| 147 | const picked = await pickTemplate(app, plugin); |
| 148 | if (!picked) return null; |
| 149 | source = picked; |
| 150 | } |
| 151 | |
| 152 | const templatePath = |
| 153 | source.kind === "choice" ? source.choice.templatePath : source.path; |
| 154 | |
| 155 | if (!isMarkdownTemplatePath(templatePath)) { |
| 156 | new Notice( |
| 157 | "QuickAdd: Only markdown templates can be applied to a note. Canvas and base templates create their own file types.", |
| 158 | ); |
| 159 | return null; |
| 160 | } |
| 161 | |
| 162 | const noteContent = await app.vault.cachedRead(file); |
| 163 | const noteIsEmpty = isNoteEffectivelyEmpty(noteContent); |
| 164 | |
| 165 | let mode: TemplateInsertModeId; |
| 166 | if (params.mode) { |
| 167 | mode = params.mode; |
| 168 | } else if (noteIsEmpty) { |
| 169 | // Empty-note fast path: the most common case is a freshly created |
| 170 | // blank note, so skip the mode picker and apply the template as-is. |
| 171 | mode = "replace"; |
| 172 | } else if (!interactive) { |
| 173 | mode = "bottom"; |
no test coverage detected