Templater icon indicating copy to clipboard operation
Templater copied to clipboard

How to work together with Note Refactor? - Existing content in templates

Open simonszu opened this issue 2 years ago • 9 comments

I am trying to refactor very long notes with the Note Refactor plugin. Currently the refactor process creates a new note in a folder where in theory a Templater folder template was defined for. However, Templater isn't firing and not inserting the template. Maybe there's room for improvement.

However, and this leads me to the second alternative of just pressing my Templater hotkey combo: I did not find a way how to let Templater wrap any content which is already preexisting in the note. A great improvement would be that one can define a tag like tp.content_goes_here, and whenever the "Insert Templater template" action is triggered on a note with existing content, the existing content is wrapped inside the template, appearing at this location.

I am aware of tp.file.content but the "Insert Template" action just appends the template to the existing note, so the original content appears twice in the resulting note, first as plain content, second as part of the template with tp.file.content.

Maybe there is a way to save the content in a variable, clear the note, and recreate the content to prevent this behaviour, but i am unsure about that.

If there would be a way to wrap existing content in a template, it would be possible to let a Folder template fire even if a file with existing content would appear in a watched folder. This could also fix this feature request: https://github.com/SilentVoid13/Templater/issues/467

simonszu avatar Mar 01 '22 15:03 simonszu

Sounds like you could use app.vault.modify to replace file contents.

AB1908 avatar Mar 01 '22 22:03 AB1908

Can you demonstrate with a minimal working example? It really sounds like you're looking for something similar to chhouman's zettelizer script.

AB1908 avatar Mar 01 '22 22:03 AB1908

Sure. So, i have a folder "Zettelkasten" in my vault. Templater has a folder template for this folder, looking like this:

<%*
if (tp.file.title == "Untitled")  {
	// Wenn die Datei gerade neu erzeugt wurde, hat sie noch keinen definierten Titel, er muss nachgefragt werden
	var noteTitle = await tp.system.prompt("Titel:")
} else {
	// Wenn die Datei aus einem Link erzeugt wurde, hat sie den Linknamen als Dateinamen
	var noteTitle = tp.file.title
}
fileName = noteTitle+ " ("+tp.date.now("YYYYMMDDHHmm")+")"
await tp.file.rename(fileName)
-%>---
Created: <% tp.file.creation_date() %>
aliases: 
	- <% noteTitle %>
tags: 
---
# <% noteTitle %>

<% tp.file.cursor() %>


---

Modified: <%+ tp.file.last_modified_date("YYYY-MM-DD HH:mm") %>

The content is obviously at the location of tp.file.cursor(). This template works fine when i create a new empty note inside that folder, either via the "New Note" button, or via an empty link. If needed, it asks me for the title of the note, renames the note's file, adds a zettelkasten ID as a suffix, and an alias without the ID for better searchability of the note.

Now, i want to refactor some notes. I tried with the Note Refactor Plugin and with the Note Composer plugin. Both plugins create a new file in the vault which already has contents. Sadly the Folder templates only work for new files, so Templater is not doing anything here. This is similar to https://github.com/SilentVoid13/Templater/issues/467 where @liambresnahan complains that the Folder templates do not work if a file with contents is copied to the vault - in both cases the root cause is that a new file is appearing with content, which causes the Folder template trigger not to fire.

I assume that is because Templater does not want to overwrite existing content. Understandable, but slightly less comfortable. My workaround for now is:

  • Let the refactor plugins create new notes in a temp folder.
  • Open the notes in the temp folder and apply the template manually with the hotkey
  • Move the templated note to the Zettelkasten folder.

It would at least a bit improve my workflow if i could just open the note, mark the content which should appear in the mid of my template, hit the hotkey, and it is properly templated - in my case with the above template, just wrapped with the header and the footer, with the header renaming the note file as well.

It would drastically improve my workflow if Folder templates could be applied to new notes in a folder autmatically, regardless if they have content or not. For example, templater could introduce a tag called tp.content_goes_here or something like that. The Folder template feature could have a switch called "Apply also for notes with content" which is only toggleable if the desired Folder template contains this tag.

simonszu avatar Mar 02 '22 08:03 simonszu

@AB1908 do you have any documentation at hand for app.vault.modify? I did not find any at the Templater documentation, so i assume it is an Obisidian API endpoint? I did not find any documentation about that - maybe because i am fairly new to all that topic :D

simonszu avatar Mar 03 '22 08:03 simonszu

Ah yea, it's Obsidian API stuff. Can be a lot to digest as a beginner but here's the link: https://github.com/obsidianmd/obsidian-api

app.vault.modify(TFile, content) is also async and needs to be awaited.

AB1908 avatar Mar 03 '22 08:03 AB1908

😨 It's excellently documented. The documentation for that method is literally only just

    /**
     * @public
     */

Well, i guess i'll stick to manually modifying it.

simonszu avatar Mar 03 '22 08:03 simonszu

Nevermind, it wasn't that hard. I think i managed it :D

simonszu avatar Mar 03 '22 09:03 simonszu

would you mind sharing the code?

dredhorse avatar May 23 '22 13:05 dredhorse

Sure. This is my code:

<%* if (tp.file.title == "Untitled")  {
	// Wenn die Datei gerade neu erzeugt wurde, hat sie noch keinen definierten Titel, er muss nachgefragt werden
	var noteTitle = await tp.system.prompt("Titel:")
} else {
	// Wenn die Datei aus einem Link erzeugt wurde, hat sie den Linknamen als Dateinamen
	var noteTitle = tp.file.title
}
fileName = noteTitle+ " ("+tp.date.now("YYYYMMDDHHmm")+")"
await tp.file.rename(fileName)

var file = app.workspace.getActiveFile()
var content = await app.vault.read(file)
var header = `---
Created: ${tp.file.creation_date()}
aliases: [${noteTitle}]
tags:
---
`
var footer = `

---
Siehe auch -> 

Modified: <%+ tp.file.last_modified_date("YYYY-MM-DD HH:mm") %>
`
var newContent = (header + content + footer).replace(/^(\r\n|\n\r)/, "")
await app.vault.modify(file, newContent)
%>

Of course, the content for header and footer is customizable.

Now, whenever i trigger the Templater for a specific note, it wraps its content in the header and footer, renames the file name (i have a plain layout and attach the timestamp to each filename to prevent duplicate filenames) and sets the plain filename without the timestamp as an alias, for easier searching and inter-note linking.

For the mechanism of the timestamp attaching, i also have a small logic which checks if the note already has a defined filename (this happens if the note is created by clicking on a dangling link) or if it is created manually. In the later case, for renaming the file, a note title is needed first, so a modal dialog asks for the desired note title name.

The only thing which is not automatically covered yet is to rename an existing note, because in that case, the filename prior to the appended timestamp needs to be changed, as well as the note alias. I havent managed to distinguish between the automatically generated alias and additional aliases which are created by hand after the automation runs.

simonszu avatar May 31 '22 06:05 simonszu