Automatic table of contents

It might seem redundant, but it makes sense when thinking of having a sort of dashboard from which to connect to these indices for an overview without keeping the explorer open and scrolling manually.

It doesn’t really make sense to me, but to each their own.

If you make this through dataview how do you then plan to use this to “connect to these indices” as you put it? You can’t link into a query result, as far as I know. You could potentially start your file tree at that position, and leave out folders above the queried folder, but I don’t see how you’ll link into it.

And you don’t need to scroll manually, when you can trigger the Files: Reveal current file in navigation either through the Command palette, a hotkey or other means to call a command.

I’ve just landed in Obsidian and it might be that I’m trying to do something that seems useful to me but actually doesn’t make sense. Setting aside all this talk of the dashboard, is it possible instead, using Dataviewjs queries, to list all the contents of a folder including non-markdown files and possibly excluding certain folders?

By default dataview queries will list only notes, and doesn’t care that much about folders. It can be persuaded to display the folder of a given note, or filter related to the folder information.

Going the other way around and getting it to display folders, is slightly trickier since you then need to step away from the ordinary queries and start dealing with the various file lists you can get through the Obsidian API.

So how you want to tackle cases like this, depends on your true use case. I.e. if you’d like to see all notes in “Folder 01”, excluding “SubF 01”, you could do something like:

```dataview
LIST
FROM "Folder 01" AND -"Folder 01/SubF 01"
```

But most likely this will not show the File JPG.jpg in SubF 03. To get that you need to switch to dataviewjs and traverse the result of app.vault.getFiles(), and do the inclusion and/or exclusion through javascript. So repeat the example above, it could look something like:

```dataviewjs

const files = 
  dv.array(app.vault.getFiles())
  .where(f => f.path.startsWith("Folder 01/"))
  .where(f => !f.path.startsWith("Folder 01/SubF 01/"))

dv.list(files.map(f => `[[${ f.path }|${ f.path }]]`))
```

Where you could exchange the last f.path with just f.basename if you just wanted to see the name and not the full path to the file. To make it appear in a tree like structure would require more manipulation.

1 Like

Thank you, this is a good starting point! Is it possible to integrate a command to return the list in alphabetical order?

Adding .sort(f => f.path) or .sort(f => f.basename) on the line after the .where(...) should sort either by full path or just the base name.

By holroy:

1 Like

It’s doable, as said, but it do require loads of manipulation and adaptation. :smiley:

I am also testing this dataviewjs code but it is still not perfect according to what I would like it to do.

// get files via Obsidian api - filter pdf and other extension and path with the string "xxxxx"
const files = app.vault.getFiles().filter(file => ((file.extension == 'png' || file.extension == 'jpg' || file.extension == 'xlsx' || file.extension == 'pdf') && file.path.includes("xxxxx")));

// creating a list with links
dv.list(files.map(f => dv.fileLink(f.path)))
dv.sort

I would like to understand if there is a way to use this function without having to always specify the whole path to the folder to be included, let me explain better, make it so that the code is valid in every folder where I place it and then it creates the list of the current folder where it is placed, some kind of function to understand the current path without having to change it every time depending on the folder, is it possbile?

Also I would like to make it so that it always excludes the “_resources” folder ( it is the one in the Local Image plugin) without having to specify the full path each time but fetches the “_resources” folder at the location where the dataviewjs code was placed, kind of like the function described above.

Try the following and see if that is closer to what you want:

```dataviewjs
const currentFolder = dv.current().file.folder

// get files via Obsidian api - filter pdf and other extension
//  and path starting with the current folder
const files = app.vault.getFiles()
  .filter(file => file.path.startsWith(currentFolder) &&
   ( file.extension == 'png'  || 
     file.extension == 'jpg'  || 
     file.extension == 'xlsx' ||
     file.extension == 'pdf') )

const pathRegex = new RegExp(`^${ currentFolder }/`)

// creating a list with links
dv.list(files
  .map(f => dv.fileLink(f.path, false, f.path.replace(pathRegex, "")))
  .sort((a, b) => a.path.localeCompare(b.path))
)
```

I’ve tossed in a proper sorting of the result, and added the sub-folder for the files to easier locate the files afterwards.

1 Like

Thank you very much, I integrated your code with the one I had already written to achieve the desired result, I leave it here in case someone needed it to make some sort of MOCs/Index of the various folders.


// Function to exclude a folder without specifying the entire path
const folderToExclude = "_resources";

// Function to extract the current path
const currentFolder = dv.current().file.folder

// Array to indicate the extensions to include
const allowedExtensions = ['md', 'png', 'jpg', 'jpeg', 'gif', 'pdf', 'docx', 'xlsx']; // Aggiungi altre estensioni se necessario

// Function to extract the file extension
function getFileExtension(filename) {
    return filename.slice(((filename.lastIndexOf(".") - 1) >>> 0) + 2);
}


//The part concerning specifying the extensions of the files that you want to extract and display in the list can be disabled with '//' if you want to directly extract and display all the files without specifying the type


// Retrieves files via Obsidian API, filters previously indicated extensions, includes the current folder to filter and excludes previously indicated folder
const files = app.vault.getFiles().filter(file => (
    allowedExtensions.includes(getFileExtension(file.path)) && 
    file.path.includes(currentFolder) && 
    !file.path.includes(folderToExclude)
))

const pathRegex = new RegExp(`^${ currentFolder }/`)

// Function to create the list with complete paths
dv.list(files
  .map(f => dv.fileLink(f.path, false, f.path.replace(pathRegex, "")))
  .sort((a, b) => a.path.localeCompare(b.path))
)

HeHe… Sometimes the turns are taken a little too fast… I forgot that you also wanted to exclude some folder(s). :slight_smile:

Here is by the way an alternate way of doing the allowed extension bit, which I feel is a little clearer and to the point without using an extra function:

// Retrieves files via Obsidian API, filters previously indicated extensions, includes the current folder to filter and excludes previously indicated folder
const files = app.vault.getFiles().filter(file => 
    allowedExtensions.some(ext => file.path.endsWith(ext)) && 
    file.path.includes(currentFolder) && 
    !file.path.includes(folderToExclude)
)

It allows you to read the full logic of the filter directly without looking at another function, and avoids repeated slicing and finding indexes and so on.

Also be aware that when doing file.path.includes(currentFolder), instead of file.path.startsWith() you run the risk of including any sub folders having a part equal to the current folder. I.e. you run the script from /tmp/myScript and it tumbles upon a folder named /someone/elses/tmp/folder

1 Like

Thank you!!! Here is the improved code

// Function to exclude a folder without specifying the entire path
const folderToExclude = "_resources";

// Function to extract the current path
const currentFolder = dv.current().file.folder

// Array to indicate the extensions to include
const allowedExtensions = ['md', 'png', 'jpg', 'jpeg', 'gif', 'pdf', 'docx', 'xlsx']; // Add more extensions if necessary



//The part concerning specifying the extensions of the files that you want to extract and display in the list can be disabled with '//' if you want to directly extract and display all the files without specifying the type


// Retrieves files via Obsidian API, filters previously indicated extensions, includes the current folder to filter and excludes previously indicated folder
const files = app.vault.getFiles().filter(file => 
    allowedExtensions.some(ext => file.path.endsWith(ext)) && 
    file.path.startsWith(currentFolder) && 
    !file.path.includes(folderToExclude)
)

const pathRegex = new RegExp(`^${ currentFolder }/`)

// Function to create the list with complete paths
dv.list(files
  .map(f => dv.fileLink(f.path, false, f.path.replace(pathRegex, "")))
  .sort((a, b) => a.path.localeCompare(b.path))
)

I have noticed that many people, rightly so, prefer to have a dedicated folder with the various scripts inside and call them faster without rewriting all the code inside the notes each time, to do this what do they use? The CustomJS plugin?

I’ve tried posting a post on this subject:

Sadly, it’s not gained a lot of attention. :cry:

1 Like

List of interesting possibilities in your thread!

I’m trying to use the Dataview specific callback function before testing with other plugins but it doesn’t work it always gives me some error.

The syntax should be correct (I created a folder for the scripts and inside there is the MOC.js file with the code written previously)

dataviewjs
dv.view("Scripts/MOC");

The Templater user script folder doesn’t particularly like foreign scripts:

There was a forum thread (on QuickAdd’s GitHub issues page as well) (I cannot find them), where there was talk about Templater throwing an error on a js file used by Quickadd.

  • Happened to me, again: no Templater snippet worked until I removed the user script for the common folder.

This is very true. I’ve also experienced that some type of errors either by a foreign script or errors in a user function within this particular folder is capable of disrupting all template insertions.

Dataview specific callback function doesn’t work, it always gives me some error.
I tried Templater and, as you also say, it works only with the script inserted in text form in the templates folder and not in the scripts folder.
CustomJS also I could not get it to work.

So far the only way I have found to be able to call scripts from a specific folder without pasting all the code each time into each individual .md file and that also works on the phone is Templater.

P.S. for those who don’t want to use Dataview, I recently discovered the Waypoint plugin to create MOC/indexes automatically, it looks interesting as an option.

1 Like

In most cases when using dv.view() I think you should add await in front of it. What other error messages do you get from doing it?

Templater should also work nicely on mobile, but there is a difference between “text” templates from your templates folder, and user functions (in javascript) from the user scripts folder. And those two folders can’t be the same. The latter is called using tp.user.YOUR_NAME() from inside another templater script, or javascript where you’ve got access to tp

CustomJS also do work, when you get the syntax correct, so play around a little more with, and the examples provided, and you’ll surely get it to work too.

In Dataview when I try to call the script with:

 await dv.view("DataviewScripts/moc")

It gives me this error:

Evaluation Error: SyntaxError: Unexpected token '{'
    at new Function (<anonymous>)
    at DataviewInlineApi.view (plugin:dataview:18584:20)
    at async eval (eval at <anonymous> (plugin:dataview), 
<anonymous>:1:55)
    at async DataviewJSRenderer.render (plugin:dataview:18670:13)

Sorry, can you explain this part better which isn’t clear to me?

Currently with Templater I simply created a .md file with the code inside in the respective templates folder and I recall it where needed by simply inserting the model.

After a bit of testing I abandoned CustomJS also because I read that it can create conflicts with other plugins such as Templater.