Inline properties and reuse of property values, basic implementation

  • Inspiration
  • Usage
    • This uses a macro with the following syntax: {{p property, value}}
      • Should not use comma (,) anywhere else.
        • Though generally the comma can be omitted: {{p property value}}
      • Omitting the value reuses the existing one: {{p property}}
    • Typing something like this (check customization below):
      {{p title "The Gray Picture"}} is a {{p type phenomenon}} discovered by {{p scientist [[Person1]]}}, {{p scientist [[Person2]]}} and {{p scientist [[Person3]]}}. {{p title}} is very rare.
      

      Should produce this:

      Notice the last macro {{p title}} , which reuses the earlier value.
  • Preparation
    • Add a macro inside file config.edn , inside macros{} :
      :p "<span class='kit' data-kit='inlineprop' data-prop='$1' data-val='$2'>$1:: $2</span>"
      
    • The code below requires having kits inside file custom.js .
    • Inside page InlineProp in Logseq, put the following code in a javascript code-block:
      logseq.kits.setStatic(function inlineprop(span){
          const dataset = span.dataset
          var propname = dataset.prop
          if (propname === "$1") propname = ""
          var val = dataset.val
          var noVal = (val === "$2")
      
          const blockId = span.closest(".ls-block").getAttribute("blockid")
          const block = logseq.api.get_block(blockId)
          const props = block.propertiesTextValues
          var old = (props && props[propname])
      
          if (noVal && old === undefined) {
              const index = propname.indexOf(" ")
              if (index > -1) {
                  val = propname.slice(index + 1)
                  noVal = (val === "")
                  propname = propname.slice(0, index)
                  old = (props && props[propname])
              }
          }
      
          const linkTarget = dataset.linkTarget
          const isLink = val.startsWith("[[") && val.endsWith("]]")
          const needsLink = !noVal && (isLink || dataset.link === "force")
          const plain = (isLink) ? val.slice(2, -2) : (noVal) ? old : val
      
          const fixval = (noVal) ? "" : val.replaceAll("[", "\\[").replaceAll("]", "\\]")
          const target = new RegExp("\\{\\{p\\s*" + propname + "\\s*,?\\s*" +
              fixval + "\\s*\\}\\}", "gimu")
          const text = (needsLink && linkTarget !== "prop") ? "[[" + plain + "]]" : plain
          logseq.api.update_block(blockId, block.content.replace(target, text))
          if (noVal || !propname || !plain) return
      
          const proptext = (needsLink && linkTarget !== "text") ? "[[" + plain + "]]" : plain
          if (old && old.split(", ").includes(proptext)) return
      
          logseq.api.upsert_block_property(blockId, propname, (old) ?
              old + ", " + proptext : proptext)
      })
      
  • Customization of values in macro’s definition
    • Default behavior:
      • typing value produces:
        • block text: value
        • property value: value
      • typing [[value]] produces:
        • block text: [[value]]
        • property value: [[value]]
    • With attribute data-link-target='prop':
      • typing [[value]] produces:
        • block text: value
        • property value: [[value]]
    • With attribute data-link-target='text':
      • typing [[value]] produces:
        • block text: [[value]]
        • property value: value
    • With both attributes data-link='force' and data-link-target='prop':
      • typing either value or [[value]] produces:
        • block text: value
        • property value: [[value]]
    • With both attributes data-link='force' and data-link-target='text':
      • typing either value or [[value]] produces:
        • block text: [[value]]
        • property value: value
    • With attribute data-link='force' alone (meaning both):
      • typing either value or [[value]] produces:
        • block text: [[value]]
        • property value: [[value]]

@mentaloid, hello!

I’ve just created similar showcase with 🏛 Full House Templates plugin:

2023-12-01 01.13.08

Setup instructions are here.

3 Likes

This is awesome, after so much discussion about this need that Logseq implements this at the core level and support this natively, I am so enthusiastic about work that is being don at least in the direction of having this functionality via custom.js because it can target very specific needs and implement them with minimal code. I am tight on time these days but will definitely try it and give feedback.

It only now occurred to me that a nice-to-have, in addition to the functionality to auto-fill some properties, is to be able to maintain some sort of sync between the two. I would imagine it as a function that, when you enter Edit Mode, reads all properties and looks them up in the Title+Body of the Block and overlays some grayed out hints of the initial {{p <found match>}} and, if you modify the text inside, it updates the property as well.

This would be especially useful for those writing evergreen notes that often get updated. Maybe difficult to implement without a plugin… dunno, it’s just a “I-wish-that-was-available” idea.

  • No kit or plugin can cover such specialized requirements.
    • You need to code it yourself, whether in Javascript or in Synthesis.
  • Furthermore, I doubt that stuffing a single block with all that info is a good idea. Should rather use:
    • one block (or more) for inserting/editing/updating and holding the info (properties etc.)
    • another block (could be the parent) only for synthesizing and rendering that info

The block will behave just like you described atop of this post and, after Editing Mode is done, will retain the changes in both text and properties so not much stuffing in a single block. I was envisioning the “observer code” to be a part of the Edit Mode that can assist with it (checking the properties’ values inside the text and monitoring those for a change, then updating the other one depending on which one is modified by the user)…

But I reckon it’s a quite distant vision :slight_smile:

I have tested the code above (long overdue) but I get a weird one: I don’t get to type much after I do {{p abc... and it already parses and executes the code. Many times i get “undefined” as the result word in the block body due to this. Is there a way to “delay” executing the code so I can get to write the whole property and value?

You have reported this in other threads too, but nobody else has. Kits in particular don’t execute any code until a macro has finished rendering, thus never during typing. Therefore, something else causes it. As a guess, it may be caused by some customization that gets triggered every time a block’s content changes.

Ok, i will check custom.js for potential culprits. Thanks.