Self-Upgrading In Document Table of Contents Plugin

That’s theme related. Normally doing the entire list with 1. works fine in Markdown, but you can provide the actual numbers like this:

const startAtLevel = input?.level || 2
const content = await dv.io.load(dv.current().file.path)
const counter = [0, startAtLevel]
let numbers = [1]
const toc = content.match(new RegExp(`^#{${startAtLevel},} \\S.*`, 'mg'))
    .map(heading => {
        const [_, level, text] = heading.match(/^(#+) (.+)$/)
        const link = dv.current().file.path + '#' + text
        if (level.length > counter[1]) {
            counter[0]++
            numbers[counter[0]] = 1
        } else if (level.length < counter[1]) {
            counter[0] = Math.max(0, counter[0] - 1)
            numbers[counter[0]]++
            numbers = numbers.slice(0, counter[0] + 1)
        }
        counter[1] = level.length
        return '\t'.repeat(counter[0]) + `${input?.style || numbers[numbers.length - 1] + '.'} [[${link}|${text}]]`
    })
if (input?.heading !== false) {
    dv.header(2, 'Table of contents')
}
dv.paragraph(toc.join('\n'))
3 Likes

Well, we could add a copy button to the outline core plugin

Why reinventing wheel ??

This is a one-liner that displays an automatically-updating table of contents inside the note, rather than in the sidebar and/or manually copy/pasting.

It’s a different wheel.

1 Like

Maybe i didn’t explain very well.
My suggestion is very simple and doable, we only need a copy button in the core Outline plugin tab

This way we could paste a nicely formatted toc into our notes. I myself wrote a script copying a table of content to the clipboard. Then i copy this wiki-links into my note. It’s a bit manually but hey, i think adding a toc is something you do at the end of the day, basically for your readers and less, for yourself
I’d like to have this one integrated in Obsidian :wink:

To add to this: a TOC feature like Markdown All in One in vscode would be perfect.

The problem with one of the previous suggestions mentioning DataView or this obsidian-automatic-table-of-contents plugin is they’re only viewable in Obsidian. If we were to commit this to our team wiki, then readers would see this:

```table-of-contents
```

instead of a table of contents.

Hi Alan,

I love your solution and am using it widely.
I’m not very savvy when it comes to js though and I’d like to remove the numbering. Is that possible within the script, or would I have to do it with css?

There’s not a specific class to the TOC, and I don’t want to hide the numbering everywhere…

Thanks in advance!

Thank you for the dataview script!
Here is a little improvement to handle links that are within headers.

// initially from AlanG (https://forum.obsidian.md/t/new-maintainer-needed-for-dynamic-toc/42381/4)
// 2024/05/02, Sourigna: Added support for links within headers

// Set this to 1 if you want to include level 1 headers,
// or set it to 2 if you want to ignore level 1 headers
const startAtLevel = 1;
const content = await dv.io.load(dv.current().file.path);
const toc = content.match(new RegExp(`^#{${startAtLevel},} \\S.*`, 'mg'))
  .map(heading => {
    var [_, level, text] = heading.match(/^(#+) (.+)$/);
    
    // Transform links that are inside `text` as their displayed text only ([[my link|displayed text]] => displayed text)
    var displayed_text = text;
    displayed_text = displayed_text.replace(/\[\[([^\|\[\]]+)\|([^\|\[\]]+)\]\]/g, `$2`);
    displayed_text = displayed_text.replace(/\[\[([^\|\[\]]+)\]\]/g, "$1");

    // Obsidian strips links characters ("[", "|" and "]") that are within headers
    // e.g.     ## my header [[lvl2|level 2]]
    // becomes  [my_file#my header lvl2 level 2]
    // For the sake of readability, let's keep only the displayed texts:
    //          [my_file#my header lvl2 level 2|my header level 2]
    text = text.replace(/([\[\]])/g, "");
    text = text.replace(/([\|])/g, " ");
    const link = dv.current().file.path + '#' + text;
    const result = '\t'.repeat(level.length - startAtLevel) + `1. [[${link}|${displayed_text}]]`;
    
    return result;
  });
dv.header(2, 'Table of contents');
dv.paragraph(toc.join('\n'));

Just used the second option mentioned in the docs here:

Like this:

```dataviewjs
dv.view('toc', { style: '-' })
```