Tinderbox Forum

Tip: Export note + children to markdown, with clickable Table of Contents

This post is intended as a quick guide to using action code within an template to export a single note and its children to a markdown file, with a Table of Contents generated automatically from the names of the children, with each item in the ToC being a clickable link to the relevant subsection.

It assumes that you know the basics of exporting a markdown note and children to a markdown file with a suitable hierarchy of subheadings, and concentrates on the mechanism for producing the ToC. Rather than just present the final code, we’ll build up the solution from first principles, so that it’s easier to understand what’s going on and adapt the code for specific needs — for example, at the moment, it only deals with one layer of hierarchy in the ToC.


There are three parts to this:

  1. Collecting the names of the descendants into a list
  2. Formatting the items into clickable links ([Section name](#section-name))
  3. Feeding the results into a bulleted list to be printed out

Steps 1 and 3 are relatively straightforward, so we’ll deal with those first.

For Step 1, we use the collect() function on all the main note’s children to collect them into a list. We don’t care if there are duplicate names, and we want the ToC in the same order as the child notes, so all we need is:

$MyList = collect(children,$Name)
==> ChildName1;ChildName2 etc

Step 3 To turn this simple list of $Names into a bullet point list for step 3, we make use of the List/Set.format(“format string”) command, with four parameters.

These mark the beginning and end of the list block and individual list items. With four empty strings this will turn out an HTML bullet point list, but we can also use it for markdown lists with the four parameters strings as

  1. "" or optionally a title for the list
  2. "- " or "1. " to mark the start of the items
  3. "\n" to end the current item
  4. "" or "\n" to end the entire list

Here we want:

$MyList.format("## TOC\n", "- ", "\n", "")

We now have a standard Markdown ‘dumb’ bullet list of the names of the child notes.

## TOC
- ChildName1
- ChildName2

But we still need to do Step 2 — create the clickable links. We need to format each list item as [Section Name](#section-name) and that’s where a problem arises. The syntax for the item is simple enough, building the string in the normal way in the collect() statement:

$MyList=collect(children,"[" +$Name+"](#"+$Name.lowercase.replace(" “,”-")+"")

The problem arises that we want the item to contain ( ) and when Tinderbox encounters parentheses in a list it automatically encloses the whole item in " ", which is not what we want.

So, we have to add another replace to remove the "" before the whole thing is printed.

$MyList=collect(children,"[" +$Name+"](#"+$Name.lowercase.replace(" ","-")+")");

==> "[Section Name]{#section-name}"


==> [Section Name](#section-name)

Note that we have to use single quotes in the search pattern and escape the double quotes.

All that remains now is to put the above expressions into action code for use in the parent export template, combining the three stages together in a string of dot operators:

^action($MyList=collect(children,"[" +$Name+"](#"+$Name.lowercase.replace(" ","-")+")").format("## ToC\n","- ","\n","").replace('\"',""))^


Final code

These two statements can of course be simplified further, removing the need for the temporary variable $MyList, into the final code:

	collect(children,"[" +$Name+"](#"+$Name.lowercase.replace(" ","-")+")"
	.format("## ToC\n","- ","\n","")

Example output:

## ToC
- [Daily Notes](#daily-notes)
- [Tinderbox: Identifying User attributes](#tinderbox:-identifying-user-attributes)
- [Tinderbox: explore core stamps for all files](#tinderbox:-explore-core-stamps-for-all-files)

Which renders to the following clickable ToC when rendered (depending on the css, of course):



For more details of the ins and outs of the issues about quotation marks and lists, and the quirks of replace, please see the later stages of this thread, starting with this post:

Because that discussion got quite detailed, and includes various other possible solutions, we thought it would be helpful to summarise it in a separate thread to make it easier to find for others.

NB:Any actual thinking and knowledge behind this solution was provided by Mark Anderson — thanks as ever to him!