diff --git a/src/commands.ts b/src/commands.ts
index 307a087..d2cd74d 100644
--- a/src/commands.ts
+++ b/src/commands.ts
@@ -1,4 +1,4 @@
-import { App, TFolder, Menu, TAbstractFile, Notice, TFile, Editor, MarkdownView, Platform } from 'obsidian';
+import { App, TFolder, Menu, TAbstractFile, Notice, TFile, Editor, MarkdownView, Platform, stringifyYaml } from 'obsidian';
import FolderNotesPlugin from './main';
import { getFolderNote, createFolderNote, deleteFolderNote, turnIntoFolderNote, openFolderNote, extractFolderName } from './functions/folderNoteFunctions';
import { ExcludedFolder } from './excludedFolder';
@@ -10,65 +10,25 @@ export class Commands {
this.app = app;
}
registerCommands() {
- this.plugin.registerEvent(this.plugin.app.workspace.on('editor-menu', (menu: Menu, editor: Editor, view: MarkdownView) => {
- const text = editor.getSelection().trim();
- menu.addItem((item) => {
- item.setTitle('Create folder overview')
- .setIcon('edit')
- .onClick(() => {
- editor.replaceSelection('```folder-overview\n```\n');
- });
- });
- if (!text || text.trim() === '') return;
- menu.addItem((item) => {
- item.setTitle('Create folder note')
- .setIcon('edit')
- .onClick(() => {
- const file = view.file;
- if (!(file instanceof TFile)) return;
- const blacklist = ['*', '\\', '"', '/', '<', '>', '?', '|', ':'];
- for (const char of blacklist) {
- if (text.includes(char)) {
- new Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?');
- return;
- }
- }
- if (text.endsWith('.')) {
- new Notice('File name cannot end with a dot');
- return;
- }
- let folder: TAbstractFile | null;
- const folderPath = this.plugin.getFolderPathFromString(file.path);
- if (folderPath === '') {
- folder = this.plugin.app.vault.getAbstractFileByPath(text);
- if (folder instanceof TFolder) {
- return new Notice('Folder note already exists');
- } else {
- this.plugin.app.vault.createFolder(text);
- createFolderNote(this.plugin, text, false);
- }
- } else {
- folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text);
- if (folder instanceof TFolder) {
- return new Notice('Folder note already exists');
- }
- if (this.plugin.settings.storageLocation === 'parentFolder') {
- if (this.app.vault.getAbstractFileByPath(folderPath + '/' + text + this.plugin.settings.folderNoteType)) {
- return new Notice('File already exists');
- }
- }
- this.plugin.app.vault.createFolder(folderPath + '/' + text);
- createFolderNote(this.plugin, folderPath + '/' + text, false);
- }
- const fileName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', text);
- if (fileName === text) {
- editor.replaceSelection(`[[${fileName}]]`);
- } else {
- editor.replaceSelection(`[[${fileName}|${text}]]`);
- }
- });
- });
- }));
+ this.editorCommands();
+ this.fileCommands();
+ this.regularCommands();
+ }
+ regularCommands() {
+ this.plugin.addCommand({
+ id: 'turn-into-folder-note',
+ name: 'Make current active note a folder note',
+ callback: () => {
+ const file = this.app.workspace.getActiveFile();
+ if (!(file instanceof TFile)) return;
+ const folder = file.parent;
+ if (!(folder instanceof TFolder)) return;
+ const folderNote = getFolderNote(this.plugin, folder.path);
+ turnIntoFolderNote(this.plugin, file, folder, folderNote);
+ }
+ });
+ }
+ fileCommands() {
this.plugin.registerEvent(this.app.workspace.on('file-menu', (menu: Menu, file: TAbstractFile) => {
let folder: TAbstractFile | TFolder | null = file.parent;
if (file instanceof TFile) {
@@ -190,4 +150,71 @@ export class Commands {
});
}));
}
+ editorCommands() {
+ this.plugin.registerEvent(this.plugin.app.workspace.on('editor-menu', (menu: Menu, editor: Editor, view: MarkdownView) => {
+ const text = editor.getSelection().trim();
+ const fileContent = editor.getValue().trim();
+ const line = editor.getCursor().line;
+ const lineText = editor.getLine(line);
+ if (!fileContent.includes('```folder-overview') && lineText.trim() == '') {
+ menu.addItem((item) => {
+ item.setTitle('Create folder overview')
+ .setIcon('edit')
+ .onClick(() => {
+ const yaml = stringifyYaml(this.plugin.settings.defaultOverview)
+ editor.replaceSelection(`\`\`\`folder-overview\n${yaml}\`\`\`\n`);
+ });
+ });
+ }
+ if (!text || text.trim() === '') return;
+ menu.addItem((item) => {
+ item.setTitle('Create folder note')
+ .setIcon('edit')
+ .onClick(() => {
+ const file = view.file;
+ if (!(file instanceof TFile)) return;
+ const blacklist = ['*', '\\', '"', '/', '<', '>', '?', '|', ':'];
+ for (const char of blacklist) {
+ if (text.includes(char)) {
+ new Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?');
+ return;
+ }
+ }
+ if (text.endsWith('.')) {
+ new Notice('File name cannot end with a dot');
+ return;
+ }
+ let folder: TAbstractFile | null;
+ const folderPath = this.plugin.getFolderPathFromString(file.path);
+ if (folderPath === '') {
+ folder = this.plugin.app.vault.getAbstractFileByPath(text);
+ if (folder instanceof TFolder) {
+ return new Notice('Folder note already exists');
+ } else {
+ this.plugin.app.vault.createFolder(text);
+ createFolderNote(this.plugin, text, false);
+ }
+ } else {
+ folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text);
+ if (folder instanceof TFolder) {
+ return new Notice('Folder note already exists');
+ }
+ if (this.plugin.settings.storageLocation === 'parentFolder') {
+ if (this.app.vault.getAbstractFileByPath(folderPath + '/' + text + this.plugin.settings.folderNoteType)) {
+ return new Notice('File already exists');
+ }
+ }
+ this.plugin.app.vault.createFolder(folderPath + '/' + text);
+ createFolderNote(this.plugin, folderPath + '/' + text, false);
+ }
+ const fileName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', text);
+ if (fileName === text) {
+ editor.replaceSelection(`[[${fileName}]]`);
+ } else {
+ editor.replaceSelection(`[[${fileName}|${text}]]`);
+ }
+ });
+ });
+ }));
+ }
}
diff --git a/src/folderOverview.ts b/src/folderOverview.ts
deleted file mode 100644
index b43ecb6..0000000
--- a/src/folderOverview.ts
+++ /dev/null
@@ -1,380 +0,0 @@
-import { MarkdownPostProcessorContext, parseYaml, TAbstractFile, TFolder, TFile } from 'obsidian';
-import { extractFolderName, getFolderNote } from './functions/folderNoteFunctions';
-import FolderNotesPlugin from './main';
-import { FolderOverviewSettings } from './modals/folderOverview';
-import { getExcludedFolder } from './excludedFolder';
-export type yamlSettings = {
- title?: string;
- disableTitle?: boolean;
- depth?: number;
- type?: 'folder' | 'markdown' | 'canvas' | 'other' | 'pdf' | 'images' | 'audio' | 'video' | 'all';
- includeTypes?: string[];
- style?: 'list' | 'grid';
- disableFileTag?: boolean;
- sortBy?: 'name' | 'created' | 'modified' | 'nameAsc' | 'createdAsc' | 'modifiedAsc';
- showEmptyFolders?: boolean;
- onlyIncludeSubfolders?: boolean;
-};
-
-export function createCanvasOverview() {
- // console.log('test');
-}
-
-export function createOverview(plugin: FolderNotesPlugin, source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) {
- // parse source to yaml
- let yaml: yamlSettings = parseYaml(source);
- if (!yaml) {
- yaml = {
- title: plugin.settings.defaultOverview.title,
- disableTitle: plugin.settings.defaultOverview.disableTitle,
- depth: plugin.settings.defaultOverview.depth,
- type: plugin.settings.defaultOverview.type,
- includeTypes: plugin.settings.defaultOverview.includeTypes,
- style: plugin.settings.defaultOverview.style,
- disableFileTag: plugin.settings.defaultOverview.disableFileTag,
- sortBy: plugin.settings.defaultOverview.sortBy,
- showEmptyFolders: plugin.settings.defaultOverview.showEmptyFolders,
- onlyIncludeSubfolders: plugin.settings.defaultOverview.onlyIncludeSubfolders,
- };
- }
- const depth = yaml?.depth || 1;
- let title = yaml?.title || plugin.settings.defaultOverview.title || '{{folderName}} overview';
- const folderName = extractFolderName(plugin.settings.folderNoteName, plugin.removeExtension(plugin.getFolderNameFromPathString(ctx.sourcePath)));
- title = title.replaceAll('{{folderName}}', folderName || '');
- const disableTitle = yaml?.disableTitle || plugin.settings.defaultOverview.disableTitle || false;
- let includeTypes: string[] = yaml?.includeTypes || plugin.settings.defaultOverview.includeTypes || ['folder', 'markdown'];
- includeTypes = includeTypes.map((type) => type.toLowerCase());
- const style: 'list' | 'grid' = yaml?.style || 'list';
- const disableFileTag = yaml?.disableFileTag || plugin.settings.defaultOverview.disableFileTag || false;
- const showEmptyFolders = yaml?.showEmptyFolders || plugin.settings.defaultOverview.showEmptyFolders || false;
- const pathBlacklist: string[] = [];
-
-
- el.parentElement?.classList.add('folder-overview-container');
- const root = el.createEl('div', { cls: 'folder-overview' });
- const titleEl = root.createEl('h1', { cls: 'folder-overview-title' });
- const ul = root.createEl('ul', { cls: 'folder-overview-list' });
- if (!disableTitle) {
- titleEl.innerText = title;
- }
- if (includeTypes.length === 0) { return; }
- let files: TAbstractFile[] = [];
- const sourceFile = plugin.app.vault.getAbstractFileByPath(ctx.sourcePath);
- if (!sourceFile) return;
- const sourceFolderPath = plugin.getFolderPathFromString(ctx.sourcePath);
- let sourceFolder: TFolder | undefined;
- if (sourceFile.parent instanceof TFolder) {
- sourceFolder = sourceFile.parent;
- } else {
- sourceFolder = plugin.app.vault.getAbstractFileByPath(plugin.getFolderPathFromString(ctx.sourcePath)) as TFolder;
- }
-
- files = sourceFolder.children;
-
- files = files.filter((file) => {
- const folderPath = plugin.getFolderPathFromString(file.path);
- if (!folderPath.startsWith(sourceFolderPath)) { return false; }
- const excludedFolder = getExcludedFolder(plugin, file.path);
- if (excludedFolder?.excludeFromFolderOverview) { return false; }
- if (file.path === ctx.sourcePath) { return false; }
- if ((file.path.split('/').length - sourceFolderPath.split('/').length) - 1 < depth) {
- return true;
- }
- });
- if (!includeTypes.includes('folder')) {
- files = getAllFiles(files, sourceFolderPath, depth);
- }
- if (files.length === 0) {
- // create button to edit the overview
- const editButton = root.createEl('button', { cls: 'folder-overview-edit-button' });
- editButton.innerText = 'Edit overview';
- editButton.addEventListener('click', (e) => {
- e.stopImmediatePropagation();
- e.preventDefault();
- e.stopPropagation();
- new FolderOverviewSettings(plugin.app, plugin, parseYaml(source), ctx, el).open();
- }, { capture: true });
- }
- files = sortFiles(files, yaml, plugin);
-
- if (style === 'grid') {
- const grid = root.createEl('div', { cls: 'folder-overview-grid' });
- files.forEach(async (file) => {
- const gridItem = grid.createEl('div', { cls: 'folder-overview-grid-item' });
- const gridArticle = gridItem.createEl('article', { cls: 'folder-overview-grid-item-article' });
- if (file instanceof TFile) {
- const fileContent = await plugin.app.vault.read(file);
- // skip --- yaml
- const descriptionEl = gridArticle.createEl('p', { cls: 'folder-overview-grid-item-description' });
- let description = fileContent.split('\n')[0];
- if (description.length > 64) {
- description = description.slice(0, 64) + '...';
- }
- descriptionEl.innerText = description;
- const link = gridArticle.createEl('a', { cls: 'folder-overview-grid-item-link internal-link' });
- const title = link.createEl('h1', { cls: 'folder-overview-grid-item-link-title' });
- title.innerText = file.name.replace('.md', '').replace('.canvas', '');
- link.href = file.path;
- } else if (file instanceof TFolder) {
- const folderItem = gridArticle.createEl('div', { cls: 'folder-overview-grid-item-folder' });
- const folderName = folderItem.createEl('h1', { cls: 'folder-overview-grid-item-folder-name' });
- folderName.innerText = file.name;
- }
- });
- } else if (style === 'list') {
- files.forEach((file) => {
- if (file instanceof TFolder) {
- const folderItem = addFolderList(plugin, ul, pathBlacklist, file);
- goThroughFolders(plugin, folderItem, file, depth, sourceFolderPath, ctx, yaml, pathBlacklist, includeTypes, disableFileTag);
- } else if (file instanceof TFile) {
- addFileList(plugin, ul, pathBlacklist, file, includeTypes, disableFileTag);
- }
- });
- } else if (style === 'explorer') {
- } else if (style === 'testing') {
- cloneFileExplorerView(plugin, ctx, root, yaml, pathBlacklist);
- }
- if (includeTypes.length > 1 && style !== 'grid' && (!showEmptyFolders || yaml.onlyIncludeSubfolders)) {
- removeEmptyFolders(ul, 1, yaml);
- }
-}
-function cloneFileExplorerView(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, root: HTMLElement, yaml: yamlSettings, pathBlacklist: string[]) {
- const folder = plugin.getEL(plugin.getFolderPathFromString(ctx.sourcePath))
- const folderElement = folder?.parentElement;
- console.log(folderElement)
- if (!folderElement) return;
- const newFolderElement = folderElement.cloneNode(true) as HTMLElement;
- newFolderElement.querySelectorAll('div.tree-item-icon').forEach((el) => {
- if (el instanceof HTMLElement) {
- el.onclick = () => {
- handleCollapseClick(el, plugin, yaml, pathBlacklist);
- }
- }
- });
- root.appendChild(newFolderElement);
-}
-function handleCollapseClick(el: HTMLElement, plugin: FolderNotesPlugin, yaml: yamlSettings, pathBlacklist: string[]) {
- el.classList.toggle('is-collapsed');
- if (el.classList.contains('is-collapsed')) {
- console.log(el.parentElement?.parentElement);
- console.log(el.parentElement?.parentElement?.childNodes[1])
- console.log(el.parentElement?.parentElement?.querySelector('tree-item-children'))
- el.parentElement?.parentElement?.childNodes[1]?.remove();
- } else {
- console.log(el.parentElement)
- const path = el.parentElement?.getAttribute('data-path');
- if (!path) return;
- const folder = plugin.app.vault.getAbstractFileByPath(path);
- if (!(folder instanceof TFolder)) return;
- const folderElement = el.parentElement?.parentElement;
- if (!folderElement) return;
- console.log('folderElement', folderElement);
- const childrenElement = folderElement.createDiv({ cls: 'tree-item-children nav-folder-children' });
- let files = sortFiles(folder.children, {}, plugin);
- files = filterFiles(files, plugin, path, yaml.depth || 1, pathBlacklist);
- files.forEach((child) => {
- if (child instanceof TFolder) {
- const svg = '';
- const folderElement = childrenElement.createDiv({
- cls: 'tree-item nav-folder',
- });
- const folderTitle = folderElement.createDiv({
- cls: 'tree-item-self is-clickable nav-folder-title',
- attr: {
- 'data-path': child.path,
- 'draggable': 'true'
- },
- })
- const collapseIcon = folderTitle.createDiv({
- cls: 'tree-item-icon collapse-icon nav-folder-collapse-indicator',
- });
- collapseIcon.innerHTML = svg;
- collapseIcon.onclick = () => {
- handleCollapseClick(collapseIcon, plugin, yaml, pathBlacklist);
- }
- folderTitle.createDiv({
- cls: 'tree-item-inner nav-folder-title-content',
- text: child.name,
- });
- } else if (child instanceof TFile) {
- const fileElement = childrenElement.createDiv({
- cls: 'tree-item nav-file',
- });
- const fileTitle = fileElement.createDiv({
- cls: 'tree-item-self is-clickable nav-file-title',
- attr: {
- 'data-path': child.path,
- 'draggable': 'true'
- },
- })
- fileTitle.createDiv({
- cls: 'tree-item-inner nav-file-title-content',
- text: child.basename,
- });
- if (child.extension !== 'md') {
- fileTitle.createDiv({
- cls: 'nav-file-tag',
- text: child.extension
- });
- }
- }
- });
- // move folders above files
- }
-}
-
-
-function goThroughFolders(plugin: FolderNotesPlugin, list: HTMLLIElement | HTMLUListElement, folder: TFolder,
- depth: number, sourceFolderPath: string, ctx: MarkdownPostProcessorContext, yaml: yamlSettings, pathBlacklist: string[], includeTypes: string[], disableFileTag: boolean) {
- if (sourceFolderPath === '') {
- depth--;
- }
- let files = filterFiles(folder.children, plugin, sourceFolderPath, depth, pathBlacklist);
- files = sortFiles(files, yaml, plugin);
- const ul = list.createEl('ul', { cls: 'folder-overview-list' });
- files.forEach((file) => {
- if (file instanceof TFolder) {
- const folderItem = addFolderList(plugin, ul, pathBlacklist, file);
- goThroughFolders(plugin, folderItem, file, depth, sourceFolderPath, ctx, yaml, pathBlacklist, includeTypes, disableFileTag);
- } else if (file instanceof TFile) {
- addFileList(plugin, ul, pathBlacklist, file, includeTypes, disableFileTag);
- }
- });
-}
-
-function filterFiles(files: TAbstractFile[], plugin: FolderNotesPlugin, sourceFolderPath: string, depth: number, pathBlacklist: string[]) {
- return files.filter((file) => {
- console.log('pathBlacklist', pathBlacklist);
- if (pathBlacklist.includes(file.path)) { return false; }
- const folderPath = plugin.getFolderPathFromString(file.path);
- if (!folderPath.startsWith(sourceFolderPath)) { return false; }
- const excludedFolder = getExcludedFolder(plugin, file.path);
- if (excludedFolder?.excludeFromFolderOverview) { return false; }
- if ((file.path.split('/').length - sourceFolderPath.split('/').length) - 1 < depth) {
- return true;
- }
- });
-}
-
-function sortFiles(files: TAbstractFile[], yaml: yamlSettings, plugin: FolderNotesPlugin) {
- if (!yaml?.sortBy) {
- yaml.sortBy = plugin.settings.defaultOverview.sortBy || 'name';
- }
- return files.sort((a, b) => {
- // sort by folder first
- if (a instanceof TFolder && !(b instanceof TFolder)) {
- return -1;
- }
- if (!(a instanceof TFolder) && b instanceof TFolder) {
- return 1;
- }
- if (!(a instanceof TFile) || !(b instanceof TFile)) { return -1; }
- if (yaml.sortBy === 'created') {
- if (a.stat.ctime > b.stat.ctime) {
- return -1;
- } else if (a.stat.ctime < b.stat.ctime) {
- return 1;
- }
- } else if (yaml.sortBy === 'createdAsc') {
- if (a.stat.ctime < b.stat.ctime) {
- return -1;
- } else if (a.stat.ctime > b.stat.ctime) {
- return 1;
- }
- } else if (yaml.sortBy === 'modified') {
- if (a.stat.mtime > b.stat.mtime) {
- return -1;
- } else if (a.stat.mtime < b.stat.mtime) {
- return 1;
- }
- } else if (yaml.sortBy === 'modifiedAsc') {
- if (a.stat.mtime < b.stat.mtime) {
- return -1;
- } else if (a.stat.mtime > b.stat.mtime) {
- return 1;
- }
- }
- // sort by name
- if (a.name < b.name && yaml.sortBy === 'name') {
- return -1;
- } else if (a.name > b.name && yaml.sortBy === 'nameAsc') {
- return -1;
- }
-
- if (a.name > b.name && yaml.sortBy === 'name') {
- return 1;
- } else if (a.name < b.name && yaml.sortBy === 'nameAsc') {
- return 1;
- }
- return 0;
- });
-}
-
-function removeEmptyFolders(ul: HTMLUListElement | HTMLLIElement, depth: number, yaml: yamlSettings) {
- const childrensToRemove: ChildNode[] = [];
- ul.childNodes.forEach((el) => {
- const childrens = (el as Element).querySelector('ul');
- if (!childrens || childrens === null) { return; }
- if (childrens && !childrens?.hasChildNodes() && !(el instanceof HTMLUListElement)) {
- childrensToRemove.push(el);
- } else if (el instanceof HTMLUListElement || el instanceof HTMLLIElement) {
- removeEmptyFolders(el, depth + 1, yaml);
- }
- });
- childrensToRemove.forEach((el) => {
- if (yaml.onlyIncludeSubfolders && depth === 1) { return; }
- el.remove();
- });
-}
-
-function addFolderList(plugin: FolderNotesPlugin, list: HTMLUListElement | HTMLLIElement, pathBlacklist: string[], folder: TFolder) {
- const folderItem = list.createEl('li', { cls: 'folder-overview-list folder-list' });
- const folderNote = getFolderNote(plugin, folder.path);
- if (folderNote instanceof TFile) {
- const folderNoteLink = folderItem.createEl('a', { cls: 'folder-overview-list-item folder-name-item internal-link', href: folderNote.path });
- folderNoteLink.innerText = folder.name;
- pathBlacklist.push(folderNote.path);
- } else {
- const folderName = folderItem.createEl('span', { cls: 'folder-overview-list-item folder-name-item' });
- folderName.innerText = folder.name;
- }
- return folderItem;
-}
-
-function addFileList(plugin: FolderNotesPlugin, list: HTMLUListElement | HTMLLIElement, pathBlacklist: string[], file: TFile, includeTypes: string[], disableFileTag: boolean) {
- if (includeTypes.length > 0 && !includeTypes.includes('all')) {
- if (file.extension === 'md' && !includeTypes.includes('markdown')) return;
- if (file.extension === 'canvas' && !includeTypes.includes('canvas')) return;
- if (file.extension === 'pdf' && !includeTypes.includes('pdf')) return;
- const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'];
- if (imageTypes.includes(file.extension) && !includeTypes.includes('image')) return;
- const videoTypes = ['mp4', 'webm', 'ogv', 'mov', 'mkv'];
- if (videoTypes.includes(file.extension) && !includeTypes.includes('video')) return;
- const audioTypes = ['mp3', 'wav', 'm4a', '3gp', 'flac', 'ogg', 'oga', 'opus'];
- if (audioTypes.includes(file.extension) && includeTypes.includes('audio')) return;
- const allTypes = ['md', 'canvas', 'pdf', ...imageTypes, ...videoTypes, ...audioTypes];
- if (!allTypes.includes(file.extension) && !includeTypes.includes('other')) return;
- }
- if (pathBlacklist.includes(file.path)) return;
- const listItem = list.createEl('li', { cls: 'folder-overview-list file-link' });
- const nameItem = listItem.createEl('div', { cls: 'folder-overview-list-item' });
- const link = nameItem.createEl('a', { cls: 'internal-link', href: file.path });
- link.innerText = file.basename;
- if (file.extension !== 'md' && !disableFileTag) {
- nameItem.createDiv({ cls: 'nav-file-tag' }).innerText = file.extension;
- }
-}
-
-function getAllFiles(files: TAbstractFile[], sourceFolderPath: string, depth: number) {
- const allFiles: TAbstractFile[] = [];
- files.forEach((file) => {
- if (file instanceof TFolder) {
- if ((file.path.split('/').length - sourceFolderPath.split('/').length) - 1 < depth - 1) {
- allFiles.push(...getAllFiles(file.children, sourceFolderPath, depth));
- }
- } else {
- allFiles.push(file);
- }
- });
- return allFiles;
-}
\ No newline at end of file
diff --git a/src/folderOverviewClass.ts b/src/folderOverview/FolderOverview.ts
similarity index 73%
rename from src/folderOverviewClass.ts
rename to src/folderOverview/FolderOverview.ts
index d7211d3..c5d8854 100644
--- a/src/folderOverviewClass.ts
+++ b/src/folderOverview/FolderOverview.ts
@@ -1,19 +1,22 @@
-import { MarkdownPostProcessorContext, parseYaml, TAbstractFile, TFolder, TFile } from 'obsidian';
-import { extractFolderName, getFolderNote } from './functions/folderNoteFunctions';
-import FolderNotesPlugin from './main';
-import { FolderOverviewSettings } from './modals/folderOverview';
-import { getExcludedFolder } from './excludedFolder';
+import { MarkdownPostProcessorContext, parseYaml, TAbstractFile, TFolder, TFile, stringifyYaml, Notice } from 'obsidian';
+import { extractFolderName, getFolderNote } from '../functions/folderNoteFunctions';
+import FolderNotesPlugin from '../main';
+import { FolderOverviewSettings } from './modalSettings';
+import { getExcludedFolder } from '../excludedFolder';
+export type includeTypes = 'folder' | 'markdown' | 'canvas' | 'other' | 'pdf' | 'images' | 'audio' | 'video' | 'all';
export type yamlSettings = {
+ folderPath: string;
title: string;
disableTitle: boolean;
depth: number;
- type: 'folder' | 'markdown' | 'canvas' | 'other' | 'pdf' | 'images' | 'audio' | 'video' | 'all';
- includeTypes: string[];
- style: 'list' | 'grid';
+ includeTypes: includeTypes[];
+ style: 'list' | 'grid' | 'explorer';
disableFileTag: boolean;
- sortBy: 'name' | 'created' | 'modified' | 'nameAsc' | 'createdAsc' | 'modifiedAsc';
+ sortBy: 'name' | 'created' | 'modified';
+ sortByAsc: boolean;
showEmptyFolders: boolean;
onlyIncludeSubfolders: boolean;
+ storeFolderCondition: boolean;
};
export class FolderOverview {
yaml: yamlSettings;
@@ -26,68 +29,77 @@ export class FolderOverview {
folders: TFolder[] = [];
constructor(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, source: string, el: HTMLElement) {
let yaml: yamlSettings = parseYaml(source);
+ if (!yaml) { yaml = {} as yamlSettings; }
const includeTypes = yaml?.includeTypes || plugin.settings.defaultOverview.includeTypes || ['folder', 'markdown'];
this.plugin = plugin;
this.ctx = ctx;
this.source = source;
this.el = el;
this.yaml = {
- title: yaml?.title || plugin.settings.defaultOverview.title || '{{folderName}} overview',
- disableTitle: yaml?.disableTitle || plugin.settings.defaultOverview.disableTitle || false,
- depth: yaml?.depth || plugin.settings.defaultOverview.depth || 1,
+ folderPath: yaml?.folderPath || plugin.getFolderPathFromString(ctx.sourcePath),
+ title: yaml?.title || plugin.settings.defaultOverview.title,
+ disableTitle: yaml?.disableTitle || plugin.settings.defaultOverview.disableTitle,
+ depth: yaml?.depth || plugin.settings.defaultOverview.depth,
style: yaml?.style || 'list',
- includeTypes: includeTypes.map((type) => type.toLowerCase()),
- disableFileTag: yaml?.disableFileTag || plugin.settings.defaultOverview.disableFileTag || false,
- sortBy: yaml?.sortBy || plugin.settings.defaultOverview.sortBy || 'name',
- showEmptyFolders: yaml?.showEmptyFolders || plugin.settings.defaultOverview.showEmptyFolders || false,
- onlyIncludeSubfolders: yaml?.onlyIncludeSubfolders || plugin.settings.defaultOverview.onlyIncludeSubfolders || false,
- type: yaml?.type || plugin.settings.defaultOverview.type || 'folder',
+ includeTypes: includeTypes.map((type) => type.toLowerCase()) as includeTypes[],
+ disableFileTag: yaml?.disableFileTag || plugin.settings.defaultOverview.disableFileTag,
+ sortBy: yaml?.sortBy || plugin.settings.defaultOverview.sortBy,
+ sortByAsc: yaml?.sortByAsc || plugin.settings.defaultOverview.sortByAsc,
+ showEmptyFolders: yaml?.showEmptyFolders || plugin.settings.defaultOverview.showEmptyFolders,
+ onlyIncludeSubfolders: yaml?.onlyIncludeSubfolders || plugin.settings.defaultOverview.onlyIncludeSubfolders,
+ storeFolderCondition: yaml?.storeFolderCondition || plugin.settings.defaultOverview.storeFolderCondition
}
}
create(plugin: FolderNotesPlugin, source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) {
+ el.empty();
el.parentElement?.classList.add('folder-overview-container');
const root = el.createEl('div', { cls: 'folder-overview' });
const titleEl = root.createEl('h1', { cls: 'folder-overview-title' });
const ul = root.createEl('ul', { cls: 'folder-overview-list' });
- if (!this.yaml.disableTitle) {
- titleEl.innerText = this.yaml.title || '';
- }
if (this.yaml.includeTypes.length === 0) { return; }
let files: TAbstractFile[] = [];
const sourceFile = plugin.app.vault.getAbstractFileByPath(ctx.sourcePath);
if (!sourceFile) return;
- const sourceFolderPath = plugin.getFolderPathFromString(ctx.sourcePath);
+ const sourceFolderPath = this.yaml.folderPath || plugin.getFolderPathFromString(ctx.sourcePath);
let sourceFolder: TFolder | undefined;
- if (sourceFile.parent instanceof TFolder) {
- sourceFolder = sourceFile.parent;
- } else {
- sourceFolder = plugin.app.vault.getAbstractFileByPath(plugin.getFolderPathFromString(ctx.sourcePath)) as TFolder;
+ if (sourceFolderPath !== '') {
+ sourceFolder = plugin.app.vault.getAbstractFileByPath(this.yaml.folderPath) as TFolder;
}
-
- files = sourceFolder.children;
-
- files = files.filter((file) => {
- const folderPath = plugin.getFolderPathFromString(file.path);
- if (!folderPath.startsWith(sourceFolderPath)) { return false; }
- const excludedFolder = getExcludedFolder(plugin, file.path);
- if (excludedFolder?.excludeFromFolderOverview) { return false; }
- if (file.path === ctx.sourcePath) { return false; }
- if ((file.path.split('/').length - sourceFolderPath.split('/').length) - 1 < this.yaml.depth) {
- return true;
+ if (!this.yaml.disableTitle) {
+ if (sourceFolder && sourceFolderPath !== '') {
+ titleEl.innerText = this.yaml.title.replace('{{folderName}}', sourceFolder.name);
+ } else if (sourceFolderPath == '' ) {
+ titleEl.innerText = this.yaml.title.replace('{{folderName}}', 'Vault');
+ } else {
+ titleEl.innerText = this.yaml.title.replace('{{folderName}}', '');
}
- });
+ }
+ if (!sourceFolder && sourceFolderPath !== '') { return new Notice('Couldn\'t find the folder'); }
+ if (sourceFolderPath == '') {
+ const rootFolders: TAbstractFile[] = [];
+ plugin.app.vault.getAllLoadedFiles().filter(f => f instanceof TFolder).forEach((file) => {
+ if (file instanceof TFolder && !file.path.includes('/')) {
+ rootFolders.push(file);
+ }
+ });
+ files = rootFolders;
+ } else if (sourceFolder) {
+ files = sourceFolder.children;
+ }
+ console.log(files);
+ files = this.filterFiles(files, plugin, sourceFolderPath, this.yaml.depth, this.pathBlacklist);
if (!this.yaml.includeTypes.includes('folder')) {
files = this.getAllFiles(files, sourceFolderPath, this.yaml.depth);
}
if (files.length === 0) {
- // create button to edit the overview
const editButton = root.createEl('button', { cls: 'folder-overview-edit-button' });
editButton.innerText = 'Edit overview';
editButton.addEventListener('click', (e) => {
e.stopImmediatePropagation();
e.preventDefault();
e.stopPropagation();
- new FolderOverviewSettings(plugin.app, plugin, parseYaml(source), ctx, el).open();
+ console.log(this)
+ new FolderOverviewSettings(plugin.app, plugin, this.yaml, ctx, el).open();
}, { capture: true });
}
files = this.sortFiles(files, this.yaml, plugin);
@@ -126,28 +138,42 @@ export class FolderOverview {
}
});
} else if (this.yaml.style === 'explorer') {
- } else if (this.yaml.style === 'testing') {
- console.log('testing');
- this.cloneFileExplorerView(plugin, ctx, root, this.yaml, this.pathBlacklist);
+ if (this.plugin.app.workspace.layoutReady) {
+ this.cloneFileExplorerView(plugin, ctx, root, this.yaml, this.pathBlacklist);
+ } else {
+ this.plugin.app.workspace.onLayoutReady(() => {
+ this.cloneFileExplorerView(plugin, ctx, root, this.yaml, this.pathBlacklist);
+ });
+ }
}
- if (this.yaml.includeTypes.length > 1 && this.yaml.style !== 'grid' && (!this.yaml.showEmptyFolders || this.yaml.onlyIncludeSubfolders)) {
+ if (this.yaml.style !== 'list') { return; }
+ if (this.yaml.includeTypes.length > 1 && (!this.yaml.showEmptyFolders || this.yaml.onlyIncludeSubfolders)) {
this.removeEmptyFolders(ul, 1, this.yaml);
}
}
cloneFileExplorerView(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, root: HTMLElement, yaml: yamlSettings, pathBlacklist: string[]) {
- const folder = plugin.getEL(plugin.getFolderPathFromString(ctx.sourcePath))
- const folderElement = folder?.parentElement;
- const tFolder = plugin.app.vault.getAbstractFileByPath(plugin.getFolderPathFromString(ctx.sourcePath));
- console.log(folderElement)
+ const folder = plugin.getEL(this.yaml.folderPath)
+ let folderElement = folder?.parentElement;
+ const tFolder = plugin.app.vault.getAbstractFileByPath(this.yaml.folderPath);
+ if (!folderElement && yaml.folderPath.trim() !== '') return;
+ folderElement = document.querySelector('div.nav-files-container') as HTMLElement;
if (!folderElement) return;
const newFolderElement = folderElement.cloneNode(true) as HTMLElement;
newFolderElement.querySelectorAll('div.nav-folder-title ').forEach((el) => {
const folder = plugin.app.vault.getAbstractFileByPath(el.getAttribute('data-path') || '');
if (!(folder instanceof TFolder)) return;
- if (el.parentElement?.classList.contains('is-collapsed')) {
- folder.collapsed = true;
+ if (this.yaml.storeFolderCondition) {
+ if (folder.collapsed) {
+ el.classList.add('is-collapsed');
+ } else {
+ el.classList.remove('is-collapsed');
+ }
} else {
- folder.collapsed = false;
+ if (el.parentElement?.classList.contains('is-collapsed')) {
+ folder.collapsed = true;
+ } else {
+ folder.collapsed = false;
+ }
}
if (el.classList.contains('has-folder-note')) {
const folderNote = getFolderNote(plugin, folder.path);
@@ -156,6 +182,14 @@ export class FolderOverview {
});
if (tFolder instanceof TFolder) {
this.addFiles(tFolder.children, root);
+ } else if (yaml.folderPath.trim() === '') {
+ const rootFolders: TAbstractFile[] = [];
+ plugin.app.vault.getAllLoadedFiles().filter(f => f instanceof TFolder).forEach((file) => {
+ if (file instanceof TFolder && !file.path.includes('/')) {
+ rootFolders.push(file);
+ }
+ });
+ this.addFiles(rootFolders, root);
}
newFolderElement.querySelectorAll('div.tree-item-icon').forEach((el) => {
if (el instanceof HTMLElement) {
@@ -170,10 +204,8 @@ export class FolderOverview {
}
async addFiles(files: TAbstractFile[], childrenElement: HTMLElement) {
- const folders = files.filter((file) => file instanceof TFolder);
- const filesWithoutFolders = files.filter((file) => !(file instanceof TFolder));
- console.log(folders);
- console.log(filesWithoutFolders);
+ const folders = files.filter((file) => file instanceof TFolder).sort();
+ const filesWithoutFolders = this.sortFiles(files.filter((file) => !(file instanceof TFolder)), this.yaml, this.plugin);
for (const child of folders) {
if (child instanceof TFolder) {
const folderNote = getFolderNote(this.plugin, child.path);
@@ -194,8 +226,6 @@ export class FolderOverview {
if (!child.collapsed) {
folderTitle.classList.remove('is-collapsed');
const childrenElement = folderElement?.createDiv({ cls: 'tree-item-children nav-folder-children' });
- console.log(folderElement);
- console.log(child.children);
this.addFiles(child.children, childrenElement);
} else {
folderTitle.classList.add('is-collapsed');
@@ -221,7 +251,7 @@ export class FolderOverview {
if (child instanceof TFile) {
if (this.pathBlacklist.includes(child.path)) { continue; }
const extension = child.extension.toLowerCase() == 'md' ? 'markdown' : child.extension.toLowerCase();
- if (!this.yaml.includeTypes.includes(extension)) { continue; }
+ if (!this.yaml.includeTypes.includes(extension as includeTypes)) { continue; }
const fileElement = childrenElement.createDiv({
cls: 'tree-item nav-file',
});
@@ -290,7 +320,7 @@ export class FolderOverview {
return files.filter((file) => {
if (pathBlacklist.includes(file.path)) { return false; }
const folderPath = plugin.getFolderPathFromString(file.path);
- if (!folderPath.startsWith(sourceFolderPath)) { return false; }
+ if (!folderPath.startsWith(sourceFolderPath) && sourceFolderPath !== '') { return false; }
const excludedFolder = getExcludedFolder(plugin, file.path);
if (excludedFolder?.excludeFromFolderOverview) { return false; }
if ((file.path.split('/').length - sourceFolderPath.split('/').length) - 1 < depth) {
@@ -304,7 +334,6 @@ export class FolderOverview {
yaml.sortBy = plugin.settings.defaultOverview.sortBy || 'name';
}
return files.sort((a, b) => {
- // sort by folder first
if (a instanceof TFolder && !(b instanceof TFolder)) {
return -1;
}
@@ -312,13 +341,48 @@ export class FolderOverview {
return 1;
}
if ((a instanceof TFolder) && (b instanceof TFolder)) {
- if (yaml.sortBy === 'name') {
+ if (a.name > b.name) {
+ return -1;
+ } else if (a.name < b.name) {
+ return 1;
+ }
+ }
+ if (!(a instanceof TFile) || !(b instanceof TFile)) { return -1; }
+ if (yaml.sortBy === 'created') {
+ if (yaml.sortByAsc) {
+ if (a.stat.ctime < b.stat.ctime) {
+ return -1;
+ } else if (a.stat.ctime > b.stat.ctime) {
+ return 1;
+ }
+ }
+ if (a.stat.ctime > b.stat.ctime) {
+ return -1;
+ } else if (a.stat.ctime < b.stat.ctime) {
+ return 1;
+ }
+ } else if (yaml.sortBy === 'modified') {
+ if (yaml.sortByAsc) {
+ if (a.stat.mtime < b.stat.mtime) {
+ return -1;
+ } else if (a.stat.mtime > b.stat.mtime) {
+ return 1;
+ }
+ }
+ if (a.stat.mtime > b.stat.mtime) {
+ return -1;
+ } else if (a.stat.mtime < b.stat.mtime) {
+ return 1;
+ }
+ } else if (yaml.sortBy === 'name') {
+ if (yaml.sortByAsc) {
if (a.name < b.name) {
+
return -1;
} else if (a.name > b.name) {
return 1;
}
- } else if (yaml.sortBy === 'nameAsc') {
+ } else {
if (a.name > b.name) {
return -1;
} else if (a.name < b.name) {
@@ -326,44 +390,6 @@ export class FolderOverview {
}
}
}
- if (!(a instanceof TFile) || !(b instanceof TFile)) { return -1; }
- if (yaml.sortBy === 'created') {
- if (a.stat.ctime > b.stat.ctime) {
- return -1;
- } else if (a.stat.ctime < b.stat.ctime) {
- return 1;
- }
- } else if (yaml.sortBy === 'createdAsc') {
- if (a.stat.ctime < b.stat.ctime) {
- return -1;
- } else if (a.stat.ctime > b.stat.ctime) {
- return 1;
- }
- } else if (yaml.sortBy === 'modified') {
- if (a.stat.mtime > b.stat.mtime) {
- return -1;
- } else if (a.stat.mtime < b.stat.mtime) {
- return 1;
- }
- } else if (yaml.sortBy === 'modifiedAsc') {
- if (a.stat.mtime < b.stat.mtime) {
- return -1;
- } else if (a.stat.mtime > b.stat.mtime) {
- return 1;
- }
- }
- // sort by name
- if (a.name < b.name && yaml.sortBy === 'name') {
- return -1;
- } else if (a.name > b.name && yaml.sortBy === 'nameAsc') {
- return -1;
- }
-
- if (a.name > b.name && yaml.sortBy === 'name') {
- return 1;
- } else if (a.name < b.name && yaml.sortBy === 'nameAsc') {
- return 1;
- }
return 0;
});
}
@@ -436,4 +462,43 @@ export class FolderOverview {
});
return allFiles;
}
+}
+
+export async function updateYaml(plugin: FolderNotesPlugin, ctx: MarkdownPostProcessorContext, el: HTMLElement, yaml: yamlSettings) {
+ console.log('update yaml');
+ const file = plugin.app.vault.getAbstractFileByPath(ctx.sourcePath);
+ if (!(file instanceof TFile)) return;
+ let stringYaml = stringifyYaml(yaml);
+ await plugin.app.vault.process(file, (text) => {
+ const info = ctx.getSectionInfo(el);
+ // check if stringYaml ends with a newline
+ if (stringYaml[stringYaml.length - 1] !== '\n') {
+ stringYaml += '\n';
+ }
+ if (info) {
+ const { lineStart } = info;
+ console.log(text);
+ const lineEnd = getCodeBlockEndLine(text, lineStart);
+ console.log(lineStart, lineEnd);
+ if (lineEnd === -1 || !lineEnd) return text;
+ const lineLength = lineEnd - lineStart;
+ const lines = text.split('\n');
+ lines.splice(lineStart, lineLength + 1, `\`\`\`folder-overview\n${stringYaml}\`\`\``);
+ return lines.join('\n');
+ }
+ return `\`\`\`folder-overview\n${stringYaml}\`\`\``;
+ });
+}
+export function getCodeBlockEndLine(text: string, startLine: number, count = 1) {
+ let line = startLine + 1;
+ const lines = text.split('\n');
+ while (line < lines.length) {
+ if (count > 20) { return -1; }
+ if (lines[line].startsWith('```')) {
+ return line;
+ }
+ line++;
+ count++;
+ }
+ return line;
}
\ No newline at end of file
diff --git a/src/folderOverview/modalSettings.ts b/src/folderOverview/modalSettings.ts
new file mode 100644
index 0000000..7531317
--- /dev/null
+++ b/src/folderOverview/modalSettings.ts
@@ -0,0 +1,295 @@
+import { App, Modal, Setting, MarkdownPostProcessorContext, stringifyYaml, TFile, TFolder } from 'obsidian';
+import { yamlSettings, includeTypes, FolderOverview } from 'src/folderOverview/FolderOverview';
+import FolderNotesPlugin from '../main';
+import ListComponent from 'src/functions/ListComponent';
+import { updateYaml } from 'src/folderOverview/FolderOverview';
+import { FolderSuggest } from 'src/suggesters/FolderSuggester';
+export class FolderOverviewSettings extends Modal {
+ plugin: FolderNotesPlugin;
+ app: App;
+ yaml: yamlSettings;
+ ctx: MarkdownPostProcessorContext;
+ el: HTMLElement;
+ defaultSettings: boolean;
+ constructor(app: App, plugin: FolderNotesPlugin, yaml: yamlSettings, ctx: MarkdownPostProcessorContext | null, el: HTMLElement | null, defaultSettings?: boolean) {
+ super(app);
+ this.plugin = plugin;
+ this.app = app;
+ if (!yaml) {
+ this.yaml = this.plugin.settings.defaultOverview;
+ } else {
+ this.yaml = yaml;
+ }
+ if (ctx) {
+ this.ctx = ctx;
+ }
+ if (el) {
+ this.el = el;
+ }
+ if (defaultSettings) {
+ this.yaml = this.plugin.settings.defaultOverview;
+ this.defaultSettings = true;
+ }
+ }
+ onOpen() {
+ this.display();
+ }
+ display() {
+ const { contentEl } = this;
+ contentEl.empty();
+ // close when user presses enter
+ contentEl.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter') {
+ this.close();
+ }
+ });
+ console.log(this.yaml);
+ contentEl.createEl('h2', { text: 'Folder overview settings' });
+ new Setting(contentEl)
+ .setName('Disable the title')
+ .setDesc('Choose if the title should be shown')
+ .addToggle((toggle) =>
+ toggle
+ .setValue(this.yaml.disableTitle || false)
+ .onChange(async (value) => {
+ console.log(this.yaml)
+ this.yaml.disableTitle = value;
+ this.display();
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);;
+ })
+ );
+ if (!this.yaml.disableTitle) {
+ new Setting(contentEl)
+ .setName('Title')
+ .setDesc('Choose the title of the folder overview')
+ .addText((text) =>
+ text
+ .setValue(this.yaml?.title || '{{folderName}} overview')
+ .onChange(async (value) => {
+ this.yaml.title = value;
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);;
+ })
+ );
+ }
+ new Setting(contentEl)
+ .setName('Folder path for the overview')
+ .setDesc('Choose the folder path for the overview')
+ .addSearch((search) => {
+ new FolderSuggest(search.inputEl, this.plugin)
+ search
+ .setPlaceholder('Folder path')
+ .setValue(this.yaml?.folderPath || '')
+ .onChange(async (value) => {
+ if (value === '' || value === '/') {
+ this.yaml.folderPath = '';
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);;
+ return;
+ }
+ if (!(this.app.vault.getAbstractFileByPath(value) instanceof TFolder)) return;
+ this.yaml.folderPath = value;
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);;
+ });
+ });
+ new Setting(contentEl)
+ .setName('Overview style')
+ .setDesc('Choose the style of the overview (grid style soon)')
+ .addDropdown((dropdown) =>
+ dropdown
+ .addOption('list', 'List')
+ .addOption('explorer', 'Explorer')
+ .setValue(this.yaml?.style || 'list')
+ .onChange(async (value: 'list') => {
+ this.yaml.style = value;
+ this.display();
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);
+ })
+ );
+ if (this.yaml.style === 'explorer') {
+ new Setting(contentEl)
+ .setName('Store collapsed condition')
+ .setDesc('Choose if the collapsed condition should be stored stored until you restart Obsidian')
+ .addToggle((toggle) =>
+ toggle
+ .setValue(this.yaml.storeFolderCondition || this.plugin.settings.defaultOverview.storeFolderCondition || true)
+ .onChange(async (value) => {
+ this.yaml.storeFolderCondition = value;
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);;
+ })
+ );
+ }
+ const setting = new Setting(contentEl);
+ setting.setName('Include types');
+ const list = setting.createList((list: ListComponent) =>
+ list
+ .addModal(this)
+ .setValues(this.yaml?.includeTypes || this.plugin.settings.defaultOverview.includeTypes || [])
+ .addResetButton()
+ );
+ if ((this.yaml?.includeTypes?.length || 0) < 8 && !this.yaml.includeTypes?.includes('all')) {
+ setting.addDropdown((dropdown) => {
+ if (!this.yaml.includeTypes) this.yaml.includeTypes = this.plugin.settings.defaultOverview.includeTypes || [];
+ this.yaml.includeTypes = this.yaml.includeTypes.map((type: string) => type.toLowerCase()) as includeTypes[];
+ const options = [
+ { value: 'markdown', label: 'Markdown' },
+ { value: 'folder', label: 'Folder' },
+ { value: 'canvas', label: 'Canvas' },
+ { value: 'pdf', label: 'PDF' },
+ { value: 'image', label: 'Image' },
+ { value: 'audio', label: 'Audio' },
+ { value: 'video', label: 'Video' },
+ { value: 'other', label: 'All other file types' },
+ { value: 'all', label: 'All file types' },
+ ];
+
+ options.forEach((option) => {
+ if (!this.yaml.includeTypes?.includes(option.value as includeTypes)) {
+ dropdown.addOption(option.value, option.label);
+ }
+ });
+ dropdown.addOption('+', '+');
+ dropdown.setValue('+');
+ dropdown.onChange(async (value) => {
+ if (value === 'all') {
+ this.yaml.includeTypes = this.yaml.includeTypes?.filter((type: string) => type === 'folder');
+ // @ts-ignore
+ list.setValues(this.yaml.includeTypes);
+ }
+ // @ts-ignore
+ await list.addValue(value.toLowerCase());
+ this.display();
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);
+ });
+ });
+ }
+ let disableFileTag;
+ this.yaml.includeTypes?.forEach((type: string) => {
+ type === 'folder' || type === 'markdown' ? (disableFileTag = true) : null;
+ });
+ if (disableFileTag) {
+ new Setting(contentEl)
+ .setName('Disable file tag')
+ .setDesc('Choose if the file tag should be shown after the file name')
+ .addToggle((toggle) => {
+ toggle
+ .setValue(this.yaml.disableFileTag || this.plugin.settings.defaultOverview.disableFileTag || false)
+ .onChange(async (value) => {
+ this.yaml.disableFileTag = value;
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);
+ });
+ });
+ }
+ if (this.yaml.style !== 'explorer') {
+ new Setting(contentEl)
+ .setName('File depth')
+ .setDesc('File & folder = +1 depth')
+ .addSlider((slider) =>
+ slider
+ .setValue(this.yaml?.depth || 2)
+ .setLimits(1, 10, 1)
+ .onChange(async (value) => {
+ this.yaml.depth = value;
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);
+ })
+ );
+ }
+
+ new Setting(contentEl)
+ .setName('Sort files by')
+ .setDesc('Choose how the files should be sorted')
+ .addDropdown((dropdown) =>
+ dropdown
+ .addOption('name', 'Name')
+ .addOption('created', 'Created')
+ .addOption('modified', 'Modified')
+ .setValue(this.yaml?.sortBy || 'name')
+ .onChange(async (value: 'name' | 'created' | 'modified') => {
+ this.yaml.sortBy = value;
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);
+ })
+ )
+ .addDropdown((dropdown) => {
+ dropdown
+ .addOption('desc', 'Descending')
+ .addOption('asc', 'Ascending')
+ if (this.yaml.sortByAsc) {
+ dropdown.setValue('asc');
+ } else {
+ dropdown.setValue('desc');
+ }
+ dropdown.onChange(async (value) => {
+ this.yaml.sortByAsc = value === 'asc';
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);
+ });
+ });
+ if (this.yaml.style === 'list') {
+ new Setting(contentEl)
+ .setName('Show folder names of folders that appear empty in the folder overview')
+ .setDesc('Show the names of folders that appear to have no files/folders in the folder overview. That\'s mostly the case when you set the file depth to 1.')
+ .addToggle((toggle) => {
+ toggle
+ .setValue(this.yaml.showEmptyFolders || this.plugin.settings.defaultOverview.showEmptyFolders || false)
+ .onChange(async (value) => {
+ this.yaml.showEmptyFolders = value;
+ this.yaml.onlyIncludeSubfolders = false;
+ this.display();
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);
+ });
+ });
+
+ if (this.yaml.showEmptyFolders) {
+ new Setting(contentEl)
+ .setName('Only show first empty subfolders of current folder')
+ .addToggle((toggle) => {
+ toggle
+ .setValue(this.yaml.onlyIncludeSubfolders || this.plugin.settings.defaultOverview.onlyIncludeSubfolders || false)
+ .onChange(async (value) => {
+ this.yaml.onlyIncludeSubfolders = value;
+ if (this.defaultSettings) {
+ return this.plugin.saveSettings();
+ }
+ await updateYaml(this.plugin, this.ctx, this.el, this.yaml);
+ });
+ });
+ }
+ }
+
+ }
+ onClose() {
+ const { contentEl } = this;
+ contentEl.empty();
+ }
+}
+
diff --git a/src/functions/ListComponent.ts b/src/functions/ListComponent.ts
index 07799a0..602087f 100644
--- a/src/functions/ListComponent.ts
+++ b/src/functions/ListComponent.ts
@@ -1,5 +1,7 @@
import { Setting } from 'obsidian';
-import { FolderOverviewSettings } from '../modals/folderOverview';
+import { FolderOverviewSettings } from '../folderOverview/modalSettings';
+import { includeTypes } from 'src/folderOverview/FolderOverview';
+import { updateYaml } from 'src/folderOverview/FolderOverview';
export default class ListComponent {
containerEl: HTMLElement;
controlEl: HTMLElement;
@@ -20,13 +22,17 @@ export default class ListComponent {
setValues(values: string[]) {
this.listEl.empty();
this.values = values;
- this.modal.yaml.includeTypes = values;
+ this.modal.yaml.includeTypes = values as includeTypes[];
if (values.length !== 0) {
values.forEach((value) => {
this.addElement(value);
});
}
- this.modal.updateYaml();
+ if (this.modal.defaultSettings) {
+ this.modal.plugin.saveSettings();
+ return this;
+ }
+ updateYaml(this.modal.plugin, this.modal.ctx, this.modal.el, this.modal.yaml);
return this;
}
addElement(value: string) {
@@ -46,7 +52,7 @@ export default class ListComponent {
async addValue(value: string) {
this.values.push(value);
this.addElement(value);
- this.modal.yaml.includeTypes = this.values;
+ this.modal.yaml.includeTypes = this.values as includeTypes[];
return this;
}
addResetButton() {
diff --git a/src/functions/folderNoteFunctions.ts b/src/functions/folderNoteFunctions.ts
index 396d4a2..d744568 100644
--- a/src/functions/folderNoteFunctions.ts
+++ b/src/functions/folderNoteFunctions.ts
@@ -126,7 +126,6 @@ export function extractFolderName(template: string, changedFileName: string) {
export function getFolderNote(plugin: FolderNotesPlugin, folderPath: string, storageLocation?: string, file?: TFile) {
if (!folderPath) return null;
- console.log(folderPath);
const folder = {
path: folderPath,
name: plugin.getFolderNameFromPathString(folderPath),
diff --git a/src/main.ts b/src/main.ts
index 7d7b591..3fc3928 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,4 +1,4 @@
-import { Plugin, TFile, TFolder, TAbstractFile, MarkdownPostProcessorContext, parseYaml } from 'obsidian';
+import { Plugin, TFile, TFolder, TAbstractFile, MarkdownPostProcessorContext, parseYaml, Notice } from 'obsidian';
import { DEFAULT_SETTINGS, FolderNotesSettings, SettingsTab } from './settings';
import { Commands } from './commands';
import { FileExplorerWorkspaceLeaf } from './globals';
@@ -7,9 +7,8 @@ import { handleFileRename, handleFolderRename } from './events/handleRename';
import { createFolderNote, extractFolderName, getFolderNote, getFolder } from './functions/folderNoteFunctions';
import { getExcludedFolder } from './excludedFolder';
import { FrontMatterTitlePluginHandler } from './events/frontMatterTitle';
-import { createOverview as createFolderOverview } from './folderOverview';
-import { FolderOverviewSettings } from './modals/folderOverview';
-import { FolderOverview } from './folderOverviewClass';
+import { FolderOverviewSettings } from './folderOverview/modalSettings';
+import { FolderOverview } from './folderOverview/FolderOverview';
import './functions/ListComponent';
export default class FolderNotesPlugin extends Plugin {
observer: MutationObserver;
@@ -160,25 +159,18 @@ export default class FolderNotesPlugin extends Plugin {
if (!this.settings.syncDelete) { return; }
this.app.vault.delete(folderNote);
}));
- this.registerMarkdownCodeBlockProcessor('folder-overview', (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
- const observer = new MutationObserver(() => {
- const editButton = el.parentElement?.childNodes.item(1);
- if (editButton) {
- editButton.addEventListener('click', (e) => {
- e.stopImmediatePropagation();
- e.preventDefault();
- e.stopPropagation();
- new FolderOverviewSettings(this.app, this, parseYaml(source), ctx, el).open();
- }, { capture: true });
- }
+ if (this.app.workspace.layoutReady) {
+ this.registerMarkdownCodeBlockProcessor('folder-overview', (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
+ this.handleOverviewBlock(source, el, ctx);
});
- observer.observe(el, {
- childList: true,
- subtree: true,
+ } else {
+ this.app.workspace.onLayoutReady(() => {
+ console.log('layout ready');
+ this.registerMarkdownCodeBlockProcessor('folder-overview', (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
+ this.handleOverviewBlock(source, el, ctx);
+ });
});
- const folderOverview = new FolderOverview(this, ctx, source, el);
- folderOverview.create(this, parseYaml(source), el, ctx);
- });
+ }
if (this.app.workspace.layoutReady) {
this.loadFileClasses();
} else {
@@ -186,6 +178,32 @@ export default class FolderNotesPlugin extends Plugin {
}
}
+ handleOverviewBlock(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) {
+ console.log('handling overview block');
+ const observer = new MutationObserver(() => {
+ const editButton = el.parentElement?.childNodes.item(1);
+ if (editButton) {
+ editButton.addEventListener('click', (e) => {
+ e.stopImmediatePropagation();
+ e.preventDefault();
+ e.stopPropagation();
+ new FolderOverviewSettings(this.app, this, parseYaml(source), ctx, el).open();
+ }, { capture: true });
+ }
+ });
+ observer.observe(el, {
+ childList: true,
+ subtree: true,
+ });
+ try {
+ const folderOverview = new FolderOverview(this, ctx, source, el);
+ folderOverview.create(this, parseYaml(source), el, ctx);
+ } catch (e) {
+ new Notice('Error creating folder overview (folder notes plugin) - check console for more details');
+ console.error(e);
+ }
+ }
+
getFileNameFromPathString(path: string): string {
return path.substring(path.lastIndexOf('/' || '\\') >= 0 ? path.lastIndexOf('/' || '\\') + 1 : 0);
}
diff --git a/src/modals/folderOverview.ts b/src/modals/folderOverview.ts
deleted file mode 100644
index c748b0a..0000000
--- a/src/modals/folderOverview.ts
+++ /dev/null
@@ -1,246 +0,0 @@
-import { App, Modal, Setting, MarkdownPostProcessorContext, stringifyYaml, TFile } from 'obsidian';
-import { yamlSettings } from 'src/folderOverview';
-import FolderNotesPlugin from '../main';
-import ListComponent from 'src/functions/ListComponent';
-export class FolderOverviewSettings extends Modal {
- plugin: FolderNotesPlugin;
- app: App;
- yaml: yamlSettings;
- ctx: MarkdownPostProcessorContext;
- el: HTMLElement;
- constructor(app: App, plugin: FolderNotesPlugin, yaml: yamlSettings, ctx: MarkdownPostProcessorContext, el: HTMLElement) {
- super(app);
- this.plugin = plugin;
- this.app = app;
- this.yaml = yaml;
- this.ctx = ctx;
- this.el = el;
- if (!this.yaml) {
- this.yaml = {
- title: this.plugin.settings.defaultOverview.title,
- disableTitle: this.plugin.settings.defaultOverview.disableTitle,
- depth: this.plugin.settings.defaultOverview.depth,
- type: this.plugin.settings.defaultOverview.type,
- includeTypes: this.plugin.settings.defaultOverview.includeTypes,
- style: this.plugin.settings.defaultOverview.style,
- disableFileTag: this.plugin.settings.defaultOverview.disableFileTag,
- sortBy: this.plugin.settings.defaultOverview.sortBy,
- showEmptyFolders: this.plugin.settings.defaultOverview.showEmptyFolders,
- onlyIncludeSubfolders: this.plugin.settings.defaultOverview.onlyIncludeSubfolders,
- };
- }
- }
- onOpen() {
- this.display();
- }
- display() {
- const { contentEl } = this;
- contentEl.empty();
- // close when user presses enter
- contentEl.addEventListener('keydown', (e) => {
- if (e.key === 'Enter') {
- this.close();
- }
- });
- contentEl.createEl('h2', { text: 'Folder overview settings' });
- new Setting(contentEl)
- .setName('Disable the title')
- .setDesc('Choose if the title should be shown')
- .addToggle((toggle) =>
- toggle
- .setValue(this.yaml.disableTitle || false)
- .onChange(async (value) => {
- this.yaml.disableTitle = value;
- this.display();
- await this.updateYaml();
- })
- );
- if (!this.yaml.disableTitle) {
- new Setting(contentEl)
- .setName('Title')
- .setDesc('Choose the title of the folder overview')
- .addText((text) =>
- text
- .setValue(this.yaml?.title || '{{folderName}} overview')
- .onChange(async (value) => {
- this.yaml.title = value;
- await this.updateYaml();
- })
- );
- }
- const setting = new Setting(contentEl);
- setting.setName('Include types');
- const list = setting.createList((list: ListComponent) =>
- list
- .addModal(this)
- .setValues(this.yaml?.includeTypes || this.plugin.settings.defaultOverview.includeTypes || [])
- .addResetButton()
- );
- if ((this.yaml?.includeTypes?.length || 0) < 8 && !this.yaml.includeTypes?.includes('all')) {
- setting.addDropdown((dropdown) => {
- if (!this.yaml.includeTypes) this.yaml.includeTypes = this.plugin.settings.defaultOverview.includeTypes || [];
- this.yaml.includeTypes = this.yaml.includeTypes.map((type: string) => type.toLowerCase());
- const options = [
- { value: 'markdown', label: 'Markdown' },
- { value: 'folder', label: 'Folder' },
- { value: 'canvas', label: 'Canvas' },
- { value: 'pdf', label: 'PDF' },
- { value: 'image', label: 'Image' },
- { value: 'audio', label: 'Audio' },
- { value: 'video', label: 'Video' },
- { value: 'other', label: 'All other file types' },
- { value: 'all', label: 'All file types' },
- ];
-
- options.forEach((option) => {
- if (!this.yaml.includeTypes?.includes(option.value)) {
- dropdown.addOption(option.value, option.label);
- }
- });
- dropdown.addOption('+', '+');
- dropdown.setValue('+');
- dropdown.onChange(async (value) => {
- if (value === 'all') {
- this.yaml.includeTypes = this.yaml.includeTypes?.filter((type: string) => type === 'folder');
- // @ts-ignore
- list.setValues(this.yaml.includeTypes);
- }
- // @ts-ignore
- await list.addValue(value.toLowerCase());
- await this.updateYaml();
- this.display();
- });
- });
- }
- let disableFileTag;
- this.yaml.includeTypes?.forEach((type: string) => {
- type === 'folder' || type === 'markdown' ? (disableFileTag = true) : null;
- });
- if (disableFileTag) {
- new Setting(contentEl)
- .setName('Disable file tag')
- .setDesc('Choose if the file tag should be shown after the file name')
- .addToggle((toggle) => {
- toggle
- .setValue(this.yaml.disableFileTag || this.plugin.settings.defaultOverview.disableFileTag || false)
- .onChange(async (value) => {
- this.yaml.disableFileTag = value;
- await this.updateYaml();
- });
- });
- }
-
- new Setting(contentEl)
- .setName('File depth')
- .setDesc('File & folder = +1 depth')
- .addSlider((slider) =>
- slider
- .setValue(this.yaml?.depth || 2)
- .setLimits(1, 10, 1)
- .onChange(async (value) => {
- this.yaml.depth = value;
- await this.updateYaml();
- })
- );
-
- new Setting(contentEl)
- .setName('Overview style')
- .setDesc('Choose the style of the overview (grid style soon)')
- .addDropdown((dropdown) =>
- dropdown
- .addOption('list', 'List')
- .setValue(this.yaml?.style || 'list')
- .onChange(async (value: 'list') => {
- this.yaml.style = value;
- await this.updateYaml();
- })
- );
-
- new Setting(contentEl)
- .setName('Sort files by')
- .setDesc('Choose how the files should be sorted')
- .addDropdown((dropdown) =>
- dropdown
- .addOption('name', 'Name descending')
- .addOption('created', 'Created descending')
- .addOption('modified', 'Modified descending')
- .addOption('nameAsc', 'Name ascending')
- .addOption('createdAsc', 'Created ascending')
- .addOption('modifiedAsc', 'Modified ascending')
- .setValue(this.yaml?.sortBy || 'name')
- .onChange(async (value: 'name' | 'created' | 'modified' | 'nameAsc' | 'createdAsc' | 'modifiedAsc') => {
- this.yaml.sortBy = value;
- await this.updateYaml();
- })
- );
-
- new Setting(contentEl)
- .setName('Show folder names of empty folders')
- .setDesc('That includes subfolders of the current folder when you are on file depth 1')
- .addToggle((toggle) => {
- toggle
- .setValue(this.yaml.showEmptyFolders || this.plugin.settings.defaultOverview.showEmptyFolders || false)
- .onChange(async (value) => {
- this.yaml.showEmptyFolders = value;
- this.yaml.onlyIncludeSubfolders = false;
- this.display();
- await this.updateYaml();
- });
- });
-
- if (this.yaml.showEmptyFolders) {
- new Setting(contentEl)
- .setName('Only show first empty subfolders of current folder')
- .addToggle((toggle) => {
- toggle
- .setValue(this.yaml.onlyIncludeSubfolders || this.plugin.settings.defaultOverview.onlyIncludeSubfolders || false)
- .onChange(async (value) => {
- this.yaml.onlyIncludeSubfolders = value;
- await this.updateYaml();
- });
- });
- }
-
-
- }
- onClose() {
- const { contentEl } = this;
- contentEl.empty();
- }
- async updateYaml() {
- const file = this.plugin.app.vault.getAbstractFileByPath(this.ctx.sourcePath);
- if (!(file instanceof TFile)) return;
- let stringYaml = stringifyYaml(this.yaml);
- await this.plugin.app.vault.process(file, (text) => {
- const info = this.ctx.getSectionInfo(this.el);
- // check if stringYaml ends with a newline
- if (stringYaml[stringYaml.length - 1] !== '\n') {
- stringYaml += '\n';
- }
- if (info) {
- const { lineStart } = info;
- const lineEnd = this.getCodeBlockEndLine(text, lineStart);
- if (lineEnd === -1 || !lineEnd) return text;
- const lineLength = lineEnd - lineStart;
- const lines = text.split('\n');
- lines.splice(lineStart, lineLength + 1, `\`\`\`folder-overview\n${stringYaml}\`\`\``);
- return lines.join('\n');
- }
- return `\`\`\`folder-overview\n${stringYaml}\`\`\``;
- });
- }
- getCodeBlockEndLine(text: string, startLine: number, count = 1) {
- let line = startLine + 1;
- const lines = text.split('\n');
- while (line < lines.length) {
- if (count > 20) { return -1; }
- if (lines[line].startsWith('```')) {
- return line;
- }
- line++;
- count++;
- }
- return line;
- }
-}
-
diff --git a/src/settings.ts b/src/settings.ts
index c788993..77e0d40 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -5,7 +5,8 @@ import { extractFolderName, getFolderNote } from './functions/folderNoteFunction
import { addExcludeFolderListItem, ExcludedFolder, addExcludedFolder, ExcludePattern, addExcludePatternListItem } from './excludedFolder';
import { FrontMatterTitlePluginHandler } from './events/frontMatterTitle';
import ConfirmationModal from "./modals/confirmCreation";
-import { yamlSettings } from './folderOverview';
+import { yamlSettings } from './folderOverview/FolderOverview';
+import { FolderOverviewSettings } from './folderOverview/modalSettings';
export interface FolderNotesSettings {
syncFolderName: boolean;
ctrlKey: boolean;
@@ -61,12 +62,18 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = {
syncDelete: false,
showRenameConfirmation: true,
defaultOverview: {
+ folderPath: '',
title: '{{folderName}} overview',
disableTitle: false,
depth: 3,
includeTypes: ['folder', 'markdown'],
style: 'list',
disableFileTag: false,
+ sortBy: 'name',
+ sortByAsc: true,
+ showEmptyFolders: false,
+ onlyIncludeSubfolders: false,
+ storeFolderCondition: true,
},
useSubmenus: true,
syncMove: true,
@@ -127,6 +134,18 @@ export class SettingsTab extends PluginSettingTab {
})
);
+ new Setting(containerEl)
+ .setName('Manage folder overview defaults')
+ .setDesc('Manage the default settings for the folder overview plugin')
+ .addButton((button) =>
+ button
+ .setButtonText('Manage')
+ .setCta()
+ .onClick(async () => {
+ new FolderOverviewSettings(this.plugin.app, this.plugin, this.plugin.settings.defaultOverview, null, null, true).open();
+ })
+ );
+
const setting = new Setting(containerEl);
const desc = document.createDocumentFragment();
desc.append(
@@ -210,11 +229,9 @@ export class SettingsTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.syncMove = value;
await this.plugin.saveSettings();
- }
- )
+ })
);
}
-
const disableSetting = new Setting(containerEl);
disableSetting.setName('Disable folder collapsing');
disableSetting.setDesc('Disable the ability to collapse folders by clicking exactly on the folder name');
@@ -449,16 +466,16 @@ export class SettingsTab extends PluginSettingTab {
new Setting(containerEl)
- .setName('Create folder note for every folder')
- .setDesc('Create a folder note for every folder in the vault')
- .addButton((cb) => {
- cb.setIcon('plus');
- cb.setTooltip('Create folder notes');
- cb.onClick(async () => {
-
- new ConfirmationModal(this.app, this.plugin).open();
+ .setName('Create folder note for every folder')
+ .setDesc('Create a folder note for every folder in the vault')
+ .addButton((cb) => {
+ cb.setIcon('plus');
+ cb.setTooltip('Create folder notes');
+ cb.onClick(async () => {
+
+ new ConfirmationModal(this.app, this.plugin).open();
+ });
});
- });
const manageExcluded = new Setting(containerEl)
diff --git a/src/suggesters/FolderSuggester.ts b/src/suggesters/FolderSuggester.ts
index e0c3b6d..cf62d1e 100644
--- a/src/suggesters/FolderSuggester.ts
+++ b/src/suggesters/FolderSuggester.ts
@@ -11,7 +11,8 @@ export enum FileSuggestMode {
export class FolderSuggest extends TextInputSuggest {
constructor(
public inputEl: HTMLInputElement,
- private plugin: FolderNotesPlugin
+ private plugin: FolderNotesPlugin,
+ public folder?: TFolder,
) {
super(inputEl);
}
@@ -29,7 +30,13 @@ export class FolderSuggest extends TextInputSuggest {
getSuggestions(input_str: string): TFolder[] {
const folders: TFolder[] = [];
const lower_input_str = input_str.toLowerCase();
- this.plugin.app.vault.getAllLoadedFiles().forEach((folder: TAbstractFile) => {
+ let files: TAbstractFile[] = [];
+ if (this.folder) {
+ files = this.folder.children;
+ } else {
+ files = this.plugin.app.vault.getAllLoadedFiles();
+ }
+ files.forEach((folder: TAbstractFile) => {
if (
folder instanceof TFolder &&
folder.path.toLowerCase().contains(lower_input_str) &&