Zotero Integration – Import Templates?

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.

23 Likes