Zotlit (previously Obsidian-Zotero) import templates?

@aidenlx’s Obsidian-Zotero plugin offers superior performance and unique features compared to the other plugins, which integrate Zotero and Obsidian.

The key difference is that the Zotero database is accessed directly, which is very fast and offers features like auto-sync of annotations to Obsidian.

It also offers advanced templating powered by Eta. However, I haven’t seen any example of such a template, and don’t have any experience with Eta templates.

I was hoping that some of you might have implemented some Eta import templates for the Obsidian-Zotero plugin that you can share.

My main interests are formatting annotations as bullet lists, placing comments before the associated highlights, and grouping annotations by color under corresponding headlines like I did in my Nunjucks template for Zotero Integration. Hopefully, someone has implemented something similar in Eta.

I’m hoping that this could be a thread similar to this one for Zotero Integration. That would be a great resource for people looking to try out and use the Obsidian-Zotero plugin.

8 Likes

Okay, I’ll go first. I managed to implement (most of) my nunjucks template for Zotero Integration in Eta for the Obsidian-Zotero plugin. See that post for details on the template.

In Obsidian-Zotero, there are three template files that get combined. There’s the template for the actual Literature note ( zt-note.eta), then the template for formatting individual annotations ( zt-annot.eta), and the a template for bulk import of annotations ( zt-annots.eta).

You need to specify the template folder path in Obsidian-Zotero settings, and then eject the template files into the template folder. After this, you can customize them.

Here’s the code and below are screenshots:

zt-note.eta:


# [<%= it.title %>](<%= it.backlink %>)


> [!NOTE]+ Info
> 
> [Open in Zotero](<%= it.backlink %>)
> [DOI](https://doi.org/<%= it.DOI %>)


> [!abstract]- Abstract
><%= it.abstractNote %>


## Notes
<%~  include("annots", it.annotations) %>

zt-annot.eta:

---
callout: false
---

<% if (it.imgEmbed) { %>
- <%= it.imgEmbed %>
<% } %>
<% if (it.comment) { %>
- <% if (it.comment.startsWith('todo ')) { %>[ ] **<%= it.comment.substring(5) %>:**<% } else { %>**<%= it.comment %>:**<% } %>  
	- ==<%= it.text %>== [p. <%= it.pageLabel %>](zotero://open-pdf/library/items/<%= it.parentItem %>?page=<%= it.pageLabel %>&annotation=<%= it.key %>)
<% } else if (it.text) { %>
- ==<%= it.text %>== [p. <%= it.pageLabel %>](zotero://open-pdf/library/items/<%= it.parentItem %>?page=<%= it.pageLabel %>&annotation=<%= it.key %>)
<% } %>

The yaml field callout: false is required here if you want to format the annotations as a bullet list, and not in individual callouts for each annotation. Without this, each line will start with >, thus breaking the formatting of the bullet list. I am not sure if this breaks some plugin behavior, but it might. See this: [FR] Simplify templating and use a single template file · Issue #101 · aidenlx/obsidian-zotero · GitHub

zt-annots.eta:

<% for (const color of ['#e56eee', '#5fb236', '#f19837', '#2ea8e5', '#ffd400', '#a28ae5', '#ff6666', '#aaaaaa']) { %>
<% let annotations = it.filter(annotation => annotation.color === color) %>
<% if (annotations.length > 0) { %>

<% if (color === '#e56eee') { %>
### ⚡ Hypotheses
<% } %>
<% if (color === '#5fb236') { %>
### 💡 Main ideas and conclusions
<% } %>
<% if (color === '#f19837') { %>
### ⚙️ Method
<% } %>
<% if (color === '#2ea8e5') { %>
### ❔ Questions
<% } %>
<% if (color === '#ffd400') { %>
### ⭐ Important
<% } %>
<% if (color === '#a28ae5') { %>
### 🧩 Definitions and concepts
<% } %>
<% if (color === '#ff6666') { %>
### ⛔ Weaknesses and caveats
<% } %>
<% if (color === '#aaaaaa') { %>
### 📣 Survey instruments
<% } %>

<% for (const annotation of annotations) { %>
<%~ include("annotation", annotation) %>
<% } %>
<% } %>
<% } %>

As for the yaml fields you want to import from Zotero, those have to be set in the plugin settings under Mapping:

Screenshots

Here’s what the result looks like:



4 Likes

Here’s a new and improved version of the templates, based on trial and error and feedback from @aidenlx.

Changes

The zt-annots.eta template was simplified and made easier to read by using the human-readable color names stored under the colorName key, rather than using hex codes. However, there is a bug where the colorName of gray is not gray but the hex code #aaaaaa.This will be fixed in the next release, according to @aidenlx. For now, I just put #aaaaaa as the name when mapping it to its heading.

I added some more information to the info callout in the zt-note.eta template. Because of a workaround for some funky whitespace behavior, which I think is due to the standard Eta configuration, the info callout looks a bit strange, but it will look right when used.

The zt-annot.eta template now also puts tags associated with annotations at the end of the annotation, after the page backlink. Furthermore, it now uses \n rather than two spaces to ensure proper linebreaks in the bullet list. This is cleaner, as \n doesn’t leave any extra spaces trailing the list items. I will share two versions of the zt-annot.eta template, one with block-ids and one without. As the block-ids don’t work in this template for now, I suggest not using them. But if these issues are fixed, then the block-id version can be used.

Template and plugin limitations

  1. Despite adding block-ids to list items, unwrapping the annotations from callouts to enabled the list format is not compatible with the In-place Update of Existing Annotations feature. As is, when updating a literature note, all annotations are reimported and duplicated. Each update adds a full set of annotations, making the literature note longer and longer. So keep that in mind. See this Github comment.

    • This is a pretty big limitation, because it leaves the Overwrite Existing Note setting as the only viable way to update literature notes. However, this means that anything added to literature notes after importing will be lost upon updating the literature note. This also excludes the possibility of linking to blocks in the literature note, as the block-ids will be lost upon updating.
  2. That brings me to another limitation: The block-ids that Obsidian-Zotero adds to annotations are not reused by Obsidian. Obsidian supports custom block-ids like ^something or even random characters like ^JKHFD4reKFG, which it will reuse if you link to that block. For some reason, this is not the case for block-ids added by Obsidian-Zotero (e.g. ^MNEN5JMTaCNYXGZ59p2). They are not reused, and Obsidian will add another block-id, if you link to a block that already has an Obsidian-Zotero block-id. See this Github comment.

  3. This limitation is not related to the template, but the plugin in general. When mapping tags as a yaml value to import under the plugin settings, only manually created tags are imported. Auto generated tags such as those made by the Zotero Tag addon will not be added to the yaml, but they are added to the info callout in the zt-note.eta template. This will probably be fixed.

  4. Some keys or metadata fields from Zotero are not available to import. This includes the Collections and the full PDF path. See this Github comment.

  5. Yaml values can only be formatted as a multi-line list, and not as single-line arrays. According to @aidenlx, this is due to limitations of stringifyYaml in the Obsidian API, but perhaps someone can contribute some solutions or workarounds in this Github issue.

Overall, there are some reliability, templating and feature implementation issues when using custom templates with the Obsidian-Zotero plugin, compared to Zotero Integration, but the result is still pretty satisfactory. The main pro is the speed. In my experience, searching for and importing a literature note is probably 10x faster using Obsidian-Zotero compared to Zotero Integration. Search and import speed is a noted issue with Zotero Integration. However, Obsidian-Zotero does offer an API with the latest release which allows other plugins to access the Zotero database through it, so that could be a solution there. Obsidian-Zotero also overcomes the annoyance of the native Zotero citation picker, which most often appears in the background rather than displaying over Obsidian.

Code

zt-note.eta

# [<%= it.title %>](<%= it.backlink %>)


> [!info]+ Links and info
> 
>**Publication:** <%= it.publicationTitle %>

>**Pages:** <%= it.pages %>

><% if (it.tags && it.tags.length > 0) { %>**Tags:** <%= it.tags.map(tag => '#' + tag.name).join(", ") %><% } %> 
> 
> [**Open in Zotero**](<%= it.backlink %>)
> [**DOI**](https://doi.org/<%= it.DOI %>)


> [!abstract]- Abstract
><%= it.abstractNote %>



## Notes
<%~  include("annots", it.annotations) %>

zt-annot.eta with block-ids

---
callout: false
---

<% if (it.imgEmbed) { %>
- <%= it.imgEmbed %> ^<%= it.blockID %>\n
<% } %>
<% if (it.comment) { %>
- <% if (it.comment.startsWith('todo ')) { %>[ ] **<%= it.comment.substring(5) %>:**<% } else { %>**<%= it.comment %>:**<% } %> ^<%= it.blockID %>\n
	- ==<%= it.text %>== [p. <%= it.pageLabel %>](zotero://open-pdf/library/items/<%= it.parentItem %>?page=<%= it.pageLabel %>&annotation=<%= it.key %>)<% if (it.tags && it.tags.length > 0) { %> <% = it.tags.map(tag => '#' + tag.name).join(", ") %><% } %>\n
<% } else if (it.text) { %>
- ==<%= it.text %>== [p. <%= it.pageLabel %>](zotero://open-pdf/library/items/<%= it.parentItem %>?page=<%= it.pageLabel %>&annotation=<%= it.key %>)<% if (it.tags && it.tags.length > 0) { %> <% = it.tags.map(tag => '#' + tag.name).join(", ") %><% } %> ^<%= it.blockID %>\n
<% } %>

zt-annot.eta without block-ids

---
callout: false
---

<% if (it.imgEmbed) { %>
- <%= it.imgEmbed %>\n
<% } %>
<% if (it.comment) { %>
- <% if (it.comment.startsWith('todo ')) { %>[ ] **<%= it.comment.substring(5) %>:**<% } else { %>**<%= it.comment %>:**<% } %>\n
	- ==<%= it.text %>== [p. <%= it.pageLabel %>](zotero://open-pdf/library/items/<%= it.parentItem %>?page=<%= it.pageLabel %>&annotation=<%= it.key %>) <% if (it.tags && it.tags.length > 0) { %> <% = it.tags.map(tag => '#' + tag.name).join(", ") %><% } %>\n
<% } else if (it.text) { %>
- ==<%= it.text %>== [p. <%= it.pageLabel %>](zotero://open-pdf/library/items/<%= it.parentItem %>?page=<%= it.pageLabel %>&annotation=<%= it.key %>) <% if (it.tags && it.tags.length > 0) { %> <% = it.tags.map(tag => '#' + tag.name).join(", ") %><% } %>\n
<% } %>

zt-annots.eta

<% 
const colorGroups = it.group(annotation => annotation.colorName);
const colorLabel = {
  'blue': '⚡ Hypotheses',
  'green': '💡 Main ideas and conclusions',
  'orange': '⚙️ Method',
  'blue': '❔ Questions',
  'yellow': '⭐ Important',
  'purple': '🧩 Definitions and concepts',
  'red': '⛔ Weaknesses and caveats',
  '#aaaaaa': '📣 Survey instruments'
};
for (const [color, annotations] of Object.entries(colorGroups)) {
%>\n
### <%= colorLabel[color] ?? 'Group' %>
\n
<% for (const annotation of annotations) { %>
<%~ include("annotation", annotation) %>
<% } %>
<% } %>

Screenshots

Metadata and link callout, abstract and annotations grouped by color under custom headings. todo at the start of comments is transformed into a proper task. Comments are bolded and annotations are highlighted using ==. This can be changed in the zt-annot.eta template. Page backlinks open the right annotation in Zotero reader.

Addition of tags:

Multi-line yaml, which is controlled in the plugin settings, not in the template files. The Zotero-key makes the plugin recognize the file as a literature note:

7 Likes

In case it helps anyone, I’ve been trying an alternative template - the main thing I wanted was authors as links and a link to directly open the PDF in Zotero.

# [<%= it.title %>](<%= it.backlink %>)
<% let id =  it.fileLink.replace(/^.*storage\//,"").replace(/\/.*/,"") %>
_<% it.authors.forEach(function(author){ %>   [[<%= author %>]] <% }) %>_

[Zotero](<%= it.backlink %>) | [PDF](zotero://open-pdf/library/items/<%= id %>) <% if (it.DOI) { %>| [DOI:<%= it.DOI %>](http://doi.org/<%= it.DOI %>) <% } %>
<% else if (it.ISBN) { %>| ISBN: <%= it.ISBN %> <% } %>
<% if (it.url) { %>| [URL](<%= it.url %>) <% } %>

 ```ad-quote
title:Abstract
<%= it.abstractNote %>

 ```
^abstract

# Notes

\n
# Annotations
<%~ include("annots", it.annotations) %>

Looks like this:

6 Likes

Hello, your template is great! I have already applied it to my own notes. However, there is a problem with one spot that doesn’t seem to work properly. I copied it below:

Do you know how to solve this issue? I’ve checked my notes and noticed that some areas don’t work when using “-”.

Hi @8wy727, thanks for the feedback. I don’t have this issue when using the template.

I tried to see what the differences might be, and it is not apparent. It could be an issue with indentation. In the code I shared, the various template elements are not indented, as they appear to be in your screenshot. Only the annotated text below the comment is indented. All the other lines should not have spaces or tabs at the start.

Another thing is that in your test here, there is not actually any highlighted text. What kind of highlights are you importing? I haven’t tested the sticky note feature (standalone comments), is that what you are importing in your test?

1 Like

Hi, I am having an issue with the templating of Obsidian-Zotero and I thought it might be relevant to this topic. Every time I close Obsidian, I lose all the templates configurations, which they go back to default.

Anybody experiencing that? Might it be related to the fact that my vault is in Google Drive for syncing?

Thanks!

Hi,
Did you export / “eject” the template files from the plugin settings into your vault?

Sorry to add another question here, but it seems relevant. I have gotten the basic templates for this plugin up and running, but I can’t seem to get the actual “notes” in Zotero to transfer over to the Obsian literature note. I get the annotations (the highlighted bits), which I don’t really need, to show up but not the actual notes I typed out while reading in Zotero. I take these notes in the Zotero PDF reader in the right sidebar, where you have to click “+” in order to add a note; then I just type everything into one long note. (To be clear, I don’t want to type my notes in Obsidian–I just want to transfer notes I’ve typed in Zotero into Obsidian to take advantage of the latter’s linking capacities.) I have tried adding an entry like “# <%= it.notes %>” to the zt-note.eta file, but no luck. Does anyone know what the syntax would be here?

I checked an item’s available metadata fields using the Annotation sidebar → Show details. The standalone notes from Zotero are not available.

I take it that this is a current limitation of the plugin.

In general, Obsidian-Zotero has fewer metadata fields available compared to Zotero Integration, as I noted in limitation 4 listed in this comment above.

1 Like

Ah that’s really too bad! Obsidian-Zotero is in many ways superior to the Integration plugin (such as speed, the ability to track highlights in a separate panel, and the ability to update notes without totally re-importing), but this is one big reason I might have to stay with Integration. Thank you for your reply.

Yeah, it’s too bad about these deal-breaker bugs and limitations.

I am also using Zotero Integration for now, and it is serving me well. But I am keeping an eye out for updates to Obsidian-Zotero, as it is very promising.

So I actually did find a plugin that does this! It may have other deal-breakers for you but, so far (last half day), it’s working pretty well for my purposes. It has a bunch of templates for keeping notes that seem to break with each update to the plugin, so that might not be worth the hassle, but the basic syncing function works smoothly, I find.

If you could share a somewhat detailed workflow (potentially open a new thread and send the link here) that’d be great. The documentation is hard for me to understand. I’ve kept an eye on it for a while and have tried to learn how it works multiple times but still don’t understand most of its features.

Could you be a bit more precise (e.g. replace “this” and “it” with what you are referring to specifically)?

It is unclear which parts of your comment refer to which plugin.

Does what?

Obsidian-Zotero or Better Notes?

Obsidian-Zotero or Better Notes?

Did you see @michaeta’s guide here? Beginner's set up for AidenLX’s Zotero Integration Plugin · GitHub

It’s currently waiting in a pull request to be added to the official documentation.

2 Likes

2 posts were split to a new topic: Zotero Better Notes plugin

I was actually referring to @macrospect’s comment before yours (I thought I had used the reply button, but apparently not). Nevertheless, less use of “it” and “this” is almost always helpful.

But my later comment to you was also because you didn’t use the reply button, so I thought you were asking me about Obsidian-Zotero. So, if anything, we might all gain from using the reply button more.

Sorry for any confusion, which seems to abound among the three of us right now.

I agree that it would be better to have the Better Notes info elsewhere to keep the thread on topic. I don’t know who can help with that.

I think you did, and I surely did, but if your reply is right next to the one you reply to, it doesn’t show the reply sign. But you told me about AidenLX Obsidian Zotero plugin, which I didn’t ask about, so I thought you were confused about my language too, which you were, just because the reply button didn’t show. And I was complacent that it’s clear that my reply was for macrospect’s comment. I’m glad we’ve cleared things up :smiley:

Regarding splitting to a new topic, apparently I can’t tag @moderators here, so I’ll try get some help from Discord. (Edit: Thanks @ CawlinTeffid!)

1 Like

Did you ever manage to get the blockID stuff working? I saw the comment on the GitHub issue that it “should” be working now, but I’m not having much luck - either it doesn’t update at all, or it ends up duplicating.