Creating dynamic templates with text highlighting and search

Inspired by: Make custom macros that replace themselves with their value on first run - #19 by mentaloid I made a macro, which creates some kind of template for short meetings, talks, phone calls etc.
So this works great.
I have this macro:

:short-meet "<div class='kit' data-kit='expandmacro'>Talk with $1 <%today%> <%time%>\n background-color:: yellow \n participant:: $1\n tag:: $1 \n collapsed:: true</div>"

Which than evaluates to this

Now coming to my question:
I want the same for other types of meetings, but they have child blocks with highlighted text and fixed bullets like Agenda and Tasks and a search for the same topic. The Template looks like this at the moment:

What I want is, that I write something like this

{{long-meeting(logseq generic-project to-be-filled)}}

and than it should look like the example above. So all of the properties get filled and the project name in the search is automatically updated. I tried a lot of things with my very very basic knowledge in HTML, Markdown and javascript. But it never worked out.
I’ve read the following posts
macros and commands let’s share
change text color for individual blocks or even words
button that would appear under the block bullet when hovering the bullet can it be done with js
ui ux improvement please make it easier to add a new bullet in between other bullets
edit and run javascript code inside logseq itself

But they didn’t help me to find a working solution. Maybe anyone here can point me in the right direction.

Thanks!

Chris

Welcome. Have you tried using a Logseq template?

Thanks für your answer. I‘m not sure what you mean with the template. The third screenshot actually is a template.
But what I don‘t get, is how to call it like I pointed out.

Typing /template and then selecting the template works. But then I have to manually jump to the properties fill them out and need to adjust the project name in the query.
So I want a quick way to fill the properties when I instantiate the template.
Maybe there is a simple solution and I just don’t get it how it works.

Have you looked at plugins like Full House Templates ?

Yes. Issue I have with plugins is that they don’t work on iOS. I use at least 50% of my day the iPhone or iPad for taking notes, making protocols etc.
I guess, once you have created a template with the plugin, that it probably would work. However, the solution you provided here Edit and run javascript code inside Logseq itself works always.

So I feel more comfortable and flexible with this.
And I spend the whole weekend in learning how the bits and pieces of javascript, html and markdown work together. So For me it is a learning experience, not just a solution to my particular problem.

For mobile support, what remains is either:

  • to create your own kit in javascript
    • Kit expandmacro is limited to a single block.
    • Should create page LongMeeting and put inside code that uses Logseq’s API to insert the multiple blocks.
  • to generate the template through Synthesis
    • The respective block-creating tutorial is not yet prepared.

Thanks. I will give it a try.
So the GoTo site for the API Documentation is this one?
Logseq Plugin API docs

Yes. Neither detailed nor complete, but useful-enough for your scenario. You could find a relevant function, then search for usage examples (e.g. in the forum).

Thanks for your help and kits. I think the code below gets the job done. This is the first time I’ve written Javascript code. So it is neither good nor complete, but it works for my use case.

For all the others stumbling across this thread:

Use case is for people who can’t use plugins because they rely heavy on working with mobile devices.

Prerequisites:

  • kits up and running
  • A page in your graph with the name InsertMeeting and the javascript code below.
  • A macro which you name like you want and calls the insertmeeting function
:my-meeting "<div class='kit' data-kit='insertmeeting'>$1</div>" 

Besides the subject of the meeting is every other parameter optional given we have a macro named my-meeting then the intended call is

{{my-meeting Topic <%today%>; Organizer; Project; Tag}}

However, you can call it like this:

{{my-meeting Topic <%today%>}}

Or omitting the date like this:

{{my-meeting Topic}}

This gives you an empty header with a highlighted topic.

If you want to omit eg. the organizer you have to write:

{{my-meeting Topic <%today%>;; Project; Tag}}

Important: If you want to have multiple Tags, Organizers or Projects you have to separate them with a . (dot followed by a whitespace).
The following code will replace them with a comma. Background is that a comma is interpreted by the macro engine.
Example:

{{my-meeting Topic <%today%>; Organizer1. Organizer2; Project1. Project2; Tag1. Tag2. Tag3}}

The resulting meeting looks like this:

// This is helper function to create a meeting by macro without a Template. 
// It is based on the "kits" implementation 
// https://discuss.logseq.com/t/edit-and-run-javascript-code-inside-logseq-itself/20763 
// by mentaloid https://discuss.logseq.com/u/mentaloid
//
// Use case is for people who  can't use plugins because they rely heavy on working with mobile devices.
// Besides the subject of the meeting is every other parameter optional
// given we have a macro named my-meeting then the intended call is
// {{my-meeting Topic <%today%>; Organizer; Project; Tag}}
//
// However, you can call it like this:
//
// {{my-meeting Topic <%today%>}} 
// 
// Or omitting the date like this: 
// 
// {{my-meeting Topic}}
//
// This gives you an empty header with a highlighted topic.
//
// If you want to omit eg. the organizer you have to write:
//
// {{my-meeting Topic <%today%>;; Project; Tag}}
//
// Important: If you want to have multiple Tags, Organizers or Projects you have to separate
// them with a `. ` (dot followed by a whitespace).
// The following code will replace them with a comma. Background is that a comma is interpreted
// by the macro engine. 
// Example:
//
// {{my-meeting Topic <%today%>; Organizer1. Organizer2; Project1. Project2; Tag1. Tag2. Tag3}}

logseq.kits.setStatic(function insertmeeting(div){
    console.info("function insertmeeting called");
  	const debug = false;
    const blockId = div.closest(".ls-block").getAttribute("blockid");
    const content = logseq.api.get_block(blockId).content;
    const macroStart = content.indexOf(`{{` + div.closest(".macro").dataset.macroName);
    const macroEnd = content.indexOf(`}}`, macroStart) + 2;
    const rawData = content.slice(0, macroStart) + div.innerHTML + content.slice(macroEnd);

    const header = {
      subject: `Meeting subject`,
      subject_color: `background-color:: yellow`,
      organizer: `organizer:: `,
      project: `project:: `,
      tag: `tag:: `,
      collapsed: `collapsed:: false`
    };
    const meetingItems = {
      participants: `Participants`,
      agenda: `Agenda`,
      discussion: `Discussion`,
      tasks: `Tasks`,
      projectNotes: `related notes of same project`,
      projectQuery: "",
      bodyheader_color: `background-color:: red`
    };
 // 
  if (debug){
    console.log("insertmeeting: split data");
    console.log(`Macro Start: ` + macroStart);
    console.log(`Macro End: ` + macroEnd);
    console.log(`content: ` + content);
    console.log(`innerHTML: ` + div.innerHTML);
    console.log(`innerText: ` + div.innerText);
    console.log(`textContent: ` + div.textContent);
    console.log(`RawData: ` + rawData);
  }
  //Build up the meeting header block
  const words = rawData.split(/;/);
  if (debug){
  	words.forEach(myArrayConsole);
  }
// as comma `,` is used in macros we need a different delimiter
// in our case we use point with a whitespace `. `
// these delimiters have to be replaced for the header with `, ` 
  const search = '. ';
  const replace = ', ';
  
  let headerBlock = "";
  let project = "";
  
 // In this for loop we build the header block
 // besides the subject of the meeting every other parameter is optional
 // given we have a macro named my-meeting then the intended call is
 // {{my-meeting Topic <%today%>; Organizer; Project; Tag1. Tag2. Tag3}}
 // however, you can call it like this:
 // {{my-meeting Topic <%today%>}} or omitting the date like this {{my-meeting Topic}}
  
  for (let index = 0; index <= 3; index++) {
   	   switch(index){
         case 0:
           if (words[index]){           
               header.subject = words[index];
       		}
           if (debug){
            	console.log("Subject= " + header.subject);
            }
           break;
         case 1:
			if (words[index]){                  
    			header.organizer += words[index].replaceAll(search, replace);
  			}
           	if (debug){
            	console.log("Organizer= " + header.organizer);
            }
           	break;
         case 2:
           if (words[index]){           
  				project += words[index].replaceAll(search, replace);
			} else {
                project = `t.b.d`;
			}
           header.project += project;
           if (debug){
            console.log("Project= " + header.project);
           }
           break;
         case 3:
           if (words[index]){
             	header.tag += words[index].replaceAll(search, replace);
           }
           if (debug){
           	console.log("Tag= " + header.tag);
           }
           break;
         default:
           console.error("header array index mismatch");
       }     
   } 
// here we build our meeting header  
        headerBlock += header.subject + `\n`;
        headerBlock += header.subject_color + `\n`;
        headerBlock += header.organizer + `\n`;
        headerBlock += header.project + `\n`;
        headerBlock += header.tag + `\n`;
        headerBlock += header.collapsed + `\n`;
  
 
   	console.info("Update Header Block");
// here the header block is updated and the original macro is replaced
  	logseq.api.update_block(blockId, headerBlock);
  
    meetingItems.projectQuery += `{{query (and (property :project` + `"` + project + `"` + `) (not (page Templates)))}}`;
    myBlockInsert(meetingItems.participants + `\n` + meetingItems.bodyheader_color ); 
    myBlockInsert(meetingItems.agenda + `\n` + meetingItems.bodyheader_color ); 
  	myBlockInsert(meetingItems.discussion + `\n` + meetingItems.bodyheader_color ); 
    myBlockInsert(meetingItems.tasks + `\n` + meetingItems.bodyheader_color ); 
    myBlockInsert(meetingItems.projectNotes + `\n`); 
    myBlockInsert(meetingItems.projectQuery + `\n`); 
 	
   function myBlockInsert(item) {
  	  if (debug){
     	console.log(item);
      }
      logseq.api.insert_block(blockId, item, {focus: true, before: false, sibling: false});
      return;
	}
  function myArrayConsole(item) {
  	   console.log("myArrayConsole");
       console.log(item);
       return;
  }
 
});
1 Like