Zotero Integration – Import Templates?

That did not work, but a slight modification of your code did the trick:

{% for a in creators %} [[{{a.firstName}} {{a.lastName}}]]{% if not loop.last %}, {% endif %}{% endfor %}

Thanks a lot!

1 Like

Thank you both for your help, it is much appreciated. I hope you have a nice day/week :slight_smile:

Hey everyone!
I just wanted to let you know that I’ve completely updated my template, merged annotations and notes back together into one big literature note, and have moved it into a template repository. You can find the new version here: obsidian-templates/zotero.md at main · lguenth/obsidian-templates · GitHub

I’m really happy with this approach. Index at the top, then a brief metadata box and the annotations at the bottom, grouped by headings so you can collapse them. Also using callouts now instead of bullet points because I find it easier to select & copy quotes from them.

Features:

  1. Handles both the Zotero colors from the built-in PDF viewser as well as the color categories added by the ZI plugin. This fixes the issue with Zotero’s purple color being grouped as “Blue”, for examples.
  2. Annotations are grouped by colors, with custom headings for each. And: You can choose a custom sort order for them, so if you want main ideas first and caveats/tasks at the bottom, you can do that now. They’re not ordered by their appearance in the paper anymore.
  3. Identifies two annotation categories (highlight or image). If you want strikethroughs/underlines etc take a look at mgmeyers template, for example.
  4. All the usual stuff like persisting old annotations and only addings new highlights on re-import.
  5. Doesn’t overwrite the note/index and metadata section at the top at re-import!

Here’s what that looks like:

13 Likes

Really like this template, thanks for putting in the effort! A question and a comment:

  1. Question first: Just to make sure, from looking at the template it looks like the Index section you mention is for me to write directly in the note - or is it supposed to get that from anywhere?
  2. If I re-import the same paper twice, the metadata section disappears on the second import. Removing the {% if isFirstImport %} (and {% endif %}) in the beginning of the note solves it. Did you have that in there for some reason, or is it just a small bug?
2 Likes

Hey! I’m glad you like it!
The index part as well as the metadata part are space for my own notes and comments. The index is my approach to Zettelkasten-style literature notes, where I note down claims/ideas/concepts from specific pages.
The isFirstImport should make it so that my handwritten notes never get overwritten… at least that was the plan. But you’re right, there seems to be a bug. I’ll update the template and comment here once I’ve solved it.

Hi lovely people! Thank you all for your sharing. I’ve been obsessed with the Zotero-Obsidian workflow lately and found this very intriguing. Inspired by the beautiful template by @apfelstrudelig and the recent youtube video by @tallguyjenks, I made my template of the ultimate version.

Most importantly, the problem with note overwriting from re-import is fixed (which I believe has also been reported by @roaldarbol )

This is what it looks like

Metadata with dataview of related items:

Notes section and Annotation section:

More callouts and images

This is my template:

---
aliases: {{citekey}}
publisher: "{{publicationTitle}}"
url: {{url}}
doi: {{DOI}}
tags: [literature-note, zotero, {{publicationTitle | replace(" ", "-")}}, {% for t in tags %}{{t.tag}}{% if not loop.last %}, {% endif %}{% endfor %}]
---

# {{title}}

> [!abstract]- 
> {% if abstractNote %} 
> {{abstractNote|replace("\n"," ")}}
> {% endif %}

---
---
## Index:
### Metadata
> [!meta]- Metadata – {% for attachment in attachments | filterby("path", "endswith", ".pdf") %}[PDF{% if not loop.first %} {{loop.index}}{% endif %}]({{attachment.desktopURI|replace("/select/", "/open-pdf/")}}){% if not loop.last %}, {% endif %}{% endfor %}
> **Title**:: {{title}}  
> **Authors**:: {%- for creator in creators %} {%- if creator.name == null %} {{creator.firstName}} {{creator.lastName}}, {%- endif -%} {%- if creator.name %}**{{creator.creatorType | capitalize}}**:: {{creator.name}}{%- endif -%}{%- endfor %}
> **Year**:: {{date | format("YYYY")}} 
> {%- if itemType %}**ItemType**:: {{itemType}}{%- endif %}  
> {%- if itemType == "journalArticle" %}**Journal**:: *{{publicationTitle}}* {%- endif %} {%- if itemType == "bookSection" %}**Book**:: {{publicationTitle}} {%- endif %}
> {% if hashTags %}**Keywords**:: {{hashTags}}{% endif %}
> **Related**:: {% for relation in relations -%} {%- if relation.citekey -%} [[{{relation.citekey}}]], {% endif -%} {%- endfor%}

---
---
{% persist "notes" %}{% if isFirstImport %}

## Main ideas:
- 
## Methodology:
- 
## Results:
- 
## Key points:
- 
{% endif %}{% endpersist %}
## Reading notes
{% persist "annotations" %}
{%-
    set zoteroColors = {
        "#ff6666": "red",
        "#f19837": "orange",
        "#5fb236": "green",
        "#ffd400": "yellow",
        "#2ea8e5": "blue",
        "#a28ae5": "purple",
        "#e56eee": "magenta",
        "#aaaaaa": "grey"
    }
-%}

{%-
   set colorHeading = {
		"red": "⭕ Very important or questionable",
		"orange": "⭐ Important or interesting",
		"green": "✅ Major statements",
		"yellow": "📚 Ordinary notes",
        "blue": "🔗 Interesting references",
        "purple": "🧩 Methodology",
        "other": "Misc"
   }
-%}

{%- macro calloutHeader(type) -%}
    {%- switch type -%}
        {%- case "highlight" -%}
        Highlight
        {%- case "image" -%}
        Image
        {%- default -%}
        Note
    {%- endswitch -%}
{%- endmacro %}

{%- set newAnnot = [] -%}
{%- set newAnnotations = [] -%}
{%- set annotations = annotations | filterby("date", "dateafter", lastImportDate) %}

{% if annotations.length > 0 %}
*Imported: {{importDate | format("YYYY-MM-DD HH:mm")}}*

{%- for annot in annotations -%}

    {%- if annot.color in zoteroColors -%}
        {%- set customColor = zoteroColors[annot.color] -%}
    {%- elif annot.colorCategory|lower in colorHeading -%}
    	{%- set customColor = annot.colorCategory|lower -%}
    {%- else -%}
	    {%- set customColor = "other" -%}
    {%- endif -%}

    {%- set newAnnotations = (newAnnotations.push({"annotation": annot, "customColor": customColor}), newAnnotations) -%}

{%- endfor -%}

{#- INSERT ANNOTATIONS -#}
{#- Loops through each of the available colors and only inserts matching annotations -#}
{#- This is a workaround for inserting categories in a predefined order (instead of using groupby & the order in which they appear in the PDF) -#}

{%- for color, heading in colorHeading -%}
{%- for entry in newAnnotations | filterby ("customColor", "startswith", color) -%}
{%- set annot = entry.annotation -%}

{%- if entry and loop.first %}

### {{colorHeading[color]}}
{%- endif %}

> [!quote{{"|" + color if color != "other"}}]+ {{calloutHeader(annot.type)}} ([page. {{annot.pageLabel}}](zotero://open-pdf/library/items/{{annot.attachment.itemKey}}?page={{annot.pageLabel}}&annotation={{annot.id}}))

{%- if annot.annotatedText %}
> {{annot.annotatedText|nl2br}} {% if annot.hashTags %}{{annot.hashTags}}{% endif -%}
{%- endif %}

{%- if annot.imageRelativePath %}
> ![[{{annot.imageRelativePath}}]]
{%- endif %}

{%- if annot.ocrText %}
> {{annot.ocrText}}
{%- endif %}

{%- if annot.comment %}
> - **{{annot.comment|nl2br}}**
{%- endif -%}

{%- endfor -%}
{%- endfor -%}
{% endif %}
{% endpersist -%}

A few remarks:

  1. Some of the colors are customed by the theme, you can be creative with these
  2. I prefer hashtags to links at the moment, so most of the tags are in the hashtag format, but if you prefer links that come with more degrees of freedom, you are welcome to change those
  3. The handwritten notes part (main ideas, methodology…) is bounded by
{% persist "notes" %}{% if isFirstImport %}
{% endif %}{% endpersist %}

so that the template only creates these tiles when the file is imported the first time. Thus you can edit this section later on without worrying about overwriting.
4. The annotation section is mostly inspired by the template from @apfelstrudelig , so I’ll just spare the details (no clue about the details haha)

12 Likes

NOTE: Somehow the code block for the dataview is messing with the code block in my answer above, so here I just put up a screenshot (which is rather simple):

you just need to put it under the Metadate part and that’s it (or you can move it somewhere you like or just forget about it)

Cheers!

PS. The theme is AnuPpuccin with a few style settings

2 Likes

Thanks for sharing this, really useful :slight_smile: One thing that happens to me is that if I use your template, I get all the headers and the heading symbols as in your picture, but everything is in a grey colour, meaning that Major Statements are in grey and Ordinary notes are in grey too, as opposed to having green and yellow callout boxes. Any idea on why this is the case, as I am not changing your template?

Hey @tophee, sorry for not responding earlier. I appreciate your thoughts and feedback.


:exclamation: EDIT: A newer version of the template is available in this gist on GitHub. It features many improvements over the template shared here. Original message continues below:


You and others may appreciate an update of the list-based template, now that I returned to Zotero Integration until Obsidian-Zotero is more stable.

I’ve addressed your comment on the highlights / colors, and made a host of other improvements (see below). I also found it a bit limiting to have all the ==highlights== in the same color, and the readability wasn’t great. Plus, like you mention, it meant forgoing the ability to highlight within the highlight, which I hadn’t considered.

Main template features

  1. As before, annotations and comments are formatted as a bullet list, where comments are listed in bold before the highlight they belong to, which is indented below and highlighted, now using List callouts.
  2. If a highlight or post-it comment has tags, those are listed next it.
  3. The annotations are grouped by color under different headings. However, I also made an ungrouped purely sequential template.
  4. Comments that start with “todo” get formatted into proper tasks.

Benefits of List Callouts

As mentioned, my new approach is to use another plugin by @mgmeyers (yeah, couldn’t get around another plugin), namely List Callouts.

List Callouts stylizes list items based on a single character placed at the beginning of the bullet (like “&” for example). This makes it is a very unobtrusive way to highlight text—unlike using Highlightr.

This offers benefits like:

  • A seamless editing experience. No hidden markdown formatting jumps around when editing the colored highlights, which is great.
  • You can replace the character with an icon of choice. After that, the trigger-character is not visible in Reading mode, Live-preview or Source-mode.
  • Furthermore, you can highlight within highlights, like @tophee mentioned.

Screenshots

Theme: Polka

Initial note state

On the left, you see the initial state of a literature note. Callouts are collapsed, and the annotation category headings are folded (see details on that below), so I can expand them as needed. On the right, you see the metadata callout expanded and the Important heading unfolded, with highlights styled by List Callouts (note the little icons).

Comments and their nested highlights, with tags and tasks:

Here’s how it looks with List Callouts disabled:

“&” is what makes the highlights yellow, under my settings.

Images in callouts

All info callouts expanded

Other improvements and features

  • Better persistence: I improved persistence by having a persistent note section at the start, which is only created on first import, and persists after that (no duplication on update). I purposefully excluded the metadata callout from this persistence, because I want it to update if I make changes in Zotero.
  • Moved status and priority from YAML to inline-dataview fields:
    • Since you can’t persist the YAML, because the persist comments would make the YAML invalid, I moved my status and priority trackers to inline-dataview fields in the persistent notes.
  • The todo hack is compatible with bolding now: Before, I was unable to convert comments starting with “todo” into markdown tasks, while also bolding the comment. This has been fixed.
  • Fixed the following issues with the list:
    • Image comments being duplicated (under the image and as a first level bullet).
    • Post-it comments (without highlights) not having page numbers and tags.
  • Use @apfelstrudelig’s alias snippet from here.
  • Condensed the metadata callout.
  • Added collections as [[links]] in the metadata.
  • Also converted author names to [[links]] in the metadata.
  • Use cursor placeholders from Templater
    • This allows me to quickly edit fields like status and priority after importing.
  • Bold the words Objective, Methodology, Method, Results, Conclusion if they appear in the abstract.
  • Use the Creases plugin to fold the color headings when creating the literature note.
    • This allows me to unfold them as necessary.
    • If you don’t want this feature, remove %% fold %% from the template.
  • Placed images in callouts to make them stand out
    • Alternatively, this also makes them collapsible.
  • Update 2023-06-03 Fixed Bibliographies with numbered styles
    • The fix is to replace line-breaks with a single space.
  • Update 2023-06-05 Added “Citations” callout with query for the current citekey
    • This is to see where I’ve cited the current item in my own writing.

How it works

To make a template that utilizes List callouts, I made a macro (“calloutCharacter”) similar to the one for creating headings based on colors. But rather than headings, it inserts the right character for each color at the start of each highlight.

It requires some setup to make it work for you! I’ve simplified the setup based on feedback from @galachus. Now, the calloutCharacter macro matches the seven default character/color combos of List callouts—except for magenta (hex: #e56eee), which there is no default List Callout for. All the other colors will work out of the box, if you just install List Callouts and leave it at that.

So, the simplified steps are:

  1. Install and enable List Callouts. Done :tada:

Optionally, you can also:

  1. Create a List Callout type for Magenta (recommended).

    • I’ve set the character for magenta to be in the template. You can either use that in List Callouts’ settings when creating the magenta List Callout type, or choose something else.
    • If you pick a different character for magenta, remember to change it in the template too.
  2. Replace the trigger-characters with icons in List Callouts’ settings (recommended).

    • This will render the characters as icons, as you can see in my screenshots.
  3. Customize which characters should correspond to which List Callout.

    • For example, I use blue highlights for questions, so I picked ? as the character for the blue list callout.

Of course, you should also change the headings to fit your needs in the heading(color) macro.

See this table for reference on which hex code corresponds to which color. This is useful when (optionally) customizing the calloutCharacter macro, and the heading(color) macro:

colorCategory color
Yellow ffd400
Red ff6666
Green 5fb236
Blue 2ea8e5
Purple a28ae5
Magenta e56eee
Orange f19837
Grey aaaaaa

The grouped template

Click the copy button in the upper-right corner of the code block to grab the template.

---
citekey: {{citekey}}
aliases: ["
    {%- if creators -%}
        {{creators[0].lastName}}
        {%- if creators|length > 1 %} et al.{% endif -%}
    {%- endif -%}
    {%- if date %} ({{date | format("YYYY")}}){% endif -%} 
    {%- if shortTitle %} {{shortTitle | safe}} {%- else %} {{title | safe}} {%- endif -%}
"]
title: "{{title}}"
authors: {{authors}}
tags: [literature-note, {% for t in tags %}{{t.tag}}{% if not loop.last %}, {% endif %}{% endfor %}]
year: {{date | format("YYYY")}}
publisher: "{{publicationTitle}}"
doi: {{DOI}}
---

# [{{title}}]({{desktopURI}})

{% persist "notes" %}
{% if isFirstImport %}
## Key takeaways
- <% tp.file.cursor(1) %>

## Processing

- **Status**:: <% tp.file.cursor(2) %>
- **Priority**:: <% tp.file.cursor(3) %>
- **Connections**:: <% tp.file.cursor(4) %>
{% endif %}{% endpersist %}

> [!info]- Info - [**Zotero**]({{desktopURI}}) | [**DOI**](https://doi.org/{{DOI}}) | {% for attachment in attachments | filterby("path", "endswith", ".pdf") %}[**PDF**](file:///{{attachment.path | replace(" ", "%20")}}){%- endfor %}
>
> {% if bibliography %}**Bibliography**: {{bibliography|replace("\n"," ")}}{% endif %}
> 
> **Authors**:: {% for a in creators %} [[{{a.firstName}} {{a.lastName}}]]{% if not loop.last %}, {% endif %}{% endfor %}
> 
> {% if hashTags %}**Tags**: {{hashTags}}{% endif %}
> 
> **Collections**:: {% for collection in collections %}[[{{collection.name}}]]{% if not loop.last %}, {% endif %}{% endfor %}
> 
> **First-page**: {% for annotation in annotations %}{% if loop.first %}{{annotation.pageLabel}}{% endif %}{% endfor %}

> [!abstract]-
> {% if abstractNote %}
> {{abstractNote|replace("\n"," ")|striptags(true)|replace("Objectives", "**Objectives**")|replace("Background", "**Background**")|replace("Methodology", "**Methodology**")|replace("Results","**Results**")|replace("Conclusion","**Conclusion**")}}
> {% endif %}

> [!quote]- Citations
> 
> ```query
> content: "@{{citekey}}" -file:@{{citekey}}
> ```
 
---

## Reading notes
{% macro heading(color) -%}
{%- if color == "#5fb236" -%}
💡 Main ideas and conclusions
{%- endif -%}
{%- if color == "#2ea8e5" -%}
❔ Questions
{%- endif -%}
{%- if color == "#ffd400" -%}
⭐ Important
{%- endif -%}
{%- if color == "#a28ae5" -%}
🧩 Definitions and concepts
{%- endif -%}
{%- if color == "#ff6666" -%}
⛔ Weaknesses and caveats
{%- endif -%}
{%- if color == "#e56eee" -%}
⚡ Hypotheses
{%- endif -%}
{%- if color == "#f19837" -%}
⚙️ Method
{%- endif -%}
{%- if color == "#aaaaaa" -%}
📣 Survey instruments
{%- endif -%}
{%- endmacro -%}

{% macro calloutCharacter(color) -%}
{%- if color == "#5fb236" -%}
$
{%- endif -%}
{%- if color == "#2ea8e5" -%}
@
{%- endif -%}
{%- if color == "#ffd400" -%}
&
{%- endif -%}
{%- if color == "#a28ae5" -%}
~
{%- endif -%}
{%- if color == "#ff6666" -%}
!
{%- endif -%}
{%- if color == "#e56eee" -%}
€
{%- endif -%}
{%- if color == "#f19837" -%}
?
{%- endif -%}
{%- if color == "#aaaaaa" -%}
%
{%- endif -%}
{%- endmacro -%}

{% persist "annotations" %}
{% set annotations = annotations | filterby("date", "dateafter", lastImportDate) -%}
{% if annotations.length > 0 %}
*Imported on {{importDate | format("YYYY-MM-DD HH:mm")}}*

{% for color, annotations in annotations | groupby("color") -%}

### {{heading(color)}} %% fold %%
{% for annotation in annotations -%}
{%- if annotation.imageRelativePath %}

> [!cite]+ Image [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}})
> ![[{{annotation.imageRelativePath}}]]{% if annotation.hashTags %}
> {{annotation.hashTags}}{% endif %}{%- if (annotation.comment or []).indexOf("todo ") !== -1 %}
> - [ ] **{{annotation.comment | replace("todo ", "")}}**{% else %}
> **{{annotation.comment}}**{%- endif -%}

{% elif (annotation.comment or []).indexOf("todo ") !== -1 %}
- [ ] **{{annotation.comment | replace("todo ", "")}}**:{% if not annotation.annotatedText %} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}){% else %}
	- {{calloutCharacter(annotation.color)}} {{annotation.annotatedText | nl2br}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}) {% if annotation.hashTags %}{{annotation.hashTags}}{% endif -%}{% endif -%}
{% elif annotation.comment %}
- **{{annotation.comment}}**:{% if not annotation.annotatedText %} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}){% else %}
	- {{calloutCharacter(annotation.color)}} {{annotation.annotatedText | nl2br}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}) {% if annotation.hashTags %}{{annotation.hashTags}}{% endif -%}{% endif %}
{%- elif annotation.annotatedText %}
- {{calloutCharacter(annotation.color)}} {{annotation.annotatedText | nl2br}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}) {% if annotation.hashTags %}{{annotation.hashTags}}{% endif %}
{%- endif -%}{%- endfor %}

{% endfor -%}
{% endif %}
{% endpersist %}

The ungrouped / sequential template

This template simply displays the annotations in one long list in the same sequence they appear in the source. The highlights are colored using List Callouts.

Note that the grouped template is my main focus, so I may not have implemented everything listed in this sequential template. Click the copy button in the upper-right corner of the code block to grab the template.

---
citekey: {{citekey}}
aliases: ["
    {%- if creators -%}
        {{creators[0].lastName}}
        {%- if creators|length > 1 %} et al.{% endif -%}
    {%- endif -%}
    {%- if date %} ({{date | format("YYYY")}}){% endif -%} 
    {%- if shortTitle %} {{shortTitle | safe}} {%- else %} {{title | safe}} {%- endif -%}
"]
title: "{{title}}"
authors: {{authors}}
tags: [literature-note, {% for t in tags %}{{t.tag}}{% if not loop.last %}, {% endif %}{% endfor %}]
year: {{date | format("YYYY")}}
publisher: "{{publicationTitle}}"
doi: {{DOI}}
---

# [{{title}}]({{desktopURI}})

{% persist "notes" %}
{% if isFirstImport %}
## Key takeaways
- <% tp.file.cursor(1) %>

## Processing

- **Status**:: <% tp.file.cursor(2) %>
- **Priority**:: <% tp.file.cursor(3) %>
- **Connections**:: <% tp.file.cursor(4) %>
{% endif %}{% endpersist %}

> [!info]- Info - [**Zotero**]({{desktopURI}}) | [**DOI**](https://doi.org/{{DOI}}) | {% for attachment in attachments | filterby("path", "endswith", ".pdf") %}[**PDF**](file:///{{attachment.path | replace(" ", "%20")}}){%- endfor %}
>
> {% if bibliography %}**Bibliography**: {{bibliography|replace("\n"," ")}}{% endif %}
> 
> **Authors**:: {% for a in creators %} [[{{a.firstName}} {{a.lastName}}]]{% if not loop.last %}, {% endif %}{% endfor %}
> 
> {% if hashTags %}**Tags**: {{hashTags}}{% endif %}
> 
> **Collections**:: {% for collection in collections %}[[{{collection.name}}]]{% if not loop.last %}, {% endif %}{% endfor %}
> 
> **First-page**: {% for annotation in annotations %}{% if loop.first %}{{annotation.pageLabel}}{% endif %}{% endfor %}

> [!abstract]-
> {% if abstractNote %}
> {{abstractNote|replace("\n"," ")|striptags(true)|replace("Objectives", "**Objectives**")|replace("Background", "**Background**")|replace("Methodology", "**Methodology**")|replace("Results","**Results**")|replace("Conclusion","**Conclusion**")}}
> {% endif %}

> [!quote]- Citations
> 
> ```query
> content: "@{{citekey}}" -file:@{{citekey}}
> ```

## Reading notes
{% macro calloutCharacter(color) -%}
{%- if color == "#5fb236" -%}
$
{%- endif -%}
{%- if color == "#2ea8e5" -%}
@
{%- endif -%}
{%- if color == "#ffd400" -%}
&
{%- endif -%}
{%- if color == "#a28ae5" -%}
~
{%- endif -%}
{%- if color == "#ff6666" -%}
!
{%- endif -%}
{%- if color == "#e56eee" -%}
€
{%- endif -%}
{%- if color == "#f19837" -%}
?
{%- endif -%}
{%- if color == "#aaaaaa" -%}
%
{%- endif -%}
{%- endmacro -%}

{% persist "annotations" %}
{% set annotations = annotations | filterby("date", "dateafter", lastImportDate) -%}
{% if annotations.length > 0 %}
*Imported on {{importDate | format("YYYY-MM-DD HH:mm")}}*
{% for annotation in annotations -%}
{%- if annotation.imageRelativePath %}

> [!cite]+ Image [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}})
> ![[{{annotation.imageRelativePath}}]]{% if annotation.hashTags %}
> {{annotation.hashTags}}{% endif %}{%- if (annotation.comment or []).indexOf("todo ") !== -1 %}
> - [ ] **{{annotation.comment | replace("todo ", "")}}**{% else %}
> **{{annotation.comment}}**{%- endif %}

{%- elif (annotation.comment or []).indexOf("todo ") !== -1 %}
- [ ] **{{annotation.comment | replace("todo ", "")}}**:{% if not annotation.annotatedText %} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}){% else %}
	- {{calloutCharacter(annotation.color)}} {{annotation.annotatedText | nl2br}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}) {% if annotation.hashTags %}{{annotation.hashTags}}{% endif -%}{% endif -%}
{% elif annotation.comment %}
- **{{annotation.comment}}**:{% if not annotation.annotatedText %} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}){% else %}
	- {{calloutCharacter(annotation.color)}} {{annotation.annotatedText | nl2br}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}) {% if annotation.hashTags %}{{annotation.hashTags}}{% endif -%}{% endif %}
{%- elif annotation.annotatedText %}
- {{calloutCharacter(annotation.color)}} {{annotation.annotatedText | nl2br}} [(p. {{annotation.pageLabel}})](zotero://open-pdf/library/items/{{annotation.attachment.itemKey}}?page={{annotation.pageLabel}}&annotation={{annotation.id}}) {% if annotation.hashTags %}{{annotation.hashTags}}{% endif %}
{%- endif -%}{%- endfor %}

{% endif %}
{% endpersist %}

Planned enhancements

  • Adopt @apfelstrudelig’s way of creating a custom order of the headings. This would aid processing, as the literature notes are more uniform that way.
  • Perhaps import handwritten / scribbled notes. Don’t know if it’s possible?
  • Perhaps make the macro in a cleaner way.
  • Change the alias so that two authors are separated by “and” rather than suffix the first author with et al…
  • Use colorCategory rather than color to make it easier to adjust and understand the template.

PAQ (Potentially asked questions)

  • What is the todo hack?
    • It’s a way to convert comments that start with “todo” into proper markdown tasks: - [ ]
    • To use it, just write “todo” at the start of comments when annotating, in lowercase. When importing, the comment will be formatted as a task.
    • You can change the following part of the code to have another keyword trigger task formatting. Change both instances of “todo”:
{% elif (annotation.comment or []).indexOf("todo ") !== -1 %}
- [ ] **{{annotation.comment | replace("todo ", "")}}** [...]

Something unclear?

Feel free to let me know if something is unclear or if you are having trouble trying out my template.

If you don’t know how set up Zotero Integration so it can use these templates, check out this explainer by @DannyHatcher: https://youtu.be/CGGeMrtyjBI

Lists looking too condensed?

I’ve been lucky that the theme I used has a bit more list spacing than some others. For those of you with themes where the List callouts are not sufficiently spaced (Minimal and AnuPpuccin, for example), you change the list spacing using CSS.

To limit the CSS change to your literature notes, you have to use a cssclass. Add this line to the yaml frontmatter of your literature note template: cssclasses: literature-note

Then, in the Snippets folder, located in the .obsidian folder, create a CSS file called something like lit-note-lists.css (for example). To make a CSS file, you create a plaintext file, and change the file extension to CSS.

Paste this as the content of the CSS file:

.literature-note {
  --list-spacing: 0.3em; /* default 0.075em; */
}

.literature-note designates that this applies to a CSS class called literature-note. You can change the CSS class name to whatever you like, but the change must be reflected in the Properties / frontmatter of the literature note template as well.

After making the CSS snippet in the Snippets folder, the last step is to enable it in Obsidian. Settings → Appearance → Scroll down to snippets, and toggle the snippet on.

If necessary, change the 0.3em value, until you are satisfied with the resulting spacing. It may be necessary to refresh the snippets under Settings → Appearance.

Thanks to @galachus for the CSS snippet above.

20 Likes

Excellent work on this. You’ve hooked me on images in callouts. I think there were a few color/symbol mismatches in the calloutCharacter macro originally, but it seems you fixed that in the templates. I thought I was losing my mind for a moment, thinking “Wait, wasn’t orange here?”

So…

  • , @{{citekey}} is present in the YAML aliases for the ungrouped/chronological template but not the grouped template.

  • The - is missing before the [ ] for the todo hack in the image element.

These may be intentional, causing other issues if changed. They’re just a few things that jumped out at me on first use.

1 Like

After reading all these beautiful templates I decided to put mine here as well. It is slightly simpler then most, but has most of the same functionality.

My main use is block-references. I want to be able to reuse zotero highlights in other notes. For this every annotation, image etc has it’s own (static) id (´^123456´)

Note: if you edit an annotation, the block-id will change!

Supports:

  • Authors clickable
  • Links back to Zotero, so easy to jump back and forth
  • Related items (from zotero) added, both local and zotero
  • Item history, so I can see when I read those papers (linked to dates in my journal)
  • Comments, with their own tags. They are in the same block as the annotations for easy reuse
  • block-ids (^12345) !!!
  • All items are grouped by color
  • Images, with notes and tags (and block-ids)
  • Persistent notes-block
  • NEW persistent annotations

NOTICE: persistent annotations are in the gist version from version 14. It works by creating a persistent block for every individual annotation. For this to work, this template has to be the one creating the page! If a page already exists, rename or remove it. This is a work in progress!!

The papers note:

The annotations linked in another note:

Github gist with the latest version, the gist gets updated, and is already ahead of the version here!

---
title: "{{title | safe}}"
alias: {% if ShortTitle %}"{{shortTitle | safe}}"{% endif %}
Year: {{date | format("YYYY")}}
tags: type/source
Authors: {{authors}}{{directors}}
---
{#- These can be changed -#}
{#- This is the order in which the annotations are ordered -#}
{%-
   set categoryHeading = {
		"orange":  "Main ideas and conclusions",
        "yellow":  "Ordinary notes",
		"blue":    "Quote / quotable",
		"green":   "Important To Me",
		"red":     "Disagree With Author",
        "purple":  "Interesting side-point",
        "magenta": "Methodology",
		"grey":    "Definitions and concepts"
   }
-%}
{%-
    set categoryIcon = {
        "orange": "💡",
        "yellow": "📚",
        "blue": "💬",
        "green": "💚",
        "red": "⛔",
        "purple": "💭",
        "magenta": "⚙️",
        "grey": "🧩"
    }
-%}
{#- ---------- Don't make any changes under here --------- #}
{%- macro minEditDate() -%}
   {%- set tempDate = "" -%}
	{%- for a in annotations -%}
		{%- set testDate = a.date | format("YYYY-MM-DD#HH:mm:ss") -%}
		{%- if testDate < tempDate or tempDate == ""-%}
			{%- set tempDate = testDate -%}
		{%- endif -%}
	{%- endfor -%}
    {%- for a in notes -%}
		{%- set testDate = a.dateModified | format("YYYY-MM-DD#HH:mm:ss") -%}
		{%- if testDate < tempDate or tempDate == ""-%}
			{%- set tempDate = testDate -%}
		{%- endif -%}
	{%- endfor -%}
	{{tempDate }}
{%- endmacro -%}
{# infer latest note date #}
{%- macro maxEditDate() -%}
   {%- set tempDate = "" -%}
	{%- for n in annotations -%}
		{%- set testDate = n.date | format("YYYY-MM-DD#HH:mm:ss") -%}
		{%- if testDate > tempDate or tempDate == ""-%}
			{%- set tempDate = testDate -%}
		{%- endif -%}
	{%- endfor -%}
	{%- for n in notes -%}
		{%- set testDate = n.dateModified | format("YYYY-MM-DD#HH:mm:ss") -%}
		{%- if testDate > tempDate or tempDate == ""-%}
			{%- set tempDate = testDate -%}
		{%- endif -%}
	{%- endfor -%}
	{{tempDate}}
{%- endmacro -%}
{#- handle | characters in zotero strings used in MD -#}
{% macro formatCell(cellText) -%}
{{ cellText | replace("|","❕")}}
{%- endmacro %}
{#- TAGS: handle space characters in zotero tags -#}
{%- set space = joiner(' ') -%} 
{%- macro printTags(rawTags) -%}
	{%- if rawTags.length > 0 -%}
		{%- for tag in rawTags -%}
			#{{ tag.tag | lower | replace(" ","_") }} {{ space() }} 
		{%- endfor -%}
	{%- endif -%}
{%- endmacro %}
{%-
    set zoteroColors = {
        "#ff6666": "red",
        "#f19837": "orange",
        "#5fb236": "green",
        "#ffd400": "yellow",
        "#2ea8e5": "blue",
        "#a28ae5": "purple",
        "#e56eee": "magenta",
        "#aaaaaa": "grey"
    }
-%}

# {{title}}

> [!info]- Info - [**Zotero**]({{desktopURI}}) | [**DOI**](https://doi.org/{{DOI}}) | Local {% for attachment in attachments | filterby("path", "endswith", ".pdf") %}[**PDF**](file:///{{attachment.path | replace(" ", "%20")}}){%- endfor %}
> {% if ShortTitle %}Short title: {{shortTitle | safe}}{% endif %}
> Authors: {% for c in creators %}[[{{c.firstName}} {{c.lastName}}]] {% endfor %} 
> Publication: {{publicationTitle}} 
> Year: {{date | format("YYYY")}}
{% if DOI %}> DOI: **{{DOI}}**{% endif %}
> Zotero links: [Item]({{select}}) {% if pdfZoteroLink %}PDF: {{pdfZoteroLink}}{% endif %} 
> Web links: [Item]({{uri}}) {% if pdfLink %}PDF: {{pdfLink}}{% endif %} 
> {{printTags(note.tags)}}
>
> **History**
> Date item added to Zotero: [[{{dateAdded | format("YYYY-MM-DD")}}]]
> First date annotations or notes modified: [[{{minEditDate() | truncate(10,true,"")}}]]
> Last date annotations or notes modified: [[{{maxEditDate()| truncate(10,true,"")}}]]

{%- if relations.length > 0 %}
{{ "" }}
> [!abstract] Related Zotero items ({{ relations.length}}):  
>
> | title | proxy note | desktopURI |
> | --- | --- | --- |
{%- for r in relations %}
> | {{formatCell(r.title)}} | [[@{{r.citekey}}]] | [Zotero Link]({{r.desktopURI}}) | {% if rel.DOI %}> DOI: {{rel.DOI}}{% endif %} |
{%- endfor -%}
{{ "" }}
{%- endif %}

{% if notes.length > 0 %}
> [!note] Notes ({{notes.length}})
{{ "" }}
{%- for note in notes -%}
{#- Clean up note, change heading level, just in case -#}
> {{ note.note | replace ("# ","### ") }}
> {{printTags(note.tags)}}
> <small>📝️ (modified: {{ note.dateModified | format("YYYY-MM-DD") }}) [link](zotero://select/library/items/{{note.key}}) - [web]({{note.uri}})</small>
>  {{ "" }}
> ---
{% endfor %}
{% endif -%}

> [!abstract]-
> {% if abstractNote %}
> {{abstractNote|replace("\n"," ")}}
> {% endif %}

---
## Persistant notes 
{% persist "notes" %}

{% endpersist %}

---
# Annotations <small>(Exported: [[{{exportDate | format("YYYY-MM-DD")}}]]</small>)

{% for color, colorCategorie in zoteroColors %}
{%- for entry in annotations | filterby ("color", "startswith", color) -%}
{%- if entry.id %}
{%- if entry and loop.first %}## {{categoryIcon[zoteroColors[entry.color]]}} {{categoryHeading[zoteroColors[color]]}}
{% endif -%}
{%- if entry.annotatedText %}{{categoryIcon[zoteroColors[entry.color]]}} {{entry.annotatedText}}
{% endif -%} 
{%- if entry.imageBaseName %}>![[/Assets/@{{citekey}}/{{entry.imageBaseName}}|300]]<br>
{%- endif %}
{%- if entry.comment %}📝️ {{entry.comment}}
{% endif -%}
{{printTags(entry.tags)}} <small>[Page {{entry.page}}](zotero://open-pdf/library/items/{{entry.attachment.itemKey}}?page={{entry.page}}) edited:[[{{ entry.date | format("YYYY-MM-DD")}}]]</small> ^{{ entry.id | lower }}

{% endif -%}
{% endfor -%}
{% endfor -%}

````Preformatted text`
7 Likes

Same here. I can see that the template aims to specify this, but it’s just not translating into the appearance. Not sure why …

Thanks for trying out my template, and especially for the feedback!

  1. The missing dash is definitely an oversight, so you can safely add that. I will update the code above.

  2. The addition of the citekey alias was a last minute change. As I am ambivalent about it, I am not sure if I should remove it in one, or add it to the other. If you want to keep it in yours, you should surround it with quotation marks, otherwise the YAML will be invalid.

  3. However, if you have color/symbol mismatches, then you haven’t fully followed steps 2 and 3:

    1. 2 This means you just have to decide on which characters should correspond to which color, and (optionally) which icon you want to represent the color/character.
    1. Setup the macro in the template (calloutCharacter) so that the color/character combos match List callout’s settings.

My own List Callout settings match the template as shared, because I have changed the characters to suit my preferences. For example, blue means questions to me, so I changed the character for blue list callouts to ?. That’s why I specified those prerequisites for using my template above.

Granted, it would probably be more user-friendly to have the default List Callout character/color combos reflected in the macro. I just assumed people would want to customize which characters correspond to which color/category.

3 Likes

Hey,

Thank you for an amazing template. Unfortunately I cannot seem to get it so that metadata such as authors, year, DOI, etc, appear in the “info” section (see photos). Being naive, is there something I am not doing in Zotero to support this?


Wow, well done and thanks for sharing! I particularly like the list callout thing. Will try it out as soon as I can. So when you say nothing is jumping around, are you referring to the behaviour of ordinary callouts, when you have to click into the callout to be able to edit it but since that reveal’s hidden characters, things “jump around”. Would be great to get rid of that.

Just some quick questions:

This will treat all creators as authors, right? Is it intentional? If not, here is how I managed to at least distinguish between editors and authors:

{%- set editor_only = true -%}
{%- for creator in creators -%}
  {% if creator.creatorType != "editor" %}
    {%- set editor_only = false -%}
  {% endif %}
{%- endfor %}
{% if editor_only -%}
  Edited by {%- for creator in creators -%}
    {%- if creator.firstName and creator.lastName -%}
      [[{{creator.firstName}} {{creator.lastName}}]]
    {%- else -%}
      [[{{creator.name}}]]
    {%- endif -%}
    {%- if not loop.last %}, {% endif %}
  {%- endfor %}
{%- endif -%}
{% if not editor_only -%}
  By {% for creator in creators -%}
    {%- if creator.creatorType != "editor" %}
      {%- if creator.firstName and creator.lastName -%}
        [[{{creator.firstName}} {{creator.lastName}}]]
      {%- else -%}
        [[{{creator.name}}]]
      {%- endif -%}
      {%- if not loop.last %}, {% endif %}
     {%- endif -%}
     {%- endfor %}
{%- endif %}

When you say chronogical it sounds like “in the order the annotations were made”, but I assume you mean “in the sequential order they appear in the text”, right?

I was recently wondering why I ended up using the zotero integration plugin, given that Obsidian-Zotero was so powerful (should take notes on these things). Could this have been the reason? Stability?

What issues have you been experiencing?

My main issue with the zotero integration plugin is that it tends to be slow and sonetimes hangs, which is why I wanted to take a look at the bibnotes formatter plugin, which doesn’t query the Zotero database but uses a JSON file exported by Zotero (which also should work on mobile). Have you tried it?

2 Likes

I’m glad you like it, let me know how the List Callouts work out for you!

Exactly. No hidden markdown formatting appears when you go to edit a list item, which was also one of the reasons I wanted a list rather than callouts. Now that I’ve also gotten rid of the ==highlights==, the editing experience is very fluid.

Regarding the authors, I hadn’t really given it much thought, because most of my sources are journal articles where there only are authors. But I also have items like books, so I’m thankful for your snippet there and will test it out.

Right, that was an imprecision. I was thinking of changing it, but was unsure how. Sequential is exactly right.

Yes, lack of stability / reliability is a likely reason. I detailed why I was going back to ZI from OZ on Discord:

Based on my recent experiences, I would actually caution against using AidenLX’s Obsidian-Zotero plugin right now over MgMeyers’ Zotero Integration, because there are several substantial bugs in the latest version:

It’s just too unreliable and buggy, and unfortunately, the speed of the plugin can’t outweigh that. I hope to return to it soon when these bugs have been resolved, but until then, I will try Zotero Integration again.

All of the above still applies.

Yeah, I have used Bibnotes formatter quite a lot in the past. It has a lot of potential, and it was quite fast. But recently, it’s been unusable for me due to formatting bugs. I cannot get formatting of the various elements like comments and highlights correct due to these bugs. It’s not abandoned, but bug resolution has been prohibitively slow for me.

One of its main appeals to me was the complex transformation rules you could make, like converting certain comments to tasks. But, as my template demonstrates, that is also possible with ZI.

True. I compared ZI and OZ over in my thread with Obsidian-Zotero templates, mainly noting the slowness and also the annoying Zotero picker, which often opens in the background:

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.

Note: This was written before I decided to go back to ZI for the time being, as the aforementioned bugs were later introduced in OZ. I actually find ZI to be much more tolerable now, speed-wise.

1 Like

It is “folded” by default, press the ´>´ and it will fold open and show the rest of the meta data. I generally don’t use it show hide it by default.

If you don’t like that, about line 95 is this line: ´> [!info]- Info - [**Zot etc´ the minus sign right after ´[!info]´ decides start folded, or start unfolded.

BTW, if there is other meta-data missing, that does exist in Zotero, let me know, I have now only hardcoded what I use, but adding it is no problem

Folded:

Unfolded:

2 Likes

@galachus I’ve updated my template based on your feedback. Now, the template matches the default character/color combos from List Callouts. That means everything should work as expected out of the box, with no customization required beyond installing List Callouts. The only exception is if you need magenta highlights, which require a custom List callout.

3 Likes

It’s funny how we evaluate things differently (or at least describe those evaluations differently) depending on the result that we’ve made up our mind about. :sweat_smile:

Thanks for pulling in all the useful quotes from your other posts. I avoid discord as much as I can for asynchronous communication (like discussions around software issues) - it’s just not made for that - so it’s valuable to have it replicated here.

The OZ issues that you mention seem to be rather straightforward to fix (from what I can tell), genuine bugs, not big problems to solve. But, of course, it requires time to do it and the developer has been less active on github recently. Unfortunately, I don’t have the knowledge to contribute fixes.

I have adopted the annotations part of your template for my use case (which I’ll explain below):

## Reading notes
{% macro calloutCharacter(color) -%}
{%- if color == "Orange" -%}
- ~ 
{%- elif  color == "Red" -%}
- & 
{%- elif color == "Purple" -%}
- @ 
{%- elif color == "Blue" -%}
- % 
{%- elif color == "Green" %}
- $ 
{%- elif color == "Gray" -%}
- § 
{%- elif color == "Magenta" -%}
- € 
{%- else -%}
- ! 
{%- endif -%} 
{%- endmacro -%}

{% persist "annotations" %}
{% set annotations = annotations | filterby("date", "dateafter", lastImportDate) -%}
{% if annotations.length > 0 %}
*Imported on {{importDate | format("YYYY-MM-DD HH:mm")}}*
{% for annotation in annotations -%}
{% set citationLink = '[(p. ' ~ annotation.pageLabel ~ ')](zotero://open-pdf/library/items/' ~ annotation.attachment.itemKey ~ '?page=' ~ annotation.pageLabel ~ '&annotation=' ~ annotation.id ~ ')' -%}
{% set annotatedText = annotation.annotatedText | default('') | nl2br %}{% if annotation.colorCategory == "Red" -%}{% set annotatedText = '[[' ~ annotatedText ~ ']]' -%}{% endif -%}
{% set annotationTags = '' -%}{% if annotation.tags.length > 0 -%}{%- for t in annotation.tags -%}{%- set annotationTags = annotationTags ~ '#[[' ~ t.tag | lower ~ ']] ' -%}{% endfor -%}{% endif -%}
{% if annotation.imageRelativePath %}

> [!cite]+ Image {{citationLink}}
> ![[{{annotation.imageRelativePath}}]]{% if annotationTags.length > 0 %}
> {{annotationTags}}{% endif %}{%- if (annotation.comment or []).indexOf("todo ") !== -1 %}
> - [ ] **{{annotation.comment | replace("todo ", "")}}**{% else %}
> **{{annotation.comment}}**{%- endif %}

{%- elif (annotation.comment or []).indexOf("todo ") !== -1 %}
- [ ] **{{annotation.comment | replace("todo ", "")}}**:{% if not annotation.annotatedText %} {{citationLink}}{% else %}
  {{calloutCharacter(annotation.colorCategory)}} {{annotatedText}}  {{citationLink}} {{annotationTags}}{% endif -%}
{% elif annotation.comment -%}
- **{{annotation.comment}}**:{% if not annotation.annotatedText %} {{citationLink}}{% else %}
  {{calloutCharacter(annotation.colorCategory)}} {{annotatedText}}  {{citationLink}} {{annotationTags}}{% endif -%}
{% elif annotation.annotatedText -%} {{calloutCharacter(annotation.colorCategory)}} {{annotatedText}}  {{citationLink}} {{annotationTags}}{% endif %}
{% endfor %}

{% endif %}
{% endpersist %}

What changes did I make?

  • I simplified the code a bit by using variables for repeated code
  • I moved the entire list-items into the macro. i.e. I’m using the macro not just to generate the symbol for each colour but also the dash before it. This was necessary because I use green highlights for headings (i.e. they represent the structure of the annotated text) and I wanted those to have an empty line before them so that the structure of the text is somewhat visualized. Caveat: The empty lines are removed in reading mode. Will need to figure out how to maintain them. I might resort to rendering them as headings.
  • Also the red highlights get special treatment because I use those for keywords in the text. These are rendered as internal links. Having the annotated text as a variable made it easier to put the square brackets around them.
  • Since I am using tags for structural/organizational things only (i.e. for concepts/content), I don’t want the Zotero tags to be rendered as tags but as internal links. To achieve that, I’m using the tags field instead of hashTags field.
  • I have switched to using colorCategory instead of color because I have a lot of pdfs that I annotated outside of Zotero and that are using slightly different shades of red and other colours and it looks like Zotero nevertheless puts them into the correct colour category (despite different hex codes). I have not tested this properly yet, so no guarantees.
  • Finally, I changed the macro from separate if-statements to one with multiple else-if conditions. The main purpose of this was to have a default option in case some undefined colour comes along.

One thing that is driving me nuts with Nunjucks templates is how difficult it is to control the unwanted whitespace they produce. I usually end up removing as many line breaks as possible, just to reduce the possible sources of error but the price for this is that the code becomes much more difficult to read. I can’t seem to wrap my head around how the {%-and -%}` affect the final output and using a lot of if-statements as we do doesn’t exactly make it easier to consider all possible scenarios. (So I expect there to be some whitespace-bugs in the above code. Feel free to correct them :wink: ) If there is any rule of thumb / simple algorithm for when remove whitespace and when not to, I’d be glad to learn about it.

Note that in my template snippet, the additional line before green annotations is produced by not removing the linebreak that is added after each annotation by default ({%- if color == "Green" %} instead of {%- if color == "Green" -%}.

Final thought: It strikes me that it may make more sense to not render comments as list items at all. Any reasons for keeping them as list-items?

1 Like

No problem!

Yeah, but that doesn’t cut it for my current deadlines. I am keeping a keen eye on any updates, though. So I haven’t turned my back on Obsidian-Zotero, but it is a bit of a pain each time you settle in with one system, then have to establish yourself again with another.

Great idea! I hadn’t really experimented with variables in Nunjucks yet, but that looks much cleaner, and more flexible.

Yeah, I’ve helped someone do that in their template before, and was wondering why you wouldn’t render them as headings, if that’s what green represents.

This is very interesting. I’ve been trying to come up with ways to make my highlight colors more useful and actionable, and this is a great candidate.

Could you give an example of your tag structure?

You are going to have issues with this, specifically purple being categorized as blue: Purple annotation is wrongly regarded as Blue · Issue #168 · mgmeyers/obsidian-zotero-integration · GitHub
To avoid this issue, you need to set the hex values explicitly to the colorCategories, like apfelstrudelig did here:

I am wondering why your macro works (it does), when you use color there, but not hex-values. I thought it would need to be {% macro calloutCharacter(colorCategory) -%} {%- if colorCategory == "Orange" -%}.

Yeah, I should do this to be safe, although I only use Zotero Reader at the moment.

Unfortunately no, except for intuition and trial and error.

That’s an interesting suggestion. Related to this, I noted (or perceived there to be) an indentation issue in your template, where commented-upon highlights are only indented by two spaces, rather than the four equal to a tab. Is that intentional? For me, having highlights fully indented under the comment bullet makes it easy to read.

But something like this doesn’t look too shabby, so I’m open to suggestions, if you make it work for you without bullets (just a test). Either way, I would still convert comments to tasks on occasion, but that can be achieved regardless.
image