- Inspiration
- Syntax for inline properties::
- Create Properties from inline text as I type and make them available as variables inside the block
- This is a basic implementation, so none of the above feature requests is properly satisfied.
- Many edge cases are not covered, use it only for the covered ones.
- 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}}
- Though generally the comma can be omitted:
- Omitting the value reuses the existing one:
{{p property}}
- Should not use comma (
- 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.
- This uses a macro with the following syntax:
- Preparation
- Add a macro inside file
config.edn
, insidemacros{}
::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) })
- Add a macro inside file
- Customization of values in macroās definition
- Default behavior:
- typing
value
produces:- block text:
value
- property value:
value
- block text:
- typing
[[value]]
produces:- block text:
[[value]]
- property value:
[[value]]
- block text:
- typing
- With attribute
data-link-target='prop'
:- typing
[[value]]
produces:- block text:
value
- property value:
[[value]]
- block text:
- typing
- With attribute
data-link-target='text'
:- typing
[[value]]
produces:- block text:
[[value]]
- property value:
value
- block text:
- typing
- With both attributes
data-link='force'
anddata-link-target='prop'
:- typing either
value
or[[value]]
produces:- block text:
value
- property value:
[[value]]
- block text:
- typing either
- With both attributes
data-link='force'
anddata-link-target='text'
:- typing either
value
or[[value]]
produces:- block text:
[[value]]
- property value:
value
- block text:
- typing either
- With attribute
data-link='force'
alone (meaning both):- typing either
value
or[[value]]
produces:- block text:
[[value]]
- property value:
[[value]]
- block text:
- typing either
- Default behavior:
@mentaloid, hello!
Iāve just created similar showcase with š Full House Templates
plugin:
Setup instructions are here.
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
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.
I have an issue with a particular situation:
this happened on {{p datetime, /today @ /current time}}
If I write such a block it will look like the following when I exit the Edit Mode and no property is created:
this happened on {{p datetime, [[Sunday, 05.05.2024]] @ 18:24 }}
I can achieve property creation if I enclose the āvalueā in quotes, but the property would be quoted:
this happened on "[[Sunday, 05.05.2024]] @ 18:24"
datetime:: "[[Sunday, 05.05.2024]] @ 18:24"
Is there a way to have the datetime property created without using the quotes?
I reckon that I do not understand how to use data-link
and data-link-target
or if they can help with this situationā¦
- Macros donāt support some special characters.
- That includes brackets and commas.
- Attributes
data-link
anddata-link-target
are for adding brackets automatically.- But they do it to the whole value, not to part of it.
- In theory, your property
datetime::
should break into two properties:date::
time::
- In theory, your property
- But they do it to the whole value, not to part of it.
- I donāt see any easy workaround.
- Should probably create a dedicated
{{datetime}}
macro.- That way would also avoid typing
/today /current time
- Synthesis allows for
{{evalonce date and time}}
(or a shorter form)- Adding the property is not yet covered in the tutorials.
- That way would also avoid typing
- Should probably create a dedicated
In this particular situation it is not something I want to make into query-able properties, more of a āgood-to-knowā situation. In general you are right.
I believe it would be useful to allow any characters after the first comma and consider it the value of the property without needing " "
. Dunno if itās possible programmatically thoughā¦
I have noticed that some properties get their value replaced while using {{p property, value}}
while others get the value in the inline call added to the existing value, with a comma after the last value.
While I found it weird I was actually looking for replacing a status::
property with the ānewā value from the āinlineā call so I made some experiments to see whatās what (I noticed before that for my due-by::
property, calling it from the inline macro would replace the date in the value with the new date while the status::
property got the new status added to the list).
I learned that property names that have a dash (ā-ā) in their name get the value replaced while the properties that are named without any dash get their property added to the list.
While I very much like the idea that for some properties the value gets added while for other it gets replaced, it might not be an intended featureā¦
Even if itās not intended Iād like to keep the feature as itās helpful, albeit modifying the names of the properties to benefit from it is a quirky way of doing it.
One other feature that would be mostly helpful for me is to be able to modify a property from the inline editing of the block but its value to be hidden from (not written to) the body of the block. This makes sense when you want to write everything down without going with arrow keys or with the mouse cursor at the right property and remove and replace the value (it saves time to just write it while youāre at it but it would also make no sense to have that value appear in the text). Could this feature be added to your āinline propertiesā module?
Thanks.
- The strange behavior you describe may have to do with passing a value with comma in it.
- But I cannot tell, unless you provide an actual block to test.
- The intended behavior of this macro is to auto-generate the properties the first time of writing the blockās content, not to update them afterwards.
- As stated both in the title and here:
- Therefore, I donāt see any of your advanced features getting implemented by this macro.
- For advanced functionality, should look at Synthesis.
- The respective tutorial is not yet prepared.
Ok, here you go: a usecase for inline properties
š³#To
status::
priority::
due-by::
worked-on:: {{today}}
context::
* ā
My workflow for introducing data in the journal is via Custom Commands, leaving the cursor at the most important section. In the above case, the cursor would be placed right after ā#Toā so I can add right away the (Action) Tag and Description of the Task: #ToCall John and invite him and wife for dinner ...
By using āinline propertiesā I would modify the Custom Command to be:
š³{{p status, #To }}
status::
priority::
due-by::
worked-on:: {{today}}
context::
* ā
and place the cursor after #To
like before so I can update right away the status
property, then hit Fn+Right (on my keyboard) to skip the closing curly brackets and go on with writing the task content:
#ToCall }} John by {{p due-by, /date }} and invite him and wife for dinner to discuss the {{p context, [[trip]]}} this summer
The block would become:
š³ #ToCall John by [[Friday, 10.05.2024]] and invite him and wife for dinner to discuss the [[trip]] this summer
status:: #ToCall
priority::
due-by:: [[Friday, 10.05.2024]]
worked-on:: [[Thursday, 09.05.2024]]
context:: [[trip]]
* ā
Now after I would have called John I would come back and replace āā (by double-clicking on it so it gets selected and then calling another Custom Command - <.
) with . The āCheckedā Custom Command would look like:
{{p status, #Done}} āļø
This is where it gets crooked. Now, the status
property would be status:: #ToCall, #Done
and I only want it to be #Done
. So, having noticed that the due-by
value gets replaced every time if it has been there before, I changed the status
property name to current-status
-so it gets a dash- and presto, now the final status will look like: status:: #Done
.
But because the {{p }}
macro didnāt just update the property but also wrote the value to the Block Title, The Block will look now like this:
#Done āļø #ToCall John by [[Friday, 10.05.2024]] and invite him and wife for dinner to discuss the [[trip]] this summer
status:: #Done
priority::
due-by:: [[Friday, 10.05.2024]]
worked-on:: [[Thursday, 09.05.2024]]
context:: [[trip]]
* ā
Or, I donāt want to see the #Done
hashtag in the Blockās Title line. If the code would allow a parameter to prevent writing anything to the block then I could also add, at the end of the Title some other properties:
[...] to discuss the {{p context, [[trip]]}} this summer {{p priority, Urgent, 0}}
, where the third variable ($3), ā0ā, means no text in the blockās body.
I donāt know how can this be achieved best, maybe a different macro altogether. And I know very little from my usage has been this kitās purpose, but it works for me and I am ok with it (if only there would be a way to also have the option to write nothing to the blockās text while updating/creating the property);
- It turns out that internally:
due-by
gets converted todueBy
current-status
gets converted tocurrentStatus
- etc.
- This conversion doesnāt seem justified, but results in the noticed behavior.
- It is better to use āa different macro altogetherā for each scenario:
- Could either use a different kit or pass the extra info as data-attributes (like
data-link
). - Instead of typing:
{{p priority, Urgent, 0}}
- to type e.g.:
{{p0 priority, Urgent}}
- to type e.g.:
- Instead of replacing the macro with a value:
block.content.replace(target, text)
- to replace it with an empty string:
block.content.replace(target, "")
- to replace it with an empty string:
- Instead of appending to the old value:
(old) ? old + ", " + proptext : proptext
- to ignore the old value with plain:
proptext
- to ignore the old value with plain:
- Could either use a different kit or pass the extra info as data-attributes (like