A no-plugin Insert Template button

Hi,

I’ve been quietly absorbing much of the information shared here, and I must say a huge thank you to everyone who’s answered other folks’ questions. I never had to ask any myself! This is my first exposure to Clojure, and it’s been a ride.

I’ve made a few other things, but I wanted to share this quite self-contained example as to what I’ve found you can accomplish in Logseq. I’m amazed every day!

Plugins are great, but I use my phone to take notes quite often, so I’ve been seeing what kinds of plugin-like functionality you can do with Advanced Queries (and their :view s)

After seeing this post and realizing I could combine macros with Advanced Queries, I’ve found a whole world of possibilities. (Big thanks to all those on that thread).

Ok, let’s get to it…

With the following in place, you can write,

{{insert-template Name of Template}}

and get a button that says:

Insert: Name of Template

that does what you’d expect: clicking it appends a block below it with the template populated. (Adding , erase to the end of the macro makes it delete the button too.)

Inside logseq/config.edn add to the :macros section:

 :macros
 {;; Usage: {{insert-template <template-name>,<erase-on-click>}}
  ;; where
  ;;   <template-name> is the name of the template to insert when clicked.
  ;;   <erase-on-click> is the word "erase" if the button is to be removed on click.
  ;; Examples:
  ;;     {{insert-template A Template}}
  ;;     {{insert-template My Other Template, erase}}
  :insert-template "\n#+BEGIN_QUERY
   {:inputs [:current-block]
    :query [:find ?block-uuid
            :in $ ?block
            :where [?block :block/uuid ?block-uuid]]
    :result-transform (fn [[block-uuid]]
                        [block-uuid \"$1\" (= \"$2\" \"erase\")])
    :view :insert-template-view}
   #+END_QUERY"
  ;;
  }

and the :query/views section:

 :query/views
 {;;
  ;; Displays the result data as nicely formatted text
  :pprint
  (fn [r] [:pre.code (pprint r)])
  ;;
  ;; Creates a button from a block UUID, template name and erase flag
  ;; On click, appends the specified template after the given block,
  ;; optionally removing the original block (if the erase flag is set)
  :insert-template-view
  (fn [[block-uuid template-name erase-on-click?]]
    [:button
     {:style {:border "1px solid silver" :border-radius "6px" :padding "2px 8px"}
      :on-click
      (fn [_]
        (if (call-api "exist_template" template-name)
          (do (call-api "insert_template" block-uuid template-name)
              (if erase-on-click?
                (call-api "remove_block" block-uuid)))
          (call-api "show_msg"
                    (str "Template not found:\n" template-name) :error)))}
     "Insert: " template-name])
  ;;
  }

And that’s it… almost.

The key to getting macros to work with queries is to include a newline at the top. Unfortunately, this also leaves some blank space at the top, and macros in general take up a whole block of space.

To fix this, we can turn to custom CSS. Here’s the contents of mine that fix these issues:

/* Queries inside macros only work if preceded by a blank line. This results
 * in a <br> tag being added and the query appearing lower than the bullet.
 * Remove the <br> here: */
div.macro>div:first-child>br:first-child {
  display: none;
}

/* Make certain macros appear inline rather than as separate HTML block elements */
div.macro[data-macro-name="insert-template"] {
  display: inline-block;
}

/* The insert template query has a custom view, hide the usual query view controls */
div.macro[data-macro-name="insert-template"] .custom-query .th {
  display: none;
}

I’m happy to answer any questions on how this works. I’m keen to give back to this community given how much I’ve learnt from you :slight_smile:

For now, enjoy.

Thanks,
Ian

This is very cool! :fire:

Ok so I didn’t know how much I needed this in my life! :heart_eyes:
Each week I would call on a week planning template. Being able to just push a button is gold!
I tested it out and it worked like a charm :smiling_face_with_three_hearts:

2 Likes

Wow. I am quietly (mostly) absorbing this myself. It’s late though. Will be revisiting in the morning. Regardless, Thank You.

I think I only just realized how useful this is to my own workflow.

I have a template for my daily journal that’s automatically added:

 :default-templates
 {:journals "Day"}

that includes some <% today %> -like entries.

- Day
  template:: Day
  template-including-parent:: false
    - {{daily-summary **Summary**, <% yesterday %>, <% today %>}}

If I visit a journal with a future date the <% today %> gets replaced with today’s date rather than the day of the journal.

However, by replacing that with a button to expand this from another template, I can choose to only click the button on the day when it really is today.

- Day
  template:: Day
  template-including-parent:: false
    - {{insert-template Summary, remove}}
- Summary
  template:: Summary
  template-including-parent:: false
    - {{daily-summary **Summary**, <% yesterday %>, <% today %>}}

I’m glad it’s helping others too :slight_smile:

1 Like