From 7c31c7cec57ebd1aefdb69f0e365674abc134138 Mon Sep 17 00:00:00 2001
From: Lost Paul <70213368+LostPaul@users.noreply.github.com>
Date: Mon, 11 Aug 2025 14:39:33 +0200
Subject: [PATCH] Every remaining file
---
src/Commands.ts | 496 ++++++++++--------
src/functions/ListComponent.ts | 26 +-
src/functions/excalidraw.ts | 33 +-
src/functions/folderNoteFunctions.ts | 458 ++++++++++++----
src/functions/styleFunctions.ts | 83 ++-
src/functions/utils.ts | 23 +-
src/globals.d.ts | 2 -
src/main.ts | 292 +++++++----
src/modals/AddSupportedFileType.ts | 12 +-
src/modals/AskForExtension.ts | 27 +-
src/modals/DeleteConfirmation.ts | 28 +-
src/modals/ExistingNote.ts | 29 +-
src/modals/FolderName.ts | 23 +-
src/modals/NewFolderName.ts | 27 +-
src/settings/ExcludedFoldersSettings.ts | 49 +-
src/settings/FileExplorerSettings.ts | 19 +-
src/settings/FolderOverviewSettings.ts | 15 +-
src/settings/GeneralSettings.ts | 72 ++-
src/settings/PathSettings.ts | 3 +-
src/settings/SettingsTab.ts | 49 +-
src/settings/modals/BackupWarning.ts | 23 +-
src/settings/modals/CreateFnForEveryFolder.ts | 38 +-
src/settings/modals/RenameFns.ts | 10 +-
src/suggesters/FolderSuggester.ts | 14 +-
src/suggesters/Suggest.ts | 17 +-
src/suggesters/TemplateSuggester.ts | 18 +-
src/template.ts | 66 ++-
27 files changed, 1333 insertions(+), 619 deletions(-)
diff --git a/src/Commands.ts b/src/Commands.ts
index 56f7dd1..a27b541 100644
--- a/src/Commands.ts
+++ b/src/Commands.ts
@@ -1,11 +1,35 @@
-import type { App, Menu, TAbstractFile, Editor, MarkdownView } from 'obsidian';
-import { TFolder, Notice, TFile, Platform } from 'obsidian';
+import {
+ TFolder,
+ Notice,
+ TFile,
+ Platform,
+ type App,
+ type Menu,
+ type TAbstractFile,
+ type Editor,
+ type MarkdownView,
+} from 'obsidian';
import type FolderNotesPlugin from './main';
-import { getFolderNote, createFolderNote, deleteFolderNote, turnIntoFolderNote, openFolderNote, extractFolderName, detachFolderNote } from './functions/folderNoteFunctions';
+import {
+ getFolderNote,
+ createFolderNote,
+ deleteFolderNote,
+ turnIntoFolderNote,
+ openFolderNote,
+ extractFolderName,
+ detachFolderNote,
+} from './functions/folderNoteFunctions';
import { ExcludedFolder } from './ExcludeFolders/ExcludeFolder';
import { getFolderPathFromString, getFileExplorerActiveFolder } from './functions/utils';
-import { deleteExcludedFolder, getDetachedFolder, getExcludedFolder } from './ExcludeFolders/functions/folderFunctions';
-import { hideFolderNoteInFileExplorer, showFolderNoteInFileExplorer } from './functions/styleFunctions';
+import {
+ deleteExcludedFolder,
+ getDetachedFolder,
+ getExcludedFolder,
+} from './ExcludeFolders/functions/folderFunctions';
+import {
+ hideFolderNoteInFileExplorer,
+ showFolderNoteInFileExplorer,
+} from './functions/styleFunctions';
@@ -16,12 +40,13 @@ export class Commands {
this.plugin = plugin;
this.app = app;
}
- registerCommands() {
+ registerCommands(): void {
this.editorCommands();
this.fileCommands();
this.regularCommands();
}
- regularCommands() {
+
+ regularCommands(): void {
this.plugin.addCommand({
id: 'turn-into-folder-note',
name: 'Use this file as the folder note for its parent folder',
@@ -52,7 +77,8 @@ export class Commands {
if (this.plugin.app.vault.getAbstractFileByPath(newPath)) {
return new Notice('Folder already exists');
}
- const automaticallyCreateFolderNote = this.plugin.settings.autoCreate;
+ const automaticallyCreateFolderNote =
+ this.plugin.settings.autoCreate;
this.plugin.settings.autoCreate = false;
this.plugin.saveSettings();
await this.plugin.app.vault.createFolder(newPath);
@@ -187,6 +213,7 @@ export class Commands {
const blacklist = ['*', '\\', '"', '/', '<', '>', '?', '|', ':'];
for (const char of blacklist) {
if (text.includes(char)) {
+ // eslint-disable-next-line max-len
new Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?');
return false;
}
@@ -207,13 +234,18 @@ export class Commands {
createFolderNote(this.plugin, text, false);
} else {
- folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text);
+ const folderFullPath = folderPath + '/' + text;
+ folder = this.plugin.app.vault.getAbstractFileByPath(folderFullPath);
if (folder instanceof TFolder) {
new Notice('Folder note already exists');
return false;
}
if (this.plugin.settings.storageLocation === 'parentFolder') {
- if (this.app.vault.getAbstractFileByPath(folderPath + '/' + text + this.plugin.settings.folderNoteType)) {
+ if (
+ this.app.vault.getAbstractFileByPath(
+ folderPath + '/' + text + this.plugin.settings.folderNoteType,
+ )
+ ) {
new Notice('File already exists');
return false;
}
@@ -221,7 +253,9 @@ export class Commands {
this.plugin.app.vault.createFolder(folderPath + '/' + text);
createFolderNote(this.plugin, folderPath + '/' + text, false);
}
- const fileName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', text);
+
+ const { folderNoteName } = this.plugin.settings;
+ const fileName = folderNoteName.replace('{{folder_name}}', text);
if (fileName !== text) {
editor.replaceSelection(`[[${fileName}]]`);
} else {
@@ -234,213 +268,243 @@ export class Commands {
});
}
- 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) {
- if (this.plugin.settings.storageLocation === 'insideFolder') {
- folder = file.parent;
- } else {
- const fileName = extractFolderName(this.plugin.settings.folderNoteName, file.basename);
- if (fileName) {
- if (file.parent?.path === '' || file.parent?.path === '/') {
- folder = this.plugin.app.vault.getAbstractFileByPath(fileName);
- } else {
- folder = this.plugin.app.vault.getAbstractFileByPath(file.parent?.path + '/' + fileName);
- }
- }
- }
-
- if (folder instanceof TFolder) {
- const folderNote = getFolderNote(this.plugin, folder.path);
- const excludedFolder = getExcludedFolder(this.plugin, folder.path, true);
- if (folderNote?.path === file.path && !excludedFolder?.detached) { return; }
- } else if (file.parent instanceof TFolder) {
- folder = file.parent;
- }
- }
-
- menu.addItem(async (item) => {
- if (Platform.isDesktop && !Platform.isTablet && this.plugin.settings.useSubmenus) {
- item
- .setTitle('Folder Note Commands')
- .setIcon('folder-edit');
- }
- let subMenu: Menu;
- if (!Platform.isDesktopApp || !Platform.isDesktop || Platform.isTablet || !this.plugin.settings.useSubmenus) {
- subMenu = menu;
- item.setDisabled(true);
- } else {
- // @ts-ignore
- subMenu = item.setSubmenu() as Menu;
- }
+ fileCommands(): void {
+ this.plugin.registerEvent(
+ this.app.workspace.on('file-menu', (menu: Menu, file: TAbstractFile) => {
+ let folder: TAbstractFile | TFolder | null = file.parent;
if (file instanceof TFile) {
- // @ts-ignore
- subMenu.addItem((item) => {
- item.setTitle('Create folder note')
- .setIcon('edit')
- .onClick(async () => {
- if (!folder) return;
- let newPath = folder.path + '/' + file.basename;
- if (folder.path === '' || folder.path === '/') {
- newPath = file.basename;
- }
- if (this.plugin.app.vault.getAbstractFileByPath(newPath)) {
- return new Notice('Folder already exists');
- }
- const automaticallyCreateFolderNote = this.plugin.settings.autoCreate;
- this.plugin.settings.autoCreate = false;
- this.plugin.saveSettings();
- await this.plugin.app.vault.createFolder(newPath);
- const newFolder = this.plugin.app.vault.getAbstractFileByPath(newPath);
- if (!(newFolder instanceof TFolder)) return;
- await createFolderNote(this.plugin, newFolder.path, true, '.' + file.extension, false, file);
- this.plugin.settings.autoCreate = automaticallyCreateFolderNote;
- this.plugin.saveSettings();
- });
- });
- if (getFolderPathFromString(file.path) === '') return;
- if (!(folder instanceof TFolder)) return;
- if (folder.path === '' || folder.path === '/') return;
- subMenu.addItem((item) => {
- item.setTitle(`Turn into folder note for ${folder?.name}`)
- .setIcon('edit')
- .onClick(() => {
- if (!folder || !(folder instanceof TFolder)) return;
- const folderNote = getFolderNote(this.plugin, folder.path);
- turnIntoFolderNote(this.plugin, file, folder, folderNote);
- });
- });
- }
- if (!(file instanceof TFolder)) return;
- const excludedFolder = getExcludedFolder(this.plugin, file.path, false);
- const detachedExcludedFolder = getDetachedFolder(this.plugin, file.path);
- if (excludedFolder && !excludedFolder.hideInSettings) {
- // I'm not sure if I'm ever going to add this because of the possibility that a folder got more than one excluded
- // subMenu.addItem((item) => {
- // item.setTitle('Manage excluded folder')
- // .setIcon('settings-2')
- // .onClick(() => {
- // if (excludedFolder instanceof ExcludedFolder) {
- // new ExcludedFolderSettings(this.plugin.app, this.plugin, excludedFolder).open();
- // } else if (excludedFolder instanceof ExcludePattern) {
- // new PatternSettings(this.plugin.app, this.plugin, excludedFolder).open();
- // }
- // })
- // })
- subMenu.addItem((item) => {
- item.setTitle('Remove folder from excluded folders')
- .setIcon('trash')
- .onClick(() => {
- this.plugin.settings.excludeFolders = this.plugin.settings.excludeFolders.filter(
- (folder) => (folder.path !== file.path) || folder.detached);
- this.plugin.saveSettings(true);
- new Notice('Successfully removed folder from excluded folders');
- });
- });
- return;
- }
- if (detachedExcludedFolder) {
- subMenu.addItem((item) => {
- item.setTitle('Remove folder from detached folders')
- .setIcon('trash')
- .onClick(() => {
- deleteExcludedFolder(this.plugin, detachedExcludedFolder);
- });
- });
- }
- if (detachedExcludedFolder) { return; }
- subMenu.addItem((item) => {
- item.setTitle('Exclude folder from folder notes')
- .setIcon('x-circle')
- .onClick(() => {
- const excludedFolder = new ExcludedFolder(file.path, this.plugin.settings.excludeFolders.length, undefined, this.plugin);
- this.plugin.settings.excludeFolders.push(excludedFolder);
- this.plugin.saveSettings(true);
- new Notice('Successfully excluded folder from folder notes');
- });
- });
- if (!(file instanceof TFolder)) return;
- const folderNote = getFolderNote(this.plugin, file.path);
- if (folderNote instanceof TFile && !detachedExcludedFolder) {
- subMenu.addItem((item) => {
- item.setTitle('Delete folder note')
- .setIcon('trash')
- .onClick(() => {
- deleteFolderNote(this.plugin, folderNote, true);
- });
- });
-
- subMenu.addItem((item) => {
- item.setTitle('Open folder note')
- .setIcon('chevron-right-square')
- .onClick(() => {
- openFolderNote(this.plugin, folderNote);
- });
- });
-
- subMenu.addItem((item) => {
- item.setTitle('Detach folder note')
- .setIcon('unlink')
- .onClick(() => {
- detachFolderNote(this.plugin, folderNote);
- });
- });
-
- subMenu.addItem((item) => {
- item.setTitle('Copy Obsidian URL')
- .setIcon('link')
- .onClick(() => {
- // @ts-ignore
- this.app.copyObsidianUrl(folderNote);
- });
- });
-
- if (this.plugin.settings.hideFolderNote) {
- if (excludedFolder?.showFolderNote) {
- subMenu.addItem((item) => {
- item.setTitle('Hide folder note in explorer')
- .setIcon('eye-off')
- .onClick(() => {
- hideFolderNoteInFileExplorer(file.path, this.plugin);
- });
- });
- } else {
- subMenu.addItem((item) => {
- item.setTitle('Show folder note in explorer')
- .setIcon('eye')
- .onClick(() => {
- showFolderNoteInFileExplorer(file.path, this.plugin);
- });
- });
+ if (this.plugin.settings.storageLocation === 'insideFolder') {
+ folder = file.parent;
+ } else {
+ const { folderNoteName } = this.plugin.settings;
+ const fileName = extractFolderName(folderNoteName, file.basename);
+ if (fileName) {
+ if (file.parent?.path === '' || file.parent?.path === '/') {
+ folder = this.plugin.app.vault.getAbstractFileByPath(fileName);
+ } else {
+ folder = this.plugin.app.vault.getAbstractFileByPath(
+ file.parent?.path + '/' + fileName,
+ );
+ }
}
}
- } else {
- subMenu.addItem((item) => {
- item.setTitle('Create markdown folder note')
- .setIcon('edit')
- .onClick(() => {
- createFolderNote(this.plugin, file.path, true, '.md');
- });
- });
+ if (folder instanceof TFolder) {
+ const folderNote = getFolderNote(this.plugin, folder.path);
+ const excludedFolder = getExcludedFolder(this.plugin, folder.path, true);
+ if (folderNote?.path === file.path && !excludedFolder?.detached) { return; }
+ } else if (file.parent instanceof TFolder) {
+ folder = file.parent;
+ }
+ }
- this.plugin.settings.supportedFileTypes.forEach((fileType) => {
- if (fileType === 'md') return;
- subMenu.addItem((item) => {
- item.setTitle(`Create ${fileType} folder note`)
+ // eslint-disable-next-line complexity
+ menu.addItem(async (menuItem) => {
+ if (
+ Platform.isDesktop &&
+ !Platform.isTablet &&
+ this.plugin.settings.useSubmenus
+ ) {
+ menuItem
+ .setTitle('Folder Note Commands')
+ .setIcon('folder-edit');
+ }
+ let subMenu: Menu;
+ if (
+ !Platform.isDesktopApp ||
+ !Platform.isDesktop ||
+ Platform.isTablet ||
+ !this.plugin.settings.useSubmenus
+ ) {
+ subMenu = menu;
+ menuItem.setDisabled(true);
+ } else {
+ subMenu = menuItem.setSubmenu() as Menu;
+ }
+ if (file instanceof TFile) {
+ subMenu.addItem((subItem) => {
+ subItem.setTitle('Create folder note')
.setIcon('edit')
- .onClick(() => {
- createFolderNote(this.plugin, file.path, true, '.' + fileType);
+ .onClick(async () => {
+ if (!folder) return;
+ let newPath = folder.path + '/' + file.basename;
+ if (folder.path === '' || folder.path === '/') {
+ newPath = file.basename;
+ }
+ if (this.plugin.app.vault.getAbstractFileByPath(newPath)) {
+ return new Notice('Folder already exists');
+ }
+ const automaticallyCreateFolderNote =
+ this.plugin.settings.autoCreate;
+ this.plugin.settings.autoCreate = false;
+ this.plugin.saveSettings();
+ await this.plugin.app.vault.createFolder(newPath);
+ const newFolder = this.plugin.app.vault
+ .getAbstractFileByPath(newPath);
+ if (!(newFolder instanceof TFolder)) return;
+ await createFolderNote(
+ this.plugin,
+ newFolder.path,
+ true,
+ '.' + file.extension,
+ false,
+ file,
+ );
+ this.plugin.settings.autoCreate = automaticallyCreateFolderNote;
+ this.plugin.saveSettings();
});
});
+ if (getFolderPathFromString(file.path) === '') return;
+ if (!(folder instanceof TFolder)) return;
+ if (folder.path === '' || folder.path === '/') return;
+ subMenu.addItem((item) => {
+ item.setTitle(`Turn into folder note for ${folder?.name}`)
+ .setIcon('edit')
+ .onClick(() => {
+ if (!folder || !(folder instanceof TFolder)) return;
+ const folderNote = getFolderNote(this.plugin, folder.path);
+ turnIntoFolderNote(this.plugin, file, folder, folderNote);
+ });
+ });
+ }
+ if (!(file instanceof TFolder)) return;
+ const excludedFolder = getExcludedFolder(this.plugin, file.path, false);
+ const detachedExcludedFolder = getDetachedFolder(this.plugin, file.path);
+ if (excludedFolder && !excludedFolder.hideInSettings) {
+ // I'm not sure if I'm ever going to add this because of the possibility that a folder got more than one excluded
+ // subMenu.addItem((item) => {
+ // item.setTitle('Manage excluded folder')
+ // .setIcon('settings-2')
+ // .onClick(() => {
+ // if (excludedFolder instanceof ExcludedFolder) {
+ // new ExcludedFolderSettings(this.plugin.app, this.plugin, excludedFolder).open();
+ // } else if (excludedFolder instanceof ExcludePattern) {
+ // new PatternSettings(this.plugin.app, this.plugin, excludedFolder).open();
+ // }
+ // })
+ // })
+ subMenu.addItem((item) => {
+ item.setTitle('Remove folder from excluded folders')
+ .setIcon('trash')
+ .onClick(() => {
+ this.plugin.settings.excludeFolders =
+ this.plugin.settings.excludeFolders.filter(
+ (excluded) =>
+ (excluded.path !== file.path) || excluded.detached,
+ );
+ this.plugin.saveSettings(true);
+ new Notice('Successfully removed folder from excluded folders');
+ });
+ });
+ return;
+ }
+ if (detachedExcludedFolder) {
+ subMenu.addItem((item) => {
+ item.setTitle('Remove folder from detached folders')
+ .setIcon('trash')
+ .onClick(() => {
+ deleteExcludedFolder(this.plugin, detachedExcludedFolder);
+ });
+ });
+ }
+ if (detachedExcludedFolder) { return; }
+ subMenu.addItem((item) => {
+ item.setTitle('Exclude folder from folder notes')
+ .setIcon('x-circle')
+ .onClick(() => {
+ const newExcludedFolder = new ExcludedFolder(
+ file.path,
+ this.plugin.settings.excludeFolders.length,
+ undefined,
+ this.plugin,
+ );
+ this.plugin.settings.excludeFolders.push(newExcludedFolder);
+ this.plugin.saveSettings(true);
+ new Notice('Successfully excluded folder from folder notes');
+ });
});
- }
- });
- }));
+ if (!(file instanceof TFolder)) return;
+ const folderNote = getFolderNote(this.plugin, file.path);
+ if (folderNote instanceof TFile && !detachedExcludedFolder) {
+ subMenu.addItem((item) => {
+ item.setTitle('Delete folder note')
+ .setIcon('trash')
+ .onClick(() => {
+ deleteFolderNote(this.plugin, folderNote, true);
+ });
+ });
+
+ subMenu.addItem((item) => {
+ item.setTitle('Open folder note')
+ .setIcon('chevron-right-square')
+ .onClick(() => {
+ openFolderNote(this.plugin, folderNote);
+ });
+ });
+
+ subMenu.addItem((item) => {
+ item.setTitle('Detach folder note')
+ .setIcon('unlink')
+ .onClick(() => {
+ detachFolderNote(this.plugin, folderNote);
+ });
+ });
+
+ subMenu.addItem((item) => {
+ item.setTitle('Copy Obsidian URL')
+ .setIcon('link')
+ .onClick(() => {
+ this.app.copyObsidianUrl(folderNote);
+ });
+ });
+
+ if (this.plugin.settings.hideFolderNote) {
+ if (excludedFolder?.showFolderNote) {
+ subMenu.addItem((item) => {
+ item.setTitle('Hide folder note in explorer')
+ .setIcon('eye-off')
+ .onClick(() => {
+ hideFolderNoteInFileExplorer(file.path, this.plugin);
+ });
+ });
+ } else {
+ subMenu.addItem((item) => {
+ item.setTitle('Show folder note in explorer')
+ .setIcon('eye')
+ .onClick(() => {
+ showFolderNoteInFileExplorer(file.path, this.plugin);
+ });
+ });
+ }
+ }
+
+ } else {
+ subMenu.addItem((item) => {
+ item.setTitle('Create markdown folder note')
+ .setIcon('edit')
+ .onClick(() => {
+ createFolderNote(this.plugin, file.path, true, '.md');
+ });
+ });
+
+ this.plugin.settings.supportedFileTypes.forEach((fileType) => {
+ if (fileType === 'md') return;
+ subMenu.addItem((item) => {
+ item.setTitle(`Create ${fileType} folder note`)
+ .setIcon('edit')
+ .onClick(() => {
+ // eslint-disable-next-line max-len
+ createFolderNote(this.plugin, file.path, true, '.' + fileType);
+ });
+ });
+ });
+ }
+ });
+ }));
}
- editorCommands() {
+ editorCommands(): void {
+ // eslint-disable-next-line max-len
this.plugin.registerEvent(this.plugin.app.workspace.on('editor-menu', (menu: Menu, editor: Editor, view: MarkdownView) => {
const text = editor.getSelection().trim();
if (!text || text.trim() === '') return;
@@ -453,6 +517,7 @@ export class Commands {
const blacklist = ['*', '\\', '"', '/', '<', '>', '?', '|', ':'];
for (const char of blacklist) {
if (text.includes(char)) {
+ // eslint-disable-next-line max-len
new Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?');
return;
}
@@ -461,9 +526,11 @@ export class Commands {
new Notice('File name cannot end with a dot');
return;
}
+
let folder: TAbstractFile | null;
const folderPath = getFolderPathFromString(file.path);
- const fileName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', text);
+ const { folderNoteName } = this.plugin.settings;
+ const fileName = folderNoteName.replace('{{folder_name}}', text);
if (folderPath === '') {
folder = this.plugin.app.vault.getAbstractFileByPath(text);
if (folder instanceof TFolder) {
@@ -473,12 +540,21 @@ export class Commands {
createFolderNote(this.plugin, text, false);
} else {
- folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text);
+ 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 + '/' + fileName + this.plugin.settings.folderNoteType)) {
+ if (
+ this.app.vault.getAbstractFileByPath(
+ folderPath +
+ '/' +
+ fileName +
+ this.plugin.settings.folderNoteType,
+ )
+ ) {
return new Notice('File already exists');
}
}
diff --git a/src/functions/ListComponent.ts b/src/functions/ListComponent.ts
index 0c8e21e..f6486a8 100644
--- a/src/functions/ListComponent.ts
+++ b/src/functions/ListComponent.ts
@@ -18,19 +18,19 @@ export class ListComponent {
this.defaultValues = defaultValues;
}
- on(event: string, listener: (data?: any) => void) {
+ on(event: string, listener: (data?: unknown) => void): void {
this.emitter.on(event, listener);
}
- off(event: string, listener: (data?: any) => void) {
+ off(event: string, listener: (data?: unknown) => void): void {
this.emitter.off(event, listener);
}
- private emit(event: string, data?: any) {
+ private emit(event: string, data?: unknown): void {
this.emitter.emit(event, data);
}
- setValues(values: string[]) {
+ setValues(values: string[]): void {
this.removeElements();
this.values = values;
if (values.length !== 0) {
@@ -41,11 +41,11 @@ export class ListComponent {
this.emit('update', this.values);
}
- removeElements() {
+ removeElements(): void {
this.listEl.empty();
}
- addElement(value: string) {
+ addElement(value: string): void {
this.listEl.createSpan('setting-hotkey', (span) => {
if (value.toLocaleLowerCase() === 'md') {
span.innerText = 'markdown';
@@ -53,35 +53,39 @@ export class ListComponent {
span.innerText = value;
}
span.setAttribute('extension', value);
+ // eslint-disable-next-line max-len
const removeSpan = span.createEl('span', { cls: 'ofn-list-item-remove setting-hotkey-icon' });
+ // eslint-disable-next-line max-len
const svg = '';
const svgElement = removeSpan.createEl('span', { cls: 'ofn-list-item-remove-icon' });
svgElement.innerHTML = svg;
- removeSpan.onClickEvent((e) => {
+ removeSpan.onClickEvent(() => {
this.removeValue(value);
span.remove();
});
});
}
- async addValue(value: string) {
+ async addValue(value: string): Promise {
this.values.push(value);
this.addElement(value);
this.emit('add', value);
this.emit('update', this.values);
}
- addResetButton() {
+ addResetButton(): this {
+ // eslint-disable-next-line max-len
const resetButton = this.controlEl.createEl('span', { cls: 'clickable-icon setting-restore-hotkey-button' });
+ // eslint-disable-next-line max-len
const svg = '';
resetButton.innerHTML = svg;
- resetButton.onClickEvent((e) => {
+ resetButton.onClickEvent(() => {
this.setValues(this.defaultValues);
});
return this;
}
- removeValue(value: string) {
+ removeValue(value: string): void {
this.values = this.values.filter((v) => v !== value);
this.listEl.find(`[extension='${value}']`).remove();
this.emit('remove', value);
diff --git a/src/functions/excalidraw.ts b/src/functions/excalidraw.ts
index 81eb8c8..8b0286e 100644
--- a/src/functions/excalidraw.ts
+++ b/src/functions/excalidraw.ts
@@ -1,19 +1,36 @@
import type { WorkspaceLeaf, App } from 'obsidian';
-export async function openExcalidrawView(leaf: WorkspaceLeaf) {
- const { excalidraw, excalidrawEnabled } = await getExcalidrawPlugin(this.app);
- if (excalidrawEnabled) {
+interface ExcalidrawPlugin {
+ setExcalidrawView(leaf: WorkspaceLeaf): void;
+}
+
+export async function openExcalidrawView(
+ app: App,
+ leaf: WorkspaceLeaf,
+): Promise {
+ const { excalidraw, excalidrawEnabled } = await getExcalidrawPlugin(app);
+ if (excalidrawEnabled && excalidraw) {
excalidraw.setExcalidrawView(leaf);
}
}
-export async function getExcalidrawPlugin(app: App) {
- const excalidraw = (app as any).plugins.plugins['obsidian-excalidraw-plugin'];
- const excalidrawEnabled = (app as any).plugins.enabledPlugins.has(
- 'obsidian-excalidraw-plugin',
+export async function getExcalidrawPlugin(
+ app: App,
+): Promise<{ excalidraw: ExcalidrawPlugin | null; excalidrawEnabled: boolean }> {
+ const { plugins: pluginManager } = app as App & {
+ plugins: {
+ plugins: Record;
+ enabledPlugins: Set;
+ };
+ };
+ const excalidraw = (
+ pluginManager.plugins[
+ 'obsidian-excalidraw-plugin'
+ ] as unknown as ExcalidrawPlugin | undefined
);
+ const excalidrawEnabled = pluginManager.enabledPlugins.has('obsidian-excalidraw-plugin');
return {
- excalidraw,
+ excalidraw: excalidraw ?? null,
excalidrawEnabled,
};
}
diff --git a/src/functions/folderNoteFunctions.ts b/src/functions/folderNoteFunctions.ts
index 17f6944..69e1830 100644
--- a/src/functions/folderNoteFunctions.ts
+++ b/src/functions/folderNoteFunctions.ts
@@ -1,15 +1,39 @@
+/* eslint-disable complexity */
+/* eslint-disable max-len */
import type FolderNotesPlugin from '../main';
import ExistingFolderNoteModal from '../modals/ExistingNote';
import { applyTemplate } from '../template';
-import type { TAbstractFile } from 'obsidian';
-import { TFolder, TFile, Keymap } from 'obsidian';
+import {
+ TFolder,
+ TFile,
+ Keymap,
+ type TAbstractFile,
+ type MarkdownView,
+ type WorkspaceLeaf,
+} from 'obsidian';
import DeleteConfirmationModal from '../modals/DeleteConfirmation';
-import { addExcludedFolder, deleteExcludedFolder, getDetachedFolder, getExcludedFolder, updateExcludedFolder } from '../ExcludeFolders/functions/folderFunctions';
+import {
+ addExcludedFolder,
+ deleteExcludedFolder,
+ getDetachedFolder,
+ getExcludedFolder,
+ updateExcludedFolder,
+} from '../ExcludeFolders/functions/folderFunctions';
import { ExcludedFolder } from '../ExcludeFolders/ExcludeFolder';
import { openExcalidrawView } from './excalidraw';
import { AskForExtensionModal } from 'src/modals/AskForExtension';
-import { addCSSClassToFileExplorerEl, removeCSSClassFromFileExplorerEL, removeActiveFolder, setActiveFolder } from 'src/functions/styleFunctions';
-import { getFolderNameFromPathString, getFolderPathFromString, removeExtension } from 'src/functions/utils';
+import {
+ addCSSClassToFileExplorerEl,
+ removeCSSClassFromFileExplorerEL,
+ removeActiveFolder,
+ setActiveFolder,
+} from 'src/functions/styleFunctions';
+import {
+ getFolderNameFromPathString,
+ getFolderPathFromString,
+ removeExtension,
+} from 'src/functions/utils';
+
const defaultExcalidrawTemplate = `---
@@ -27,24 +51,36 @@ tags: [excalidraw]
\`\`\`
%%`;
-export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: string, openFile: boolean, extension?: string, displayModal?: boolean, preexistingNote?: TFile) {
- const leaf = plugin.app.workspace.getLeaf(false);
- const folderName = getFolderNameFromPathString(folderPath);
- const fileName = plugin.settings.folderNoteName.replace('{{folder_name}}', folderName);
- let folderNote = getFolderNote(plugin, folderPath);
- if (preexistingNote) {
- folderNote = preexistingNote;
- }
- let folderNoteType = extension ?? plugin.settings.folderNoteType;
- const detachedFolder = getDetachedFolder(plugin, folderPath);
- let path = '';
+export async function createFolderNote(
+ plugin: FolderNotesPlugin,
+ folderPath: string,
+ openFile: boolean,
+ extension?: string,
+ displayModal?: boolean,
+ preexistingNote?: TFile,
+): Promise {
+ let {
+ leaf,
+ fileName,
+ folderNote,
+ folderNoteType,
+ detachedFolder,
+ path,
+ } = getArgs(plugin, folderPath, extension, preexistingNote);
if (folderNoteType === '.excalidraw') {
folderNoteType = '.md';
extension = '.excalidraw';
} else if (folderNoteType === '.ask') {
if (plugin.askModalCurrentlyOpen) return;
- return new AskForExtensionModal(plugin, folderPath, openFile, folderNoteType, displayModal, preexistingNote).open();
+ return new AskForExtensionModal(
+ plugin,
+ folderPath,
+ openFile,
+ folderNoteType,
+ displayModal,
+ preexistingNote,
+ ).open();
}
if (plugin.settings.storageLocation === 'parentFolder') {
@@ -61,27 +97,7 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st
}
if (detachedFolder && folderNote?.extension !== extension && folderNote) {
- deleteExcludedFolder(plugin, detachedFolder);
- removeCSSClassFromFileExplorerEL(folderNote?.path, 'is-folder-note', false, plugin);
- const folder = plugin.app.vault.getAbstractFileByPath(folderPath) as TFolder;
- if (!folderNote || folderNote.basename !== fileName) return;
- let count = 1;
- let newName = removeExtension(folderNote.path) + ` (${count}).${folderNote.path.split('.').pop()}`;
- while (count < 100 && plugin.app.vault.getAbstractFileByPath(newName)) {
- count++;
- newName = removeExtension(folderNote.path) + ` (${count}).${folderNote.path.split('.').pop()}`;
- }
- const [excludedFolder, excludedFolderExisted, disabledSync] = await tempDisableSync(plugin, folder);
-
- await plugin.app.fileManager.renameFile(folderNote, newName).then(() => {
- if (!excludedFolder) return;
- if (!excludedFolderExisted) {
- deleteExcludedFolder(plugin, excludedFolder);
- } else if (!disabledSync) {
- excludedFolder.disableSync = false;
- updateExcludedFolder(plugin, excludedFolder, excludedFolder);
- }
- });
+ await handleTurnNoteIntoFolderNote(plugin, folderNote, detachedFolder, folderPath, fileName);
}
if (!extension) {
@@ -89,33 +105,7 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st
}
if (!folderNote) {
- let content = '';
- if (extension !== '.md' && extension) {
- if (plugin.settings.templatePath && folderNoteType.split('.').pop() === plugin.settings.templatePath.split('.').pop()) {
- const templateFile = plugin.app.vault.getAbstractFileByPath(plugin.settings.templatePath);
- if (templateFile instanceof TFile) {
- if (['md', 'canvas', 'txt'].includes(templateFile.extension)) {
- content = await plugin.app.vault.read(templateFile);
- if (extension === '.excalidraw' && !content.includes('==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==')) {
- content = defaultExcalidrawTemplate;
- }
- } else {
- return plugin.app.vault.readBinary(templateFile).then(async (data) => {
- folderNote = await plugin.app.vault.createBinary(path, data);
- if (openFile) {
- await leaf.openFile(folderNote);
- }
- });
- }
- }
- } else if (plugin.settings.folderNoteType === '.excalidraw' || extension === '.excalidraw') {
- content = defaultExcalidrawTemplate;
- } else if (plugin.settings.folderNoteType === '.canvas') {
- content = '{}';
- }
- }
-
- folderNote = await plugin.app.vault.create(path, content);
+ folderNote = await handleCreateFolderNote(plugin, folderNoteType, openFile, leaf, folderNote, path, extension);
} else {
await plugin.app.fileManager.renameFile(folderNote, path);
}
@@ -131,11 +121,13 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st
}
await leaf.openFile(folderNote);
if (plugin.settings.folderNoteType === '.excalidraw' || extension === '.excalidraw') {
- openExcalidrawView(leaf);
+ openExcalidrawView(plugin.app, leaf);
}
}
- const matchingExtension = extension?.split('.').pop() === plugin.settings.templatePath.split('.').pop();
+ const matchingExtension =
+ extension?.split('.').pop() ===
+ plugin.settings.templatePath.split('.').pop();
if (folderNote && matchingExtension && plugin.settings.folderNoteType !== '.excalidraw') {
applyTemplate(plugin, folderNote, leaf, plugin.settings.templatePath);
}
@@ -146,19 +138,159 @@ export async function createFolderNote(plugin: FolderNotesPlugin, folderPath: st
addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin);
}
-export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile, folder: TFolder, folderNote?: TFile | null | TAbstractFile, skipConfirmation?: boolean) {
+function getArgs(
+ plugin: FolderNotesPlugin,
+ folderPath: string,
+ extension?: string,
+ preexistingNote?: TFile,
+): {
+ leaf: WorkspaceLeaf;
+ fileName: string;
+ folderNote: TFile | null | undefined;
+ folderNoteType: string;
+ detachedFolder: ExcludedFolder | undefined;
+ path: string | '';
+} {
+ const leaf = plugin.app.workspace.getLeaf(false);
+ const folderName = getFolderNameFromPathString(folderPath);
+ const fileName = plugin.settings.folderNoteName.replace('{{folder_name}}', folderName);
+ let folderNote = getFolderNote(plugin, folderPath);
+ if (preexistingNote) {
+ folderNote = preexistingNote;
+ }
+ let folderNoteType = extension ?? plugin.settings.folderNoteType;
+ const detachedFolder = getDetachedFolder(plugin, folderPath);
+ let path = '';
+
+ return {
+ leaf,
+ fileName,
+ folderNote,
+ folderNoteType,
+ detachedFolder,
+ path,
+ };
+}
+
+async function handleCreateFolderNote(plugin: FolderNotesPlugin, folderNoteType: string, openFile: boolean, leaf: WorkspaceLeaf, folderNote: TFile | null | undefined, path: string, extension?: string): Promise {
+ let content = '';
+ if (extension !== '.md' && extension) {
+ if (
+ plugin.settings.templatePath &&
+ folderNoteType.split('.').pop() ===
+ plugin.settings.templatePath.split('.').pop()
+ ) {
+ const templateFile = plugin.app.vault.getAbstractFileByPath(
+ plugin.settings.templatePath,
+ );
+ if (templateFile instanceof TFile) {
+ if (['md', 'canvas', 'txt'].includes(templateFile.extension)) {
+ content = await plugin.app.vault.read(templateFile);
+ if (
+ extension === '.excalidraw' &&
+ !content.includes(
+ '==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==',
+ )
+ ) {
+ content = defaultExcalidrawTemplate;
+ }
+ } else {
+ plugin.app.vault.readBinary(templateFile).then(async (data) => {
+ folderNote = await plugin.app.vault.createBinary(path, data);
+ if (openFile) {
+ await leaf.openFile(folderNote);
+ }
+ return folderNote;
+ });
+ }
+ }
+ } else if (
+ plugin.settings.folderNoteType === '.excalidraw' ||
+ extension === '.excalidraw'
+ ) {
+ content = defaultExcalidrawTemplate;
+ } else if (plugin.settings.folderNoteType === '.canvas') {
+ content = '{}';
+ }
+ }
+
+ folderNote = await plugin.app.vault.create(path, content);
+ return folderNote;
+}
+
+async function handleTurnNoteIntoFolderNote(
+ plugin: FolderNotesPlugin,
+ folderNote: TFile,
+ detachedFolder: ExcludedFolder,
+ folderPath: string,
+ fileName: string,
+): Promise {
+ deleteExcludedFolder(plugin, detachedFolder);
+ removeCSSClassFromFileExplorerEL(folderNote?.path, 'is-folder-note', false, plugin);
+ const folder = plugin.app.vault.getAbstractFileByPath(folderPath) as TFolder;
+ if (!folderNote || folderNote.basename !== fileName) return;
+ let count = 1;
+ const baseName = removeExtension(folderNote.path);
+ const ext = folderNote.path.split('.').pop();
+ let newName = `${baseName} (${count}).${ext}`;
+ const MAX_FOLDER_NOTE_RENAME_ATTEMPTS = 100;
+
+ while (
+ count < MAX_FOLDER_NOTE_RENAME_ATTEMPTS &&
+ plugin.app.vault.getAbstractFileByPath(newName)
+ ) {
+ count++;
+ newName = `${baseName} (${count}).${ext}`;
+ }
+ const [
+ excludedFolder,
+ excludedFolderExisted,
+ disabledSync,
+ ] = await tempDisableSync(plugin, folder);
+
+ await plugin.app.fileManager.renameFile(folderNote, newName).then(() => {
+ if (!excludedFolder) return;
+ if (!excludedFolderExisted) {
+ deleteExcludedFolder(plugin, excludedFolder);
+ } else if (!disabledSync) {
+ excludedFolder.disableSync = false;
+ updateExcludedFolder(plugin, excludedFolder, excludedFolder);
+ }
+ });
+}
+
+export async function turnIntoFolderNote(
+ plugin: FolderNotesPlugin,
+ file: TFile,
+ folder: TFolder,
+ folderNote?: TFile | null | TAbstractFile,
+ skipConfirmation?: boolean,
+): Promise {
const { extension } = file;
const detachedExcludedFolder = getDetachedFolder(plugin, folder.path);
if (folderNote) {
- if (plugin.settings.showRenameConfirmation && !skipConfirmation && !detachedExcludedFolder) {
+ if (
+ plugin.settings.showRenameConfirmation &&
+ !skipConfirmation &&
+ !detachedExcludedFolder
+ ) {
return new ExistingFolderNoteModal(plugin.app, plugin, file, folder, folderNote).open();
}
removeCSSClassFromFileExplorerEL(folderNote.path, 'is-folder-note', false, plugin);
- const [excludedFolder, excludedFolderExisted, disabledSync] = await tempDisableSync(plugin, folder);
+ const [
+ excludedFolder,
+ excludedFolderExisted,
+ disabledSync,
+ ] = await tempDisableSync(plugin, folder);
+
+ const CTIME_SLICE_START = 10;
+ const RANDOM_SUFFIX_MAX = 1000;
+ const randomSuffix = Math.floor(Math.random() * RANDOM_SUFFIX_MAX);
+ const ctimeSuffix = file.stat.ctime.toString().slice(CTIME_SLICE_START);
+ const newPath = `${folder.path}/${folder.name} (${ctimeSuffix}${randomSuffix}).${extension}`;
- const newPath = `${folder.path}/${folder.name} (${file.stat.ctime.toString().slice(10) + Math.floor(Math.random() * 1000)}).${extension}`;
plugin.app.fileManager.renameFile(folderNote, newPath).then(() => {
if (!excludedFolder) return;
if (!excludedFolderExisted) {
@@ -195,14 +327,28 @@ export async function turnIntoFolderNote(plugin: FolderNotesPlugin, file: TFile,
setActiveFolder(folder.path, plugin);
}
-export async function tempDisableSync(plugin: FolderNotesPlugin, folder: TFolder): Promise<[excludedFolder: ExcludedFolder | undefined, excludedFolderExisted: boolean, disabledSync: boolean]> {
+export async function tempDisableSync(
+ plugin: FolderNotesPlugin,
+ folder: TFolder,
+): Promise<
+ [
+ excludedFolder: ExcludedFolder | undefined,
+ excludedFolderExisted: boolean,
+ disabledSync: boolean
+ ]
+> {
let excludedFolder = getExcludedFolder(plugin, folder.path, false);
let excludedFolderExisted = true;
let disabledSync = false;
if (!excludedFolder) {
excludedFolderExisted = false;
- excludedFolder = new ExcludedFolder(folder.path, plugin.settings.excludeFolders.length, undefined, plugin);
+ excludedFolder = new ExcludedFolder(
+ folder.path,
+ plugin.settings.excludeFolders.length,
+ undefined,
+ plugin,
+ );
excludedFolder.disableSync = true;
addExcludedFolder(plugin, excludedFolder);
} else if (!excludedFolder.disableSync) {
@@ -214,7 +360,11 @@ export async function tempDisableSync(plugin: FolderNotesPlugin, folder: TFolder
return [excludedFolder, excludedFolderExisted, disabledSync];
}
-export async function openFolderNote(plugin: FolderNotesPlugin, file: TAbstractFile, evt?: MouseEvent) {
+export async function openFolderNote(
+ plugin: FolderNotesPlugin,
+ file: TAbstractFile,
+ evt?: MouseEvent,
+): Promise {
const { path } = file;
const focusExistingTab = plugin.settings.focusExistingTab && plugin.settings.openInNewTab;
const activeFilePath = plugin.app.workspace.getActiveFile()?.path;
@@ -230,7 +380,7 @@ export async function openFolderNote(plugin: FolderNotesPlugin, file: TAbstractF
plugin.app.workspace.iterateAllLeaves((leaf) => {
if (
leaf.getViewState().type === 'markdown' &&
- (leaf.view as import('obsidian').MarkdownView).file?.path === path
+ (leaf.view as MarkdownView).file?.path === path
) {
foundLeaf = leaf;
}
@@ -240,14 +390,19 @@ export async function openFolderNote(plugin: FolderNotesPlugin, file: TAbstractF
if (foundLeaf) {
plugin.app.workspace.setActiveLeaf(foundLeaf, { focus: true });
} else {
- const leaf = plugin.app.workspace.getLeaf(Keymap.isModEvent(evt) || plugin.settings.openInNewTab);
+ const shouldOpenInNewTab = Keymap.isModEvent(evt) || plugin.settings.openInNewTab;
+ const leaf = plugin.app.workspace.getLeaf(shouldOpenInNewTab);
if (file instanceof TFile) {
await leaf.openFile(file);
}
}
}
-export async function deleteFolderNote(plugin: FolderNotesPlugin, file: TFile, displayModal: boolean) {
+export async function deleteFolderNote(
+ plugin: FolderNotesPlugin,
+ file: TFile,
+ displayModal: boolean,
+): Promise {
if (plugin.settings.showDeleteConfirmation && displayModal) {
return new DeleteConfirmationModal(plugin.app, plugin, file).open();
}
@@ -272,7 +427,7 @@ export async function deleteFolderNote(plugin: FolderNotesPlugin, file: TFile, d
}
}
-export function extractFolderName(template: string, changedFileName: string) {
+export function extractFolderName(template: string, changedFileName: string): string | null {
const [prefix, suffix] = template.split('{{folder_name}}');
if (prefix.trim() === '' && suffix.trim() === '') {
return changedFileName;
@@ -288,39 +443,23 @@ export function extractFolderName(template: string, changedFileName: string) {
return null;
}
-export function getFolderNote(plugin: FolderNotesPlugin, folderPath: string, storageLocation?: string, file?: TFile, oldFolderNoteName?: string) {
- if (!folderPath) return null;
- const folder = {
- path: folderPath,
- name: getFolderNameFromPathString(folderPath),
- };
- const folderNoteName = oldFolderNoteName ?? plugin.settings.folderNoteName;
-
- let fileName = folderNoteName.replace('{{folder_name}}', folder.name);
- if (file) {
- fileName = folderNoteName.replace('{{folder_name}}', file.basename);
- }
- if (!fileName) return null;
-
- if ((plugin.settings.storageLocation === 'parentFolder' || storageLocation === 'parentFolder') && storageLocation !== 'insideFolder') {
- folder.path = getFolderPathFromString(folderPath);
- }
-
-
- let path = `${folder.path}/${fileName}`;
- folder.path === '/' ? path = fileName : path = `${folder.path}/${fileName}`;
-
-
- let { folderNoteType } = plugin.settings;
- if (folderNoteType === '.excalidraw') {
- folderNoteType = '.md';
- }
-
- let folderNote = plugin.app.vault.getAbstractFileByPath(path + folderNoteType);
- if (folderNote instanceof TFile && plugin.settings.supportedFileTypes.includes(plugin.settings.folderNoteType.replace('.', ''))) {
+function findFolderNoteFile(
+ plugin: FolderNotesPlugin,
+ path: string,
+ primaryType: string,
+): TFile | null {
+ let folderNote = plugin.app.vault.getAbstractFileByPath(path + primaryType);
+ if (
+ folderNote instanceof TFile &&
+ plugin.settings.supportedFileTypes.includes(primaryType.replace('.', ''))
+ ) {
return folderNote;
}
- const supportedFileTypes = plugin.settings.supportedFileTypes.filter((type) => type !== plugin.settings.folderNoteType.replace('.', ''));
+
+ const supportedFileTypes = plugin.settings.supportedFileTypes.filter(
+ (type) => type !== primaryType.replace('.', ''),
+ );
+
for (let type of supportedFileTypes) {
if (type === 'excalidraw' || type === '.excalidraw') {
type = '.md';
@@ -333,13 +472,40 @@ export function getFolderNote(plugin: FolderNotesPlugin, folderPath: string, sto
return folderNote;
}
}
-
+ return null;
}
-export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile) {
+export function getFolderNote(
+ plugin: FolderNotesPlugin,
+ folderPath: string,
+ storageLocation?: string,
+ file?: TFile,
+ oldFolderNoteName?: string,
+): TFile | null | undefined {
+ const folder = getFolderInfo(folderPath);
+ if (!folder) return null;
+
+ let fileName = resolveFileName(plugin, folder, file, oldFolderNoteName);
+ if (!fileName) return null;
+
+ adjustFolderPathForStorage(folder, folderPath, plugin, storageLocation);
+
+ const path = buildFullPath(folder, fileName);
+ const primaryType = normalizeFolderNoteType(plugin.settings.folderNoteType);
+
+ return findFolderNoteFile(plugin, path, primaryType);
+}
+
+
+export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile): void {
const folder = getFolder(plugin, file);
if (!folder) return;
- const excludedFolder = new ExcludedFolder(folder.path, plugin.settings.excludeFolders.length, undefined, plugin);
+ const excludedFolder = new ExcludedFolder(
+ folder.path,
+ plugin.settings.excludeFolders.length,
+ undefined,
+ plugin,
+ );
excludedFolder.hideInSettings = true;
excludedFolder.disableFolderNote = true;
excludedFolder.disableSync = true;
@@ -351,16 +517,28 @@ export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile) {
}
-export function getFolder(plugin: FolderNotesPlugin, file: TFile, storageLocation?: string) {
+export function getFolder(
+ plugin: FolderNotesPlugin,
+ file: TFile,
+ storageLocation?: string,
+): TFolder | TAbstractFile | null {
if (!file) return null;
let folderName = extractFolderName(plugin.settings.folderNoteName, file.basename);
- if (plugin.settings.folderNoteName === file.basename && plugin.settings.storageLocation === 'insideFolder') {
+ if (
+ plugin.settings.folderNoteName === file.basename &&
+ plugin.settings.storageLocation === 'insideFolder'
+ ) {
folderName = file.parent?.name ?? '';
}
if (!folderName) return null;
let folderPath = getFolderPathFromString(file.path);
let folder: TFolder | TAbstractFile | null = null;
- if ((plugin.settings.storageLocation === 'parentFolder' || storageLocation === 'parentFolder') && storageLocation !== 'insideFolder') {
+
+ if (
+ (plugin.settings.storageLocation === 'parentFolder' ||
+ storageLocation === 'parentFolder') &&
+ storageLocation !== 'insideFolder'
+ ) {
if (folderPath.trim() === '' || folderPath === '/') {
folderPath = folderName;
} else {
@@ -370,11 +548,16 @@ export function getFolder(plugin: FolderNotesPlugin, file: TFile, storageLocatio
} else {
folder = plugin.app.vault.getAbstractFileByPath(folderPath);
}
+
if (!folder) { return null; }
return folder;
}
-export function getFolderNoteFolder(plugin: FolderNotesPlugin, folderNote: TFile | string, fileName: string) {
+export function getFolderNoteFolder(
+ plugin: FolderNotesPlugin,
+ folderNote: TFile | string,
+ fileName: string,
+): TFolder | TAbstractFile | null {
if (!folderNote) return null;
let filePath = '';
if (typeof folderNote === 'string') {
@@ -399,3 +582,46 @@ export function getFolderNoteFolder(plugin: FolderNotesPlugin, folderNote: TFile
if (!folder) { return null; }
return folder;
}
+
+function getFolderInfo(folderPath: string): { path: string; name: string } | null {
+ if (!folderPath) return null;
+ return {
+ path: folderPath,
+ name: getFolderNameFromPathString(folderPath),
+ };
+}
+
+function resolveFileName(
+ plugin: FolderNotesPlugin,
+ folder: { path: string; name: string },
+ file?: TFile,
+ oldFolderNoteName?: string,
+): string | null {
+ const templateName = oldFolderNoteName ?? plugin.settings.folderNoteName;
+ if (!templateName) return null;
+ const nameSource = file ? file.basename : folder.name;
+ return templateName.replace('{{folder_name}}', nameSource);
+}
+
+function adjustFolderPathForStorage(
+ folder: { path: string; name: string },
+ folderPath: string,
+ plugin: FolderNotesPlugin,
+ storageLocation?: string,
+): void {
+ if (
+ (plugin.settings.storageLocation === 'parentFolder' ||
+ storageLocation === 'parentFolder') &&
+ storageLocation !== 'insideFolder'
+ ) {
+ folder.path = getFolderPathFromString(folderPath);
+ }
+}
+
+function buildFullPath(folder: { path: string }, fileName: string): string {
+ return folder.path === '/' ? fileName : `${folder.path}/${fileName}`;
+}
+
+function normalizeFolderNoteType(type: string): string {
+ return type === '.excalidraw' ? '.md' : type;
+}
diff --git a/src/functions/styleFunctions.ts b/src/functions/styleFunctions.ts
index d65d886..a99c6b8 100644
--- a/src/functions/styleFunctions.ts
+++ b/src/functions/styleFunctions.ts
@@ -1,6 +1,10 @@
import { TFile, TFolder } from 'obsidian';
import type FolderNotesPlugin from '../main';
-import { getDetachedFolder, getExcludedFolder, addExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions';
+import {
+ getDetachedFolder,
+ getExcludedFolder,
+ addExcludedFolder,
+} from 'src/ExcludeFolders/functions/folderFunctions';
import { getFolder, getFolderNote } from 'src/functions/folderNoteFunctions';
import { getFileExplorer } from './utils';
import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder';
@@ -9,7 +13,7 @@ import type FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main';
/**
* @description Refreshes the CSS classes for all folder notes in the file explorer.
*/
-export function refreshAllFolderStyles(forceReload = false, plugin: FolderNotesPlugin) {
+export function refreshAllFolderStyles(forceReload = false, plugin: FolderNotesPlugin): void {
if (plugin.activeFileExplorer === getFileExplorer(plugin) && !forceReload) { return; }
plugin.activeFileExplorer = getFileExplorer(plugin);
plugin.app.vault.getAllLoadedFiles().forEach(async (file) => {
@@ -22,7 +26,10 @@ export function refreshAllFolderStyles(forceReload = false, plugin: FolderNotesP
/**
* @description Updates the CSS classes for a specific folder in the file explorer.
*/
-export async function updateCSSClassesForFolder(folderPath: string, plugin: FolderNotesPlugin) {
+export async function updateCSSClassesForFolder(
+ folderPath: string,
+ plugin: FolderNotesPlugin,
+): Promise {
const folder = plugin.app.vault.getAbstractFileByPath(folderPath);
if (!folder || !(folder instanceof TFolder)) { return; }
@@ -64,7 +71,10 @@ export async function updateCSSClassesForFolder(folderPath: string, plugin: Fold
/**
* @description Updates the CSS classes for a folder note file in the file explorer and then also updates the folder it belongs to.
*/
-export async function updateCSSClassesForFolderNote(filePath: string, plugin: FolderNotesPlugin) {
+export async function updateCSSClassesForFolderNote(
+ filePath: string,
+ plugin: FolderNotesPlugin,
+): Promise {
const file = plugin.app.vault.getAbstractFileByPath(filePath);
if (!file || !(file instanceof TFile)) { return; }
@@ -74,17 +84,25 @@ export async function updateCSSClassesForFolderNote(filePath: string, plugin: Fo
updateCSSClassesForFolder(folder.path, plugin);
}
-export function markFolderAndNoteWithClasses(file: TFile, folder: TFolder, plugin: FolderNotesPlugin) {
+export function markFolderAndNoteWithClasses(
+ file: TFile,
+ folder: TFolder,
+ plugin: FolderNotesPlugin,
+): void {
markFileAsFolderNote(file, plugin);
markFolderWithFolderNoteClasses(folder, plugin);
}
-export function clearFolderAndNoteClasses(folder: TFolder, file: TFile, plugin: FolderNotesPlugin) {
+export function clearFolderAndNoteClasses(
+ folder: TFolder,
+ file: TFile,
+ plugin: FolderNotesPlugin,
+): void {
unmarkFileAsFolderNote(file, plugin);
clearFolderNoteClassesFromFolder(folder, plugin);
}
-export function markFolderWithFolderNoteClasses(folder: TFolder, plugin: FolderNotesPlugin) {
+export function markFolderWithFolderNoteClasses(folder: TFolder, plugin: FolderNotesPlugin): void {
addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin);
if (plugin.isEmptyFolderNoteFolder(folder) && getFolderNote(plugin, folder.path)) {
addCSSClassToFileExplorerEl(folder.path, 'only-has-folder-note', true, plugin);
@@ -93,20 +111,20 @@ export function markFolderWithFolderNoteClasses(folder: TFolder, plugin: FolderN
}
}
-export function markFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin) {
+export function markFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin): void {
addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin);
}
-export function unmarkFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin) {
+export function unmarkFileAsFolderNote(file: TFile, plugin: FolderNotesPlugin): void {
removeCSSClassFromFileExplorerEL(file.path, 'is-folder-note', false, plugin);
}
-export function unmarkFolderAsFolderNote(folder: TFolder, plugin: FolderNotesPlugin) {
+export function unmarkFolderAsFolderNote(folder: TFolder, plugin: FolderNotesPlugin): void {
removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin);
}
-export function clearFolderNoteClassesFromFolder(folder: TFolder, plugin: FolderNotesPlugin) {
+export function clearFolderNoteClassesFromFolder(folder: TFolder, plugin: FolderNotesPlugin): void {
removeCSSClassFromFileExplorerEL(folder.path, 'has-folder-note', false, plugin);
removeCSSClassFromFileExplorerEL(folder.path, 'only-has-folder-note', true, plugin);
}
@@ -115,11 +133,21 @@ export function clearFolderNoteClassesFromFolder(folder: TFolder, plugin: Folder
* @param path Can be a folder or file path
* @returns nothing
*/
-export async function addCSSClassToFileExplorerEl(path: string, cssClass: string, parent = false, plugin: FolderNotesPlugin, waitForCreate = false, count = 0) {
+export async function addCSSClassToFileExplorerEl(
+ path: string,
+ cssClass: string,
+ parent = false,
+ plugin: FolderNotesPlugin,
+ waitForCreate = false,
+ count = 0,
+): Promise {
const fileExplorerItem = getFileExplorerElement(path, plugin);
+ const MAX_RETRIES = 5;
+ const RETRY_DELAY = 500;
+
if (!fileExplorerItem) {
- if (waitForCreate && count < 5) {
- await new Promise((r) => setTimeout(r, 500));
+ if (waitForCreate && count < MAX_RETRIES) {
+ await new Promise((r) => setTimeout(r, RETRY_DELAY));
addCSSClassToFileExplorerEl(path, cssClass, parent, plugin, waitForCreate, count + 1);
return;
}
@@ -143,7 +171,12 @@ export async function addCSSClassToFileExplorerEl(path: string, cssClass: string
* @param cssClass The CSS class to remove from the file explorer element
* @returns nothing
*/
-export function removeCSSClassFromFileExplorerEL(path: string | undefined, cssClass: string, parent: boolean, plugin: FolderNotesPlugin) {
+export function removeCSSClassFromFileExplorerEL(
+ path: string | undefined,
+ cssClass: string,
+ parent: boolean,
+ plugin: FolderNotesPlugin,
+): void {
if (!path) return;
const fileExplorerItem = getFileExplorerElement(path, plugin);
document.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => {
@@ -161,15 +194,23 @@ export function removeCSSClassFromFileExplorerEL(path: string | undefined, cssCl
}
-export function getFileExplorerElement(path: string, plugin: FolderNotesPlugin | FolderOverviewPlugin): HTMLElement | null {
+export function getFileExplorerElement(
+ path: string,
+ plugin: FolderNotesPlugin | FolderOverviewPlugin,
+): HTMLElement | null {
const fileExplorer = getFileExplorer(plugin);
if (!fileExplorer?.view?.fileItems) { return null; }
const fileExplorerItem = fileExplorer.view.fileItems?.[path];
return fileExplorerItem?.selfEl ?? fileExplorerItem?.titleEl ?? null;
}
-export function showFolderNoteInFileExplorer(path: string, plugin: FolderNotesPlugin) {
- const excludedFolder = new ExcludedFolder(path, plugin.settings.excludeFolders.length, undefined, plugin);
+export function showFolderNoteInFileExplorer(path: string, plugin: FolderNotesPlugin): void {
+ const excludedFolder = new ExcludedFolder(
+ path,
+ plugin.settings.excludeFolders.length,
+ undefined,
+ plugin,
+ );
excludedFolder.subFolders = false;
excludedFolder.disableSync = false;
excludedFolder.disableAutoCreate = false;
@@ -183,7 +224,7 @@ export function showFolderNoteInFileExplorer(path: string, plugin: FolderNotesPl
updateCSSClassesForFolder(path, plugin);
}
-export function hideFolderNoteInFileExplorer(folderPath: string, plugin: FolderNotesPlugin) {
+export function hideFolderNoteInFileExplorer(folderPath: string, plugin: FolderNotesPlugin): void {
plugin.settings.excludeFolders = plugin.settings.excludeFolders.filter(
(folder) => (folder.path !== folderPath) && folder.showFolderNote);
plugin.saveSettings(false);
@@ -191,7 +232,7 @@ export function hideFolderNoteInFileExplorer(folderPath: string, plugin: FolderN
updateCSSClassesForFolder(folderPath, plugin);
}
-export function setActiveFolder(folderPath: string, plugin: FolderNotesPlugin) {
+export function setActiveFolder(folderPath: string, plugin: FolderNotesPlugin): void {
const fileExplorerItem = getFileExplorerElement(folderPath, plugin);
if (fileExplorerItem) {
fileExplorerItem.addClass('fn-is-active');
@@ -199,7 +240,7 @@ export function setActiveFolder(folderPath: string, plugin: FolderNotesPlugin) {
}
}
-export function removeActiveFolder(plugin: FolderNotesPlugin) {
+export function removeActiveFolder(plugin: FolderNotesPlugin): void {
if (plugin.activeFolderDom) {
plugin.activeFolderDom.removeClass('fn-is-active');
plugin.activeFolderDom?.removeClass('has-focus');
diff --git a/src/functions/utils.ts b/src/functions/utils.ts
index 468c5de..ec9f2ff 100644
--- a/src/functions/utils.ts
+++ b/src/functions/utils.ts
@@ -2,7 +2,7 @@ import { TFolder, TFile, View } from 'obsidian';
import type { FileExplorerWorkspaceLeaf, FileExplorerView } from 'src/globals';
import { getFolderNote } from './folderNoteFunctions';
import type FolderNotesPlugin from 'src/main';
-import type { FileExplorerLeaf } from 'obsidian-typings';
+import type { FileExplorerLeaf, FileTreeItem, TreeNode } from 'obsidian-typings';
import type FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main';
export function getFileNameFromPathString(path: string): string {
@@ -10,11 +10,12 @@ export function getFileNameFromPathString(path: string): string {
}
export function getFolderNameFromPathString(path: string): string {
+ const PARENT_FOLDER_INDEX = -2;
+ const LAST_FOLDER_INDEX = -1;
if (path.endsWith('.md') || path.endsWith('.canvas')) {
- return path.split('/').slice(-2)[0];
+ return path.split('/').slice(PARENT_FOLDER_INDEX)[0];
}
- return path.split('/').slice(-1)[0];
-
+ return path.split('/').slice(LAST_FOLDER_INDEX)[0];
}
export function removeExtension(name: string): string {
@@ -39,16 +40,20 @@ export function getParentFolderPath(path: string): string {
return this.getFolderPathFromString(this.getFolderPathFromString(path));
}
-export function getFileExplorer(plugin: FolderNotesPlugin | FolderOverviewPlugin) {
- return plugin.app.workspace.getLeavesOfType('file-explorer')[0] as any as FileExplorerWorkspaceLeaf;
+export function getFileExplorer(
+ plugin: FolderNotesPlugin | FolderOverviewPlugin,
+): FileExplorerWorkspaceLeaf {
+ // eslint-disable-next-line max-len
+ const leaf = plugin.app.workspace.getLeavesOfType('file-explorer')[0] as unknown as FileExplorerWorkspaceLeaf;
+ return leaf;
}
-export function getFileExplorerView(plugin: FolderNotesPlugin) {
+export function getFileExplorerView(plugin: FolderNotesPlugin): FileExplorerView {
return getFileExplorer(plugin).view;
}
-export function getFocusedItem(plugin: FolderNotesPlugin) {
- const fileExplorer = getFileExplorer(plugin) as any as FileExplorerLeaf;
+export function getFocusedItem(plugin: FolderNotesPlugin): TreeNode | null {
+ const fileExplorer = getFileExplorer(plugin) as unknown as FileExplorerLeaf;
const { focusedItem } = fileExplorer.view.tree;
return focusedItem;
}
diff --git a/src/globals.d.ts b/src/globals.d.ts
index 1229822..53fff77 100644
--- a/src/globals.d.ts
+++ b/src/globals.d.ts
@@ -40,7 +40,6 @@ interface FileItem {
el: HTMLDivElement;
file: TFile;
fileExplorer: FileExplorerView;
- info: any;
selfEl: HTMLDivElement;
innerEl: HTMLDivElement;
}
@@ -48,7 +47,6 @@ interface FileItem {
interface FolderItem {
el: HTMLDivElement;
fileExplorer: FileExplorerView;
- info: any;
selfEl: HTMLDivElement;
innerEl: HTMLDivElement;
file: TFolder;
diff --git a/src/main.ts b/src/main.ts
index 6f18134..ee94694 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,13 +1,23 @@
-
-import type { TAbstractFile, MarkdownPostProcessorContext, WorkspaceLeaf } from 'obsidian';
-import { Plugin, TFile, TFolder, parseYaml, Notice, Keymap, requireApiVersion, Platform, debounce } from 'obsidian';
-import type { FolderNotesSettings } from './settings/SettingsTab';
-import { DEFAULT_SETTINGS, SettingsTab } from './settings/SettingsTab';
+import {
+ type TAbstractFile,
+ type MarkdownPostProcessorContext,
+ type WorkspaceLeaf,
+ Plugin, TFile, TFolder,
+ parseYaml, Notice, Keymap,
+ requireApiVersion, Platform, debounce,
+} from 'obsidian';
+import {
+ type FolderNotesSettings, DEFAULT_SETTINGS, SettingsTab,
+} from './settings/SettingsTab';
import { Commands } from './Commands';
import type { FileExplorerWorkspaceLeaf } from './globals';
-import { registerFileExplorerObserver, unregisterFileExplorerObserver } from './events/MutationObserver';
+import {
+ registerFileExplorerObserver, unregisterFileExplorerObserver,
+} from './events/MutationObserver';
import { handleRename } from './events/handleRename';
-import { getFolderNote, getFolder, openFolderNote, createFolderNote } from './functions/folderNoteFunctions';
+import {
+ getFolderNote, getFolder, openFolderNote, createFolderNote,
+} from './functions/folderNoteFunctions';
import { handleCreate } from './events/handleCreate';
import { FrontMatterTitlePluginHandler } from './events/FrontMatterTitle';
import { FolderOverviewSettings } from './obsidian-folder-overview/src/modals/Settings';
@@ -15,10 +25,12 @@ import { FolderOverview } from './obsidian-folder-overview/src/FolderOverview';
import { TabManager } from './events/TabManager';
import './functions/ListComponent';
import { handleDelete } from './events/handleDelete';
-import { addCSSClassToFileExplorerEl, getFileExplorerElement, removeCSSClassFromFileExplorerEL, refreshAllFolderStyles, setActiveFolder, removeActiveFolder } from './functions/styleFunctions';
+import {
+ addCSSClassToFileExplorerEl, getFileExplorerElement, removeCSSClassFromFileExplorerEL,
+ refreshAllFolderStyles, setActiveFolder, removeActiveFolder,
+} from './functions/styleFunctions';
import { getExcludedFolder } from './ExcludeFolders/functions/folderFunctions';
import type { FileExplorerView, InternalPlugin } from 'obsidian-typings';
-// import { getFocusedItem } from './functions/utils';
import { FOLDER_OVERVIEW_VIEW, FolderOverviewView } from './obsidian-folder-overview/src/view';
import { registerOverviewCommands } from './obsidian-folder-overview/src/Commands';
import { updateOverviewView, updateViewDropdown } from './obsidian-folder-overview/src/main';
@@ -26,7 +38,6 @@ import { FvIndexDB } from './obsidian-folder-overview/src/utils/IndexDB';
import { updateAllOverviews } from './obsidian-folder-overview/src/utils/functions';
export default class FolderNotesPlugin extends Plugin {
- observer: MutationObserver;
settings: FolderNotesSettings;
settingsTab: SettingsTab;
activeFolderDom: HTMLElement | null;
@@ -43,7 +54,7 @@ export default class FolderNotesPlugin extends Plugin {
private fileExplorerPlugin!: InternalPlugin;
private fileExplorerView!: FileExplorerView;
- async onload() {
+ async onload(): Promise {
console.log('loading folder notes plugin');
await this.loadSettings();
this.settingsTab = new SettingsTab(this.app, this);
@@ -54,17 +65,29 @@ export default class FolderNotesPlugin extends Plugin {
// Add CSS Classes
document.body.classList.add('folder-notes-plugin');
if (this.settings.hideFolderNote) { document.body.classList.add('hide-folder-note'); }
- if (this.settings.hideCollapsingIconForEmptyFolders) { document.body.classList.add('fn-hide-empty-collapse-icon'); }
+ if (this.settings.hideCollapsingIconForEmptyFolders) {
+ document.body.classList.add('fn-hide-empty-collapse-icon');
+ }
if (this.settings.underlineFolder) { document.body.classList.add('folder-note-underline'); }
if (this.settings.boldName) { document.body.classList.add('folder-note-bold'); }
if (this.settings.cursiveName) { document.body.classList.add('folder-note-cursive'); }
if (this.settings.boldNameInPath) { document.body.classList.add('folder-note-bold-path'); }
- if (this.settings.cursiveNameInPath) { document.body.classList.add('folder-note-cursive-path'); }
- if (this.settings.underlineFolderInPath) { document.body.classList.add('folder-note-underline-path'); }
- if (this.settings.stopWhitespaceCollapsing) { document.body.classList.add('fn-whitespace-stop-collapsing'); }
- if (this.settings.hideCollapsingIcon) { document.body.classList.add('fn-hide-collapse-icon'); }
- if (!this.settings.highlightFolder) { document.body.classList.add('disable-folder-highlight'); }
- // document.body.classList.add('fv-hide-link-list');
+ if (this.settings.cursiveNameInPath) {
+ document.body.classList.add('folder-note-cursive-path');
+ }
+ if (this.settings.underlineFolderInPath) {
+ document.body.classList.add('folder-note-underline-path');
+ }
+ if (this.settings.stopWhitespaceCollapsing) {
+ document.body.classList.add('fn-whitespace-stop-collapsing');
+ }
+ if (this.settings.hideCollapsingIcon) {
+ document.body.classList.add('fn-hide-collapse-icon');
+ }
+ if (!this.settings.highlightFolder) {
+ document.body.classList.add('disable-folder-highlight');
+ }
+
if (requireApiVersion('1.7.2')) {
document.body.classList.add('version-1-7-2');
}
@@ -74,23 +97,11 @@ export default class FolderNotesPlugin extends Plugin {
this.app.workspace.onLayoutReady(this.onLayoutReady.bind(this));
- // this.observer.observe(document.body, {
- // childList: true,
- // subtree: true,
- // });
-
if (!this.settings.persistentSettingsTab.afterRestart) {
this.settings.settingsTab = 'general';
}
this.registerDomEvent(window, 'keydown', (event: KeyboardEvent) => {
- // Was a bit too buggy
- // if (event.key === 'Enter') {
- // const folderNote = getFolderNote(this, getFocusedItem(this)?.file?.path || '');
- // if (!folderNote) return;
- // openFolderNote(this, folderNote);
- // }
-
const { hoveredElement } = this;
if (this.hoverLinkTriggered) return;
if (!hoveredElement) return;
@@ -143,12 +154,15 @@ export default class FolderNotesPlugin extends Plugin {
this.handleVaultChange();
}));
- this.registerMarkdownCodeBlockProcessor('folder-overview', (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
- this.handleOverviewBlock(source, el, ctx);
- });
+ this.registerMarkdownCodeBlockProcessor(
+ 'folder-overview',
+ (source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
+ this.handleOverviewBlock(source, el, ctx);
+ },
+ );
}
- onLayoutReady() {
+ onLayoutReady(): void {
if (!this._loaded) {
return;
}
@@ -175,8 +189,9 @@ export default class FolderNotesPlugin extends Plugin {
const fileExplorerPlugin = this.app.internalPlugins.getEnabledPluginById('file-explorer');
if (fileExplorerPlugin) {
- const originalRevealInFolder = fileExplorerPlugin.revealInFolder.bind(fileExplorerPlugin);
- fileExplorerPlugin.revealInFolder = (file: TAbstractFile) => {
+ const originalRevealInFolder = fileExplorerPlugin.revealInFolder
+ .bind(fileExplorerPlugin);
+ fileExplorerPlugin.revealInFolder = (file: TAbstractFile): void => {
if (file instanceof TFile) {
const folder = getFolder(this, file);
if (folder instanceof TFolder) {
@@ -186,9 +201,10 @@ export default class FolderNotesPlugin extends Plugin {
}
document.body.classList.remove('hide-folder-note');
originalRevealInFolder.call(fileExplorerPlugin, folder);
+ const FOLDER_REVEAL_DELAY = 100;
setTimeout(() => {
document.body.classList.add('hide-folder-note');
- }, 100);
+ }, FOLDER_REVEAL_DELAY);
return;
}
}
@@ -201,26 +217,29 @@ export default class FolderNotesPlugin extends Plugin {
if (!view) { return; }
- // @ts-ignore
- const editMode = view.editMode ?? view.sourceMode ?? this.app.workspace.activeEditor?.editMode;
+ // @ts-expect-error use internal API
+ const editMode = view.editMode ?? view.sourceMode
+ // @ts-expect-error use internal API
+ ?? this.app.workspace.activeEditor?.editMode;
const plugin = this;
if (!editMode) { return; }
- // @ts-ignore
const clipboardProto = editMode.clipboardManager.constructor.prototype;
const originalHandleDragOver = clipboardProto.handleDragOver;
const originalHandleDrop = clipboardProto.handleDrop;
- clipboardProto.handleDragOver = function (evt: DragEvent, ...args: any[]) {
+ clipboardProto.handleDragOver = function (evt: DragEvent, ...args: unknown[]): void {
const { dragManager } = this.app;
const draggable = dragManager?.draggable;
if (draggable?.file instanceof TFolder) {
const folderNote = getFolderNote(plugin, draggable.file.path);
if (folderNote) {
- dragManager.setAction(window.i18next.t('interface.drag-and-drop.insert-link-here'));
+ dragManager.setAction(
+ window.i18next.t('interface.drag-and-drop.insert-link-here'),
+ );
return;
}
}
@@ -228,7 +247,7 @@ export default class FolderNotesPlugin extends Plugin {
return originalHandleDragOver.call(this, evt, ...args);
};
- clipboardProto.handleDrop = function (evt: DragEvent, ...args: any[]) {
+ clipboardProto.handleDrop = function (evt: DragEvent, ...args: unknown[]): void {
const { dragManager } = this.app;
const draggable = dragManager?.draggable;
@@ -248,57 +267,35 @@ export default class FolderNotesPlugin extends Plugin {
}
}
- handleVaultChange() {
+ handleVaultChange(): void {
if (!this.settings.fvGlobalSettings.autoUpdateLinks) return;
+ const DEBOUNCE_DELAY = 2000;
debounce(() => {
updateAllOverviews(this);
- }, 2000, true)();
+ }, DEBOUNCE_DELAY, true)();
}
- handleFileExplorerClick(evt: MouseEvent) {
+ handleFileExplorerClick(evt: MouseEvent): void {
const target = evt.target as HTMLElement;
if (evt.shiftKey) return;
+ if (this.isMobileClickDisabled()) return;
- if (Platform.isMobile && this.settings.disableOpenFolderNoteOnClick) return;
-
- // Check if the click is on a folder
- const folderTitleEl = target.closest('.nav-folder-title') as HTMLElement;
+ const { folderTitleEl, onlyClickedOnFolderTitle } = this.getFolderTitleInfo(target);
if (!folderTitleEl) return;
+ if (this.shouldIgnoreClickByWhitespaceOrCollapse(target, onlyClickedOnFolderTitle)) return;
- const onlyClickedOnFolderTitle = !!target.closest('.nav-folder-title-content');
- // const folderTitleContentEl = target.closest('.nav-folder-title-content') as HTMLElement;
- // if (folderTitleContentEl) {
- // const rect = folderTitleContentEl.getBoundingClientRect();
- // const clickOffsetX = evt.clientX - rect.left;
- // // Ignore clicks within the first N pixels, e.g., 20px where the icon is displayed
- // if (clickOffsetX < 20) return;
- // }
- if (!this.settings.stopWhitespaceCollapsing && !onlyClickedOnFolderTitle) return;
-
- // Ignore clicks on the collapse icon
- if (target.closest('.collapse-icon')) return;
-
- const folderPath = folderTitleEl.getAttribute('data-path');
+ const folderPath = this.getValidFolderPath(folderTitleEl);
if (!folderPath) return;
- const excludedFolder = getExcludedFolder(this, folderPath, true);
- if (excludedFolder?.disableFolderNote) return;
-
- const usedCtrl = Platform.isMacOS ? evt.metaKey : evt.ctrlKey;
-
+ const usedCtrl = this.isCtrlUsed(evt);
const folderNote = getFolderNote(this, folderPath);
- if (!folderNote && (evt.altKey || Keymap.isModEvent(evt) === 'tab')) {
- if ((this.settings.altKey && evt.altKey) || (usedCtrl && this.settings.ctrlKey)) {
- createFolderNote(this, folderPath, true, undefined, true);
- addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, this);
- removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, this);
- return;
- }
+
+ if (!folderNote && this.shouldCreateNote(evt, usedCtrl)) {
+ this.createNoteAndMark(folderPath);
+ return;
}
if (!(folderNote instanceof TFile)) return;
-
- if (this.settings.openWithCtrl && !usedCtrl) return;
- if (this.settings.openWithAlt && !evt.altKey) return;
+ if (!this.shouldOpenNote(usedCtrl, evt)) return;
if (!this.settings.enableCollapsing || usedCtrl) {
evt.preventDefault();
@@ -308,6 +305,58 @@ export default class FolderNotesPlugin extends Plugin {
openFolderNote(this, folderNote, evt);
}
+ private isMobileClickDisabled(): boolean {
+ return Platform.isMobile && this.settings.disableOpenFolderNoteOnClick;
+ }
+
+ private getFolderTitleInfo(target: HTMLElement): {
+ folderTitleEl: HTMLElement | null;
+ onlyClickedOnFolderTitle: boolean;
+ } {
+ const folderTitleEl = target.closest('.nav-folder-title') as HTMLElement | null;
+ const onlyClickedOnFolderTitle = !!target.closest('.nav-folder-title-content');
+ return { folderTitleEl, onlyClickedOnFolderTitle };
+ }
+
+ private shouldIgnoreClickByWhitespaceOrCollapse(
+ target: HTMLElement,
+ onlyClickedOnFolderTitle: boolean,
+ ): boolean {
+ if (!this.settings.stopWhitespaceCollapsing && !onlyClickedOnFolderTitle) return true;
+ if (target.closest('.collapse-icon')) return true;
+ return false;
+ }
+
+ private getValidFolderPath(folderTitleEl: HTMLElement): string | null {
+ const folderPath = folderTitleEl.getAttribute('data-path');
+ if (!folderPath) return null;
+ const excludedFolder = getExcludedFolder(this, folderPath, true);
+ if (excludedFolder?.disableFolderNote) return null;
+ return folderPath;
+ }
+
+ private isCtrlUsed(evt: MouseEvent): boolean {
+ return Platform.isMacOS ? evt.metaKey : evt.ctrlKey;
+ }
+
+ private shouldCreateNote(evt: MouseEvent, usedCtrl: boolean): boolean {
+ const isTabMod = Keymap.isModEvent(evt) === 'tab';
+ if (!(evt.altKey || isTabMod)) return false;
+ return (this.settings.altKey && evt.altKey) || (usedCtrl && this.settings.ctrlKey);
+ }
+
+ private createNoteAndMark(folderPath: string): void {
+ createFolderNote(this, folderPath, true, undefined, true);
+ addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, this);
+ removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, this);
+ }
+
+ private shouldOpenNote(usedCtrl: boolean, evt: MouseEvent): boolean {
+ if (this.settings.openWithCtrl && !usedCtrl) return false;
+ if (this.settings.openWithAlt && !evt.altKey) return false;
+ return true;
+ }
+
handleOverviewBlock(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext): void {
const observer = new MutationObserver(() => {
const editButton = el.parentElement?.childNodes.item(1);
@@ -317,8 +366,12 @@ export default class FolderNotesPlugin extends Plugin {
e.preventDefault();
e.stopPropagation();
new FolderOverviewSettings(
- this.app, this, parseYaml(source),
- ctx, el, this.settings.defaultOverview,
+ this.app,
+ this,
+ parseYaml(source),
+ ctx,
+ el,
+ this.settings.defaultOverview,
).open();
}, { capture: true });
}
@@ -331,21 +384,35 @@ export default class FolderNotesPlugin extends Plugin {
try {
if (this.app.workspace.layoutReady) {
- const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverview);
+ const { defaultOverview } = this.settings;
+ const folderOverview = new FolderOverview(
+ this,
+ ctx,
+ source,
+ el,
+ defaultOverview,
+ );
folderOverview.create(this, el, ctx);
} else {
this.app.workspace.onLayoutReady(() => {
- const folderOverview = new FolderOverview(this, ctx, source, el, this.settings.defaultOverview);
+ const folderOverview = new FolderOverview(
+ this,
+ ctx,
+ source,
+ el,
+ this.settings.defaultOverview,
+ );
folderOverview.create(this, el, ctx);
});
}
} catch (e) {
+ // eslint-disable-next-line max-len
new Notice('Error creating folder overview (folder notes plugin) - check console for more details');
console.error(e);
}
}
- async activateOverviewView() {
+ async activateOverviewView(): Promise {
const { workspace } = this.app;
let leaf: WorkspaceLeaf | null = null;
@@ -362,13 +429,14 @@ export default class FolderNotesPlugin extends Plugin {
workspace.revealLeaf(leaf);
}
- updateOverviewView = updateOverviewView;
- updateViewDropdown = updateViewDropdown;
+ updateOverviewView: typeof updateOverviewView = updateOverviewView;
+ updateViewDropdown: typeof updateViewDropdown = updateViewDropdown;
isEmptyFolderNoteFolder(folder: TFolder): boolean {
const attachmentFolderPath = this.app.vault.getConfig('attachmentFolderPath') as string;
const cleanAttachmentFolderPath = attachmentFolderPath?.replace('./', '') || '';
- const attachmentsAreInRootFolder = attachmentFolderPath === './' || attachmentFolderPath === '';
+ const attachmentsAreInRootFolder = attachmentFolderPath === './'
+ || attachmentFolderPath === '';
const threshold = this.settings.storageLocation === 'insideFolder' ? 1 : 0;
if (folder.children.length === 0) {
addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this);
@@ -379,10 +447,13 @@ export default class FolderNotesPlugin extends Plugin {
} else if (folder.children.length > threshold) {
if (attachmentsAreInRootFolder) {
return false;
- } else if (this.settings.ignoreAttachmentFolder && this.app.vault.getAbstractFileByPath(`${folder.path}/${cleanAttachmentFolderPath}`)) {
+ } else if (this.settings.ignoreAttachmentFolder
+ && this.app.vault.getAbstractFileByPath(
+ `${folder.path}/${cleanAttachmentFolderPath}`)) {
const folderPath = `${folder.path}/${cleanAttachmentFolderPath}`;
const attachmentFolder = this.app.vault.getAbstractFileByPath(folderPath);
- if (attachmentFolder instanceof TFolder && folder.children.length <= threshold + 1) {
+ if (attachmentFolder instanceof TFolder
+ && folder.children.length <= threshold + 1) {
if (!folder.collapsed) {
getFileExplorerElement(folder.path, this)?.click();
}
@@ -395,13 +466,20 @@ export default class FolderNotesPlugin extends Plugin {
return true;
}
- async changeFolderNameInExplorer(folder: TFolder, newName: string | null | undefined, waitForCreate = false, count = 0) {
+ async changeFolderNameInExplorer(
+ folder: TFolder,
+ newName: string | null | undefined,
+ waitForCreate = false,
+ count = 0,
+ ): Promise {
+ const MAX_RETRY_COUNT = 5;
+ const RETRY_DELAY_MS = 500;
if (!newName) newName = folder.name;
let fileExplorerItem = getFileExplorerElement(folder.path, this);
if (!fileExplorerItem) {
- if (waitForCreate && count < 5) {
- await new Promise((r) => setTimeout(r, 500));
- this.changeFolderNameInExplorer(folder, newName, waitForCreate, count + 1);
+ if (waitForCreate && count < MAX_RETRY_COUNT) {
+ await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
+ void this.changeFolderNameInExplorer(folder, newName, waitForCreate, count + 1);
return;
}
return;
@@ -410,15 +488,19 @@ export default class FolderNotesPlugin extends Plugin {
fileExplorerItem = fileExplorerItem?.querySelector('div.nav-folder-title-content');
if (!fileExplorerItem) { return; }
if (this.settings.frontMatterTitle.explorer && this.settings.frontMatterTitle.enabled) {
- fileExplorerItem.innerText = newName;
- fileExplorerItem.setAttribute('old-name', folder.name);
+ (fileExplorerItem as HTMLElement).innerText = newName;
+ (fileExplorerItem as HTMLElement).setAttribute('old-name', folder.name);
} else {
- fileExplorerItem.innerText = folder.name;
- fileExplorerItem.removeAttribute('old-name');
+ (fileExplorerItem as HTMLElement).innerText = folder.name;
+ (fileExplorerItem as HTMLElement).removeAttribute('old-name');
}
}
- async changeFolderNameInPath(folder: TFolder, newName: string | null | undefined, breadcrumb: HTMLElement) {
+ async changeFolderNameInPath(
+ folder: TFolder,
+ newName: string | null | undefined,
+ breadcrumb: HTMLElement,
+ ): Promise {
if (!newName) newName = folder.name;
breadcrumb.textContent = folder.newName || folder.name;
@@ -429,7 +511,7 @@ export default class FolderNotesPlugin extends Plugin {
/**
* Updates all folder names in the path above the note editor
*/
- updateAllBreadcrumbs(remove?: boolean) {
+ updateAllBreadcrumbs(remove?: boolean): void {
if (!this.settings.frontMatterTitle.path && !remove) { return; }
const viewHeaderItems = document.querySelectorAll('span.view-header-breadcrumb');
const files = this.app.vault.getAllLoadedFiles().filter((file) => file instanceof TFolder);
@@ -449,7 +531,7 @@ export default class FolderNotesPlugin extends Plugin {
});
}
- onunload() {
+ onunload(): void {
unregisterFileExplorerObserver();
document.body.classList.remove('folder-notes-plugin');
document.body.classList.remove('folder-note-underline');
@@ -461,7 +543,7 @@ export default class FolderNotesPlugin extends Plugin {
}
}
- async loadSettings() {
+ async loadSettings(): Promise {
const data = await this.loadData();
if (data) {
if (data.allowWhitespaceCollapsing === true) {
@@ -479,12 +561,14 @@ export default class FolderNotesPlugin extends Plugin {
}
if (!data) { return; }
- const overview = (data as any).defaultOverview;
+ const overview = data.defaultOverview;
if (!overview) { return; }
- this.settings.defaultOverview = Object.assign({}, DEFAULT_SETTINGS.defaultOverview, overview);
+ this.settings.defaultOverview = Object.assign(
+ {}, DEFAULT_SETTINGS.defaultOverview, overview,
+ );
}
- async saveSettings(reloadStyles?: boolean) {
+ async saveSettings(reloadStyles?: boolean): Promise {
await this.saveData(this.settings);
// cleanup any css if we need too
if ((!this.settingsOpened || reloadStyles === true) && reloadStyles !== false) {
diff --git a/src/modals/AddSupportedFileType.ts b/src/modals/AddSupportedFileType.ts
index c4e802d..787f4e1 100644
--- a/src/modals/AddSupportedFileType.ts
+++ b/src/modals/AddSupportedFileType.ts
@@ -1,5 +1,4 @@
-import type { App, SettingTab } from 'obsidian';
-import { Modal, Setting, Notice } from 'obsidian';
+import { Modal, Setting, Notice, type App, type SettingTab } from 'obsidian';
import type FolderNotesPlugin from '../main';
import type { ListComponent } from 'src/functions/ListComponent';
@@ -17,7 +16,8 @@ export default class AddSupportedFileModal extends Modal {
this.list = list;
this.settingsTab = settingsTab;
}
- onOpen() {
+
+ onOpen(): void {
const { contentEl } = this;
// close when user presses enter
contentEl.addEventListener('keydown', (e) => {
@@ -38,7 +38,7 @@ export default class AddSupportedFileModal extends Modal {
}),
);
}
- async onClose() {
+ async onClose(): Promise {
if (this.name.toLocaleLowerCase() === 'markdown') {
this.name = 'md';
}
@@ -47,9 +47,9 @@ export default class AddSupportedFileModal extends Modal {
contentEl.empty();
this.settingsTab.display();
} else if (this.plugin.settings.supportedFileTypes.includes(this.name.toLowerCase())) {
- return new Notice('This extension is already supported');
+ new Notice('This extension is already supported');
+ return;
} else {
- // @ts-ignore
await this.list.addValue(this.name.toLowerCase());
this.settingsTab.display();
this.plugin.saveSettings();
diff --git a/src/modals/AskForExtension.ts b/src/modals/AskForExtension.ts
index 50b5ee8..7acd569 100644
--- a/src/modals/AskForExtension.ts
+++ b/src/modals/AskForExtension.ts
@@ -1,5 +1,4 @@
-import type { TFile } from 'obsidian';
-import { FuzzySuggestModal } from 'obsidian';
+import { FuzzySuggestModal, type TFile } from 'obsidian';
import type FolderNotesPlugin from 'src/main';
import { createFolderNote } from 'src/functions/folderNoteFunctions';
export class AskForExtensionModal extends FuzzySuggestModal {
@@ -9,7 +8,14 @@ export class AskForExtensionModal extends FuzzySuggestModal {
openFile: boolean;
useModal: boolean | undefined;
existingNote: TFile | undefined;
- constructor(plugin: FolderNotesPlugin, folderPath: string, openFile: boolean, extension: string, useModal?: boolean, existingNote?: TFile) {
+ constructor(
+ plugin: FolderNotesPlugin,
+ folderPath: string,
+ openFile: boolean,
+ extension: string,
+ useModal?: boolean,
+ existingNote?: TFile,
+ ) {
super(plugin.app);
this.plugin = plugin;
this.folderPath = folderPath;
@@ -21,17 +27,26 @@ export class AskForExtensionModal extends FuzzySuggestModal {
}
getItems(): string[] {
- return this.plugin.settings.supportedFileTypes.filter((item) => item.toLowerCase() !== '.ask');
+ return this.plugin.settings.supportedFileTypes.filter(
+ (item) => item.toLowerCase() !== '.ask',
+ );
}
getItemText(item: string): string {
return item;
}
- onChooseItem(item: string, evt: MouseEvent | KeyboardEvent) {
+ onChooseItem(item: string, _evt: MouseEvent | KeyboardEvent): void {
this.plugin.askModalCurrentlyOpen = false;
this.extension = '.' + item;
- createFolderNote(this.plugin, this.folderPath, this.openFile, this.extension, this.useModal, this.existingNote);
+ createFolderNote(
+ this.plugin,
+ this.folderPath,
+ this.openFile,
+ this.extension,
+ this.useModal,
+ this.existingNote,
+ );
this.close();
}
}
diff --git a/src/modals/DeleteConfirmation.ts b/src/modals/DeleteConfirmation.ts
index c179ec4..ac857b6 100644
--- a/src/modals/DeleteConfirmation.ts
+++ b/src/modals/DeleteConfirmation.ts
@@ -1,5 +1,4 @@
-import type { App, TFile } from 'obsidian';
-import { Modal, Platform } from 'obsidian';
+import { Modal, Platform, type App, type TFile } from 'obsidian';
import type FolderNotesPlugin from '../main';
import { deleteFolderNote } from 'src/functions/folderNoteFunctions';
export default class DeleteConfirmationModal extends Modal {
@@ -12,21 +11,25 @@ export default class DeleteConfirmationModal extends Modal {
this.app = app;
this.file = file;
}
- onOpen() {
+ onOpen(): void {
const { contentEl, plugin } = this;
const modalTitle = contentEl.createDiv({ cls: 'fn-modal-title' });
const modalContent = contentEl.createDiv({ cls: 'fn-modal-content' });
modalTitle.createEl('h2', { text: 'Delete folder note' });
+ // eslint-disable-next-line max-len
modalContent.createEl('p', { text: `Are you sure you want to delete the folder note '${this.file.name}' ?` });
switch (plugin.settings.deleteFilesAction) {
case 'trash':
modalContent.createEl('p', { text: 'It will be moved to your system trash.' });
break;
case 'obsidianTrash':
+ // eslint-disable-next-line max-len
modalContent.createEl('p', { text: 'It will be moved to your Obsidian trash, which is located in the ".trash" hidden folder in your vault.' });
break;
case 'delete':
- modalContent.createEl('p', { text: 'It will be permanently deleted.' }).setCssStyles({ color: 'red' });
+ modalContent
+ .createEl('p', { text: 'It will be permanently deleted.' })
+ .setCssStyles({ color: 'red' });
break;
}
@@ -47,7 +50,10 @@ export default class DeleteConfirmationModal extends Modal {
plugin.saveSettings();
});
} else {
- const confirmButton = buttonContainer.createEl('button', { text: 'Delete and don\'t ask again', cls: 'mod-destructive' });
+ const confirmButton = buttonContainer.createEl('button', {
+ text: 'Delete and don\'t ask again',
+ cls: 'mod-destructive',
+ });
confirmButton.addEventListener('click', async () => {
plugin.settings.showDeleteConfirmation = false;
plugin.saveSettings();
@@ -56,19 +62,25 @@ export default class DeleteConfirmationModal extends Modal {
});
}
- const deleteButton = buttonContainer.createEl('button', { text: 'Delete', cls: 'mod-warning' });
+ const deleteButton = buttonContainer.createEl('button', {
+ text: 'Delete',
+ cls: 'mod-warning',
+ });
deleteButton.addEventListener('click', async () => {
this.close();
deleteFolderNote(plugin, this.file, false);
});
deleteButton.focus();
- const cancelButton = buttonContainer.createEl('button', { text: 'Cancel', cls: 'mod-cancel' });
+ const cancelButton = buttonContainer.createEl('button', {
+ text: 'Cancel',
+ cls: 'mod-cancel',
+ });
cancelButton.addEventListener('click', async () => {
this.close();
});
}
- onClose() {
+ onClose(): void {
const { contentEl } = this;
contentEl.empty();
}
diff --git a/src/modals/ExistingNote.ts b/src/modals/ExistingNote.ts
index d3c680c..95ac4da 100644
--- a/src/modals/ExistingNote.ts
+++ b/src/modals/ExistingNote.ts
@@ -1,5 +1,12 @@
-import type { App, TFile, TFolder, TAbstractFile } from 'obsidian';
-import { Modal, Setting, Platform } from 'obsidian';
+import {
+ Modal,
+ Setting,
+ Platform,
+ type App,
+ type TFile,
+ type TFolder,
+ type TAbstractFile,
+} from 'obsidian';
import type FolderNotesPlugin from '../main';
import { turnIntoFolderNote } from 'src/functions/folderNoteFunctions';
export default class ExistingFolderNoteModal extends Modal {
@@ -8,7 +15,13 @@ export default class ExistingFolderNoteModal extends Modal {
file: TFile;
folder: TFolder;
folderNote: TAbstractFile;
- constructor(app: App, plugin: FolderNotesPlugin, file: TFile, folder: TFolder, folderNote: TAbstractFile) {
+ constructor(
+ app: App,
+ plugin: FolderNotesPlugin,
+ file: TFile,
+ folder: TFolder,
+ folderNote: TAbstractFile,
+ ) {
super(app);
this.plugin = plugin;
this.app = app;
@@ -16,18 +29,22 @@ export default class ExistingFolderNoteModal extends Modal {
this.folder = folder;
this.folderNote = folderNote;
}
- onOpen() {
+ onOpen(): void {
const { contentEl } = this;
contentEl.createEl('h2', { text: 'A folder note for this folder already exists' });
const setting = new Setting(contentEl);
+ // eslint-disable-next-line max-len
setting.infoEl.createEl('p', { text: 'Are you sure you want to turn the note into a folder note and rename the existing folder note?' });
setting.infoEl.parentElement?.classList.add('fn-delete-confirmation-modal');
// Create a container for the buttons and the checkbox
+ // eslint-disable-next-line max-len
const buttonContainer = setting.infoEl.createEl('div', { cls: 'fn-delete-confirmation-modal-buttons' });
if (Platform.isMobileApp) {
- const confirmButton = buttonContainer.createEl('button', { text: 'Rename and don\'t ask again' });
+ const confirmButton = buttonContainer.createEl('button', {
+ text: 'Rename and don\'t ask again',
+ });
confirmButton.classList.add('mod-warning', 'fn-confirmation-modal-button');
confirmButton.addEventListener('click', async () => {
this.plugin.settings.showRenameConfirmation = false;
@@ -64,7 +81,7 @@ export default class ExistingFolderNoteModal extends Modal {
});
}
- onClose() {
+ onClose(): void {
const { contentEl } = this;
contentEl.empty();
}
diff --git a/src/modals/FolderName.ts b/src/modals/FolderName.ts
index bdef456..b50cf57 100644
--- a/src/modals/FolderName.ts
+++ b/src/modals/FolderName.ts
@@ -1,5 +1,4 @@
-import type { App, TFolder } from 'obsidian';
-import { Modal, Setting } from 'obsidian';
+import { Modal, Setting, type App, type TFolder } from 'obsidian';
import type FolderNotesPlugin from '../main';
export default class FolderNameModal extends Modal {
plugin: FolderNotesPlugin;
@@ -11,7 +10,8 @@ export default class FolderNameModal extends Modal {
this.app = app;
this.folder = folder;
}
- onOpen() {
+
+ onOpen(): void {
const { contentEl } = this;
// close when user presses enter
contentEl.addEventListener('keydown', (e) => {
@@ -27,14 +27,25 @@ export default class FolderNameModal extends Modal {
.setValue(this.folder.name.replace(this.plugin.settings.folderNoteType, ''))
.onChange(async (value) => {
if (value.trim() !== '') {
- if (!this.app.vault.getAbstractFileByPath(this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + value.trim())) {
- this.plugin.app.fileManager.renameFile(this.folder, this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + value.trim());
+ const parentPath = this.folder.path.slice(
+ 0,
+ this.folder.path.lastIndexOf('/') + 1,
+ );
+ const newFolderPath = parentPath + value.trim();
+ if (
+ !this.app.vault.getAbstractFileByPath(newFolderPath)
+ ) {
+ this.plugin.app.fileManager.renameFile(
+ this.folder,
+ newFolderPath,
+ );
}
}
}),
);
}
- onClose() {
+
+ onClose(): void {
const { contentEl } = this;
contentEl.empty();
}
diff --git a/src/modals/NewFolderName.ts b/src/modals/NewFolderName.ts
index 64fa454..8ecb63f 100644
--- a/src/modals/NewFolderName.ts
+++ b/src/modals/NewFolderName.ts
@@ -1,5 +1,4 @@
-import type { App, TFolder } from 'obsidian';
-import { Modal } from 'obsidian';
+import { Modal, type App, type TFolder } from 'obsidian';
import type FolderNotesPlugin from '../main';
export default class NewFolderNameModal extends Modal {
plugin: FolderNotesPlugin;
@@ -11,7 +10,8 @@ export default class NewFolderNameModal extends Modal {
this.app = app;
this.folder = folder;
}
- onOpen() {
+
+ onOpen(): void {
const { contentEl } = this;
contentEl.addEventListener('keydown', (e) => {
@@ -37,7 +37,7 @@ export default class NewFolderNameModal extends Modal {
},
});
- textarea.addEventListener('focus', function() {
+ textarea.addEventListener('focus', function () {
this.select();
});
@@ -50,23 +50,32 @@ export default class NewFolderNameModal extends Modal {
this.close();
});
- const cancelButton = buttonContainer.createEl('button', { text: 'Cancel', cls: 'mod-cancel' });
+ const cancelButton = buttonContainer.createEl('button', {
+ text: 'Cancel',
+ cls: 'mod-cancel',
+ });
cancelButton.addEventListener('click', () => {
this.close();
});
}
- onClose() {
+
+ onClose(): void {
const { contentEl } = this;
contentEl.empty();
}
- saveFolderName() {
+ saveFolderName(): void {
const textarea = this.contentEl.querySelector('textarea');
if (textarea) {
const newName = textarea.value.trim();
if (newName.trim() !== '') {
- if (!this.app.vault.getAbstractFileByPath(this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + newName.trim())) {
- this.plugin.app.fileManager.renameFile(this.folder, this.folder.path.slice(0, this.folder.path.lastIndexOf('/') + 1) + newName.trim());
+ const folderBasePath = this.folder.path.slice(
+ 0,
+ this.folder.path.lastIndexOf('/') + 1,
+ );
+ const newFolderPath = folderBasePath + newName.trim();
+ if (!this.app.vault.getAbstractFileByPath(newFolderPath)) {
+ this.plugin.app.fileManager.renameFile(this.folder, newFolderPath);
}
}
}
diff --git a/src/settings/ExcludedFoldersSettings.ts b/src/settings/ExcludedFoldersSettings.ts
index e4126fb..b8b9345 100644
--- a/src/settings/ExcludedFoldersSettings.ts
+++ b/src/settings/ExcludedFoldersSettings.ts
@@ -1,4 +1,7 @@
-import { addExcludeFolderListItem, addExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions';
+import {
+ addExcludeFolderListItem,
+ addExcludedFolder,
+} from 'src/ExcludeFolders/functions/folderFunctions';
import { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder';
import { addExcludePatternListItem } from 'src/ExcludeFolders/functions/patternFunctions';
import { Setting } from 'obsidian';
@@ -8,7 +11,7 @@ import PatternSettings from 'src/ExcludeFolders/modals/PatternSettings';
import WhitelistedFoldersSettings from 'src/ExcludeFolders/modals/WhitelistedFoldersSettings';
// import ExcludedFoldersWhitelist from 'src/ExcludeFolders/modals/WhitelistModal';
-export async function renderExcludeFolders(settingsTab: SettingsTab) {
+export async function renderExcludeFolders(settingsTab: SettingsTab): Promise {
const containerEl = settingsTab.settingsPage;
const manageExcluded = new Setting(containerEl)
.setHeading()
@@ -25,9 +28,12 @@ export async function renderExcludeFolders(settingsTab: SettingsTab) {
'Use * after the folder name to exclude folders that start with the folder name.',
);
manageExcluded.setDesc(desc3);
+ // eslint-disable-next-line max-len
manageExcluded.infoEl.appendText('The regexes and wildcards are only for the folder name, not the path.');
manageExcluded.infoEl.createEl('br');
+ // eslint-disable-next-line max-len
manageExcluded.infoEl.appendText('If you want to switch to a folder path delete the pattern first.');
+ // eslint-disable-next-line max-len
manageExcluded.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed';
@@ -48,7 +54,11 @@ export async function renderExcludeFolders(settingsTab: SettingsTab) {
cb.setButtonText('Manage');
cb.setCta();
cb.onClick(async () => {
- new ExcludedFolderSettings(settingsTab.app, settingsTab.plugin, settingsTab.plugin.settings.excludeFolderDefaultSettings).open();
+ new ExcludedFolderSettings(
+ settingsTab.app,
+ settingsTab.plugin,
+ settingsTab.plugin.settings.excludeFolderDefaultSettings,
+ ).open();
});
});
@@ -58,7 +68,11 @@ export async function renderExcludeFolders(settingsTab: SettingsTab) {
cb.setButtonText('Manage');
cb.setCta();
cb.onClick(async () => {
- new PatternSettings(settingsTab.app, settingsTab.plugin, settingsTab.plugin.settings.excludePatternDefaultSettings).open();
+ new PatternSettings(
+ settingsTab.app,
+ settingsTab.plugin,
+ settingsTab.plugin.settings.excludePatternDefaultSettings,
+ ).open();
});
});
@@ -71,18 +85,29 @@ export async function renderExcludeFolders(settingsTab: SettingsTab) {
cb.setClass('add-exclude-folder');
cb.setTooltip('Add excluded folder');
cb.onClick(() => {
- const excludedFolder = new ExcludedFolder('', settingsTab.plugin.settings.excludeFolders.length, undefined, settingsTab.plugin);
+ const excludedFolder = new ExcludedFolder(
+ '',
+ settingsTab.plugin.settings.excludeFolders.length,
+ undefined,
+ settingsTab.plugin,
+ );
addExcludeFolderListItem(settingsTab, containerEl, excludedFolder);
addExcludedFolder(settingsTab.plugin, excludedFolder);
settingsTab.display();
});
});
- settingsTab.plugin.settings.excludeFolders.filter((folder) => !folder.hideInSettings).sort((a, b) => a.position - b.position).forEach((excludedFolder) => {
- if (excludedFolder.string?.trim() !== '' && excludedFolder.path?.trim() === '') {
- addExcludePatternListItem(settingsTab, containerEl, excludedFolder);
- } else {
- addExcludeFolderListItem(settingsTab, containerEl, excludedFolder);
- }
- });
+ settingsTab.plugin.settings.excludeFolders
+ .filter((folder) => !folder.hideInSettings)
+ .sort((a, b) => a.position - b.position)
+ .forEach((excludedFolder) => {
+ if (
+ excludedFolder.string?.trim() !== '' &&
+ excludedFolder.path?.trim() === ''
+ ) {
+ addExcludePatternListItem(settingsTab, containerEl, excludedFolder);
+ } else {
+ addExcludeFolderListItem(settingsTab, containerEl, excludedFolder);
+ }
+ });
}
diff --git a/src/settings/FileExplorerSettings.ts b/src/settings/FileExplorerSettings.ts
index 0d79841..c69e620 100644
--- a/src/settings/FileExplorerSettings.ts
+++ b/src/settings/FileExplorerSettings.ts
@@ -1,6 +1,7 @@
+/* eslint-disable max-len */
import { Setting } from 'obsidian';
import type { SettingsTab } from './SettingsTab';
-export async function renderFileExplorer(settingsTab: SettingsTab) {
+export async function renderFileExplorer(settingsTab: SettingsTab): Promise {
const containerEl = settingsTab.settingsPage;
new Setting(containerEl)
@@ -34,7 +35,8 @@ export async function renderFileExplorer(settingsTab: SettingsTab) {
);
setting2.infoEl.appendText('Requires a restart to take effect');
- setting2.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed';
+ const setting2AccentColor = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed';
+ setting2.infoEl.style.color = setting2AccentColor;
new Setting(containerEl)
.setName('Open folder notes by only clicking directly on the folder name')
@@ -65,7 +67,8 @@ export async function renderFileExplorer(settingsTab: SettingsTab) {
}),
);
disableSetting.infoEl.appendText('Requires a restart to take effect');
- disableSetting.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed';
+ const accentColor = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed';
+ disableSetting.infoEl.style.color = accentColor;
new Setting(containerEl)
.setName('Use submenus')
@@ -91,7 +94,15 @@ export async function renderFileExplorer(settingsTab: SettingsTab) {
settingsTab.plugin.settings.frontMatterTitle.explorer = value;
await settingsTab.plugin.saveSettings();
settingsTab.plugin.app.vault.getFiles().forEach((file) => {
- settingsTab.plugin.fmtpHandler?.fmptUpdateFileName({ id: '', result: false, path: file.path, pathOnly: false }, false);
+ settingsTab.plugin.fmtpHandler?.fmptUpdateFileName(
+ {
+ id: '',
+ result: false,
+ path: file.path,
+ pathOnly: false,
+ },
+ false,
+ );
});
}),
);
diff --git a/src/settings/FolderOverviewSettings.ts b/src/settings/FolderOverviewSettings.ts
index 310fb1e..de253e3 100644
--- a/src/settings/FolderOverviewSettings.ts
+++ b/src/settings/FolderOverviewSettings.ts
@@ -2,7 +2,7 @@ import { Setting } from 'obsidian';
import type { SettingsTab } from './SettingsTab';
import { createOverviewSettings } from 'src/obsidian-folder-overview/src/settings';
-export async function renderFolderOverview(settingsTab: SettingsTab) {
+export async function renderFolderOverview(settingsTab: SettingsTab): Promise {
const { plugin } = settingsTab;
const defaultOverviewSettings = plugin.settings.defaultOverview;
const containerEl = settingsTab.settingsPage;
@@ -10,6 +10,7 @@ export async function renderFolderOverview(settingsTab: SettingsTab) {
containerEl.createEl('h3', { text: 'Global settings' });
new Setting(containerEl)
.setName('Auto-update links without opening the overview')
+ // eslint-disable-next-line max-len
.setDesc('If enabled, the links that appear in the graph view will be updated even when you don\'t have the overview open somewhere.')
.addToggle((toggle) =>
toggle
@@ -35,5 +36,15 @@ export async function renderFolderOverview(settingsTab: SettingsTab) {
span.setAttr('style', `color: ${accentColor};`);
pEl.appendChild(span);
- createOverviewSettings(containerEl, defaultOverviewSettings, plugin, plugin.settings.defaultOverview, settingsTab.display, undefined, undefined, undefined, settingsTab);
+ createOverviewSettings(
+ containerEl,
+ defaultOverviewSettings,
+ plugin,
+ plugin.settings.defaultOverview,
+ settingsTab.display,
+ undefined,
+ undefined,
+ undefined,
+ settingsTab,
+ );
}
diff --git a/src/settings/GeneralSettings.ts b/src/settings/GeneralSettings.ts
index 3a15b8e..27b026a 100644
--- a/src/settings/GeneralSettings.ts
+++ b/src/settings/GeneralSettings.ts
@@ -1,3 +1,4 @@
+/* eslint-disable max-len */
import { Setting, Platform } from 'obsidian';
import type { SettingsTab } from './SettingsTab';
import { ListComponent } from '../functions/ListComponent';
@@ -11,7 +12,8 @@ import RenameFolderNotesModal from './modals/RenameFns';
let debounceTimer: NodeJS.Timeout;
-export async function renderGeneral(settingsTab: SettingsTab) {
+// eslint-disable-next-line complexity
+export async function renderGeneral(settingsTab: SettingsTab): Promise {
const containerEl = settingsTab.settingsPage;
const nameSetting = new Setting(containerEl)
.setName('Folder note name template')
@@ -25,6 +27,7 @@ export async function renderGeneral(settingsTab: SettingsTab) {
await settingsTab.plugin.saveSettings();
clearTimeout(debounceTimer);
+ const FOLDER_NOTE_NAME_DEBOUNCE_MS = 2000;
debounceTimer = setTimeout(() => {
if (!value.includes('{{folder_name}}')) {
if (!settingsTab.showFolderNameInTabTitleSetting) {
@@ -37,7 +40,7 @@ export async function renderGeneral(settingsTab: SettingsTab) {
settingsTab.showFolderNameInTabTitleSetting = false;
}
}
- }, 2000);
+ }, FOLDER_NOTE_NAME_DEBOUNCE_MS);
}),
)
.addButton((button) =>
@@ -91,13 +94,24 @@ export async function renderGeneral(settingsTab: SettingsTab) {
}
});
- if (!settingsTab.plugin.settings.supportedFileTypes.includes(settingsTab.plugin.settings.folderNoteType.replace('.', '')) && settingsTab.plugin.settings.folderNoteType !== '.ask') {
+ if (
+ !settingsTab.plugin.settings.supportedFileTypes.includes(
+ settingsTab.plugin.settings.folderNoteType.replace('.', ''),
+ ) &&
+ settingsTab.plugin.settings.folderNoteType !== '.ask'
+ ) {
settingsTab.plugin.settings.folderNoteType = '.md';
settingsTab.plugin.saveSettings();
}
- let defaultType = settingsTab.plugin.settings.folderNoteType.startsWith('.') ? settingsTab.plugin.settings.folderNoteType : '.' + settingsTab.plugin.settings.folderNoteType;
- if (!settingsTab.plugin.settings.supportedFileTypes.includes(defaultType.replace('.', ''))) {
+ let defaultType = settingsTab.plugin.settings.folderNoteType.startsWith('.')
+ ? settingsTab.plugin.settings.folderNoteType
+ : '.' + settingsTab.plugin.settings.folderNoteType;
+ if (
+ !settingsTab.plugin.settings.supportedFileTypes.includes(
+ defaultType.replace('.', ''),
+ )
+ ) {
defaultType = '.ask';
settingsTab.plugin.settings.folderNoteType = defaultType;
}
@@ -118,14 +132,22 @@ export async function renderGeneral(settingsTab: SettingsTab) {
'Specify which file types are allowed as folder notes. Applies to both new and existing folders. Adding many types may affect performance.',
);
setting0.setDesc(desc0);
- const list = new ListComponent(setting0.settingEl, settingsTab.plugin.settings.supportedFileTypes || [], ['md', 'canvas']);
+ const list = new ListComponent(
+ setting0.settingEl,
+ settingsTab.plugin.settings.supportedFileTypes || [],
+ ['md', 'canvas'],
+ );
list.on('update', async (values: string[]) => {
settingsTab.plugin.settings.supportedFileTypes = values;
await settingsTab.plugin.saveSettings();
settingsTab.display();
});
- if (!settingsTab.plugin.settings.supportedFileTypes.includes('md') || !settingsTab.plugin.settings.supportedFileTypes.includes('canvas') || !settingsTab.plugin.settings.supportedFileTypes.includes('excalidraw')) {
+ if (
+ !settingsTab.plugin.settings.supportedFileTypes.includes('md') ||
+ !settingsTab.plugin.settings.supportedFileTypes.includes('canvas') ||
+ !settingsTab.plugin.settings.supportedFileTypes.includes('excalidraw')
+ ) {
setting0.addDropdown((dropdown) => {
const options = [
{ value: 'md', label: 'Markdown' },
@@ -144,7 +166,12 @@ export async function renderGeneral(settingsTab: SettingsTab) {
dropdown.setValue('+');
dropdown.onChange(async (value) => {
if (value === 'custom') {
- return new AddSupportedFileModal(settingsTab.app, settingsTab.plugin, settingsTab, list as ListComponent).open();
+ return new AddSupportedFileModal(
+ settingsTab.app,
+ settingsTab.plugin,
+ settingsTab,
+ list as ListComponent,
+ ).open();
}
await list.addValue(value.toLowerCase());
settingsTab.display();
@@ -157,7 +184,12 @@ export async function renderGeneral(settingsTab: SettingsTab) {
.setButtonText('Add custom file type')
.setCta()
.onClick(async () => {
- new AddSupportedFileModal(settingsTab.app, settingsTab.plugin, settingsTab, list as ListComponent).open();
+ new AddSupportedFileModal(
+ settingsTab.app,
+ settingsTab.plugin,
+ settingsTab,
+ list as ListComponent,
+ ).open();
}),
);
}
@@ -169,7 +201,11 @@ export async function renderGeneral(settingsTab: SettingsTab) {
.addSearch((cb) => {
new TemplateSuggest(cb.inputEl, settingsTab.plugin);
cb.setPlaceholder('Template path');
- cb.setValue(settingsTab.plugin.app.vault.getAbstractFileByPath(settingsTab.plugin.settings.templatePath)?.name.replace('.md', '') || '');
+ const templateFile = settingsTab.plugin.app.vault.getAbstractFileByPath(
+ settingsTab.plugin.settings.templatePath,
+ );
+ const templateName = templateFile?.name.replace('.md', '') || '';
+ cb.setValue(templateName);
cb.onChange(async (value) => {
if (value.trim() === '') {
settingsTab.plugin.settings.templatePath = '';
@@ -464,16 +500,26 @@ export async function renderGeneral(settingsTab: SettingsTab) {
settingsTab.plugin.settings.frontMatterTitle.enabled = value;
await settingsTab.plugin.saveSettings();
if (value) {
- settingsTab.plugin.fmtpHandler = new FrontMatterTitlePluginHandler(settingsTab.plugin);
+ settingsTab.plugin.fmtpHandler =
+ new FrontMatterTitlePluginHandler(settingsTab.plugin);
} else {
if (settingsTab.plugin.fmtpHandler) {
settingsTab.plugin.updateAllBreadcrumbs(true);
}
settingsTab.plugin.app.vault.getFiles().forEach((file) => {
- settingsTab.plugin.fmtpHandler?.fmptUpdateFileName({ id: '', result: false, path: file.path, pathOnly: false }, false);
+ settingsTab.plugin.fmtpHandler?.fmptUpdateFileName(
+ {
+ id: '',
+ result: false,
+ path: file.path,
+ pathOnly: false,
+ },
+ false,
+ );
});
settingsTab.plugin.fmtpHandler?.deleteEvent();
- settingsTab.plugin.fmtpHandler = new FrontMatterTitlePluginHandler(settingsTab.plugin);
+ settingsTab.plugin.fmtpHandler =
+ new FrontMatterTitlePluginHandler(settingsTab.plugin);
}
settingsTab.display();
}),
diff --git a/src/settings/PathSettings.ts b/src/settings/PathSettings.ts
index 3ea8be2..034b1ed 100644
--- a/src/settings/PathSettings.ts
+++ b/src/settings/PathSettings.ts
@@ -1,6 +1,7 @@
+/* eslint-disable max-len */
import { Setting } from 'obsidian';
import type { SettingsTab } from './SettingsTab';
-export async function renderPath(settingsTab: SettingsTab) {
+export async function renderPath(settingsTab: SettingsTab): Promise {
const containerEl = settingsTab.settingsPage;
new Setting(containerEl)
.setName('Open folder note through path')
diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts
index e6723c8..2eeb650 100644
--- a/src/settings/SettingsTab.ts
+++ b/src/settings/SettingsTab.ts
@@ -1,5 +1,7 @@
-import type { App, MarkdownPostProcessorContext } from 'obsidian';
-import { Notice, PluginSettingTab, TFile, TFolder } from 'obsidian';
+import {
+ Notice, PluginSettingTab, TFile,
+ TFolder, type App, type MarkdownPostProcessorContext,
+} from 'obsidian';
import type FolderNotesPlugin from '../main';
import type { ExcludePattern } from 'src/ExcludeFolders/ExcludePattern';
import type { ExcludedFolder } from 'src/ExcludeFolders/ExcludeFolder';
@@ -236,7 +238,8 @@ export class SettingsTab extends PluginSettingTab {
id: 'path',
},
};
- renderSettingsPage(tabId: string) {
+
+ renderSettingsPage(tabId: string): void {
this.settingsPage.empty();
switch (tabId.toLocaleLowerCase()) {
case this.TABS.GENERAL.id:
@@ -257,7 +260,17 @@ export class SettingsTab extends PluginSettingTab {
}
}
- display(contentEl?: HTMLElement, yaml?: defaultOverviewSettings, plugin?: FolderNotesPlugin, defaultSettings?: boolean, display?: CallableFunction, el?: HTMLElement, ctx?: MarkdownPostProcessorContext, file?: TFile | null, settingsTab?: this) {
+ display(
+ contentEl?: HTMLElement,
+ yaml?: defaultOverviewSettings,
+ plugin?: FolderNotesPlugin,
+ defaultSettings?: boolean,
+ display?: CallableFunction,
+ el?: HTMLElement,
+ ctx?: MarkdownPostProcessorContext,
+ file?: TFile | null,
+ settingsTab?: this,
+ ): void {
plugin = this?.plugin ?? plugin;
if (plugin) {
plugin.settingsOpened = true;
@@ -274,13 +287,17 @@ export class SettingsTab extends PluginSettingTab {
for (const [tabId, tabInfo] of Object.entries(settingsTab.TABS)) {
const tabEl = tabBar.createEl('div', { cls: 'fn-settings-tab' });
tabEl.createEl('div', { cls: 'fn-settings-tab-name', text: tabInfo.name });
- if (plugin && plugin.settings.settingsTab.toLocaleLowerCase() === tabId.toLocaleLowerCase()) {
+ if (
+ plugin &&
+ plugin.settings.settingsTab.toLocaleLowerCase() ===
+ tabId.toLocaleLowerCase()
+ ) {
tabEl.addClass('fn-settings-tab-active');
}
tabEl.addEventListener('click', () => {
- // @ts-ignore
- for (const tabEl of tabBar.children) {
- tabEl.removeClass('fn-settings-tab-active');
+ // @ts-expect-error: tabBar.children may not have removeClass method, but we know it works in this context
+ for (const child of tabBar.children) {
+ child.removeClass('fn-settings-tab-active');
if (!plugin) { return; }
plugin.settings.settingsTab = tabId.toLocaleLowerCase();
plugin.saveSettings();
@@ -300,23 +317,31 @@ export class SettingsTab extends PluginSettingTab {
}
}
- renameFolderNotes() {
+ renameFolderNotes(): void {
new Notice('Starting to update folder notes...');
const oldTemplate = this.plugin.settings.oldFolderNoteName ?? '{{folder_name}}';
for (const folder of this.app.vault.getAllLoadedFiles()) {
if (folder instanceof TFolder) {
- const folderNote = getFolderNote(this.plugin, folder.path, undefined, undefined, oldTemplate);
+ const folderNote = getFolderNote(
+ this.plugin,
+ folder.path,
+ undefined,
+ undefined,
+ oldTemplate,
+ );
if (!(folderNote instanceof TFile)) { continue; }
const folderName = extractFolderName(oldTemplate, folderNote.basename) ?? '';
- const newFolderNoteName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', folderName);
+ const newFolderNoteName = this.plugin.settings.folderNoteName
+ .replace('{{folder_name}}', folderName);
let newPath = '';
if (this.plugin.settings.storageLocation === 'parentFolder') {
if (getFolderPathFromString(folder.path).trim() === '/') {
newPath = `${newFolderNoteName}.${folderNote.extension}`;
} else {
+ // eslint-disable-next-line max-len
newPath = `${folderNote.parent?.path}/${newFolderNoteName}.${folderNote.extension}`;
}
} else if (this.plugin.settings.storageLocation === 'insideFolder') {
@@ -332,7 +357,7 @@ export class SettingsTab extends PluginSettingTab {
new Notice('Finished updating folder notes');
}
- switchStorageLocation(oldMethod: string) {
+ switchStorageLocation(oldMethod: string): void {
new Notice('Starting to switch storage location...');
this.app.vault.getAllLoadedFiles().forEach((file) => {
if (file instanceof TFolder) {
diff --git a/src/settings/modals/BackupWarning.ts b/src/settings/modals/BackupWarning.ts
index a0deb27..f6c8694 100644
--- a/src/settings/modals/BackupWarning.ts
+++ b/src/settings/modals/BackupWarning.ts
@@ -5,10 +5,16 @@ export default class BackupWarningModal extends Modal {
plugin: FolderNotesPlugin;
title: string;
desc: string;
- callback: (...args: any[]) => void;
- args: any[];
+ callback: (...args: unknown[]) => void;
+ args: unknown[];
- constructor(plugin: FolderNotesPlugin, title: string, description: string, callback: (...args: any[]) => void, args: any[] = []) {
+ constructor(
+ plugin: FolderNotesPlugin,
+ title: string,
+ description: string,
+ callback: (...args: unknown[]) => void,
+ args: unknown[] = [],
+ ) {
super(plugin.app);
this.plugin = plugin;
this.title = title;
@@ -17,7 +23,7 @@ export default class BackupWarningModal extends Modal {
this.desc = description;
}
- onOpen() {
+ onOpen(): void {
this.modalEl.addClass('fn-backup-warning-modal');
const { contentEl } = this;
@@ -25,8 +31,7 @@ export default class BackupWarningModal extends Modal {
contentEl.createEl('p', { text: this.desc });
- this.insertCustomHtml();
-
+ // eslint-disable-next-line max-len
contentEl.createEl('p', { text: 'Make sure to backup your vault before using this feature.' }).style.color = '#fb464c';
const buttonContainer = contentEl.createDiv({ cls: 'fn-modal-button-container' });
@@ -45,11 +50,7 @@ export default class BackupWarningModal extends Modal {
});
}
- insertCustomHtml(): void {
-
- }
-
- onClose() {
+ onClose(): void {
const { contentEl } = this;
contentEl.empty();
}
diff --git a/src/settings/modals/CreateFnForEveryFolder.ts b/src/settings/modals/CreateFnForEveryFolder.ts
index 0380886..048664e 100644
--- a/src/settings/modals/CreateFnForEveryFolder.ts
+++ b/src/settings/modals/CreateFnForEveryFolder.ts
@@ -1,5 +1,4 @@
-import type { App, ButtonComponent } from 'obsidian';
-import { Modal, Setting, TFolder, Notice } from 'obsidian';
+import { Modal, Setting, TFolder, Notice, type App, type ButtonComponent } from 'obsidian';
import type FolderNotesPlugin from '../../main';
import { createFolderNote, getFolderNote } from 'src/functions/folderNoteFunctions';
import { getTemplatePlugins } from 'src/template';
@@ -15,7 +14,8 @@ export default class ConfirmationModal extends Modal {
this.app = app;
this.extension = plugin.settings.folderNoteType;
}
- onOpen() {
+
+ onOpen(): void {
this.modalEl.addClass('fn-confirmation-modal');
let templateFolderPath: string;
const { templateFolder, templaterPlugin } = getTemplatePlugins(this.plugin.app);
@@ -23,19 +23,29 @@ export default class ConfirmationModal extends Modal {
templateFolderPath = '';
}
if (templaterPlugin) {
- templateFolderPath = templaterPlugin.plugin?.settings?.templates_folder as string;
- } else {
+ templateFolderPath = (
+ templaterPlugin as unknown as {
+ plugin?: { settings?: { templates_folder?: string } }
+ }
+ ).plugin?.settings?.templates_folder as string;
+ } else if (templateFolder) {
templateFolderPath = templateFolder;
}
const { contentEl } = this;
contentEl.createEl('h2', { text: 'Create folder note for every folder' });
const setting = new Setting(contentEl);
+ // eslint-disable-next-line max-len
setting.infoEl.createEl('p', { text: 'Make sure to backup your vault before using this feature.' }).style.color = '#fb464c';
+ // eslint-disable-next-line max-len
setting.infoEl.createEl('p', { text: 'This feature will create a folder note for every folder in your vault.' });
+ // eslint-disable-next-line max-len
setting.infoEl.createEl('p', { text: 'Every folder that already has a folder note will be ignored.' });
setting.infoEl.createEl('p', { text: 'Every excluded folder will be ignored.' });
- if (!this.plugin.settings.templatePath || this.plugin.settings.templatePath?.trim() === '') {
+ if (
+ !this.plugin.settings.templatePath ||
+ this.plugin.settings.templatePath?.trim() === ''
+ ) {
new Setting(contentEl)
.setName('Folder note file extension')
.setDesc('Choose the file extension for the folder notes.')
@@ -56,17 +66,24 @@ export default class ConfirmationModal extends Modal {
cb.setCta();
cb.buttonEl.focus();
cb.onClick(async () => {
- if (this.plugin.settings.templatePath && this.plugin.settings.templatePath.trim() !== '') {
+ if (
+ this.plugin.settings.templatePath &&
+ this.plugin.settings.templatePath.trim() !== ''
+ ) {
this.extension = '.' + this.plugin.settings.templatePath.split('.').pop();
}
if (this.extension === '.ask') {
return new Notice('Please choose a file extension');
}
this.close();
- const folders = this.app.vault.getAllLoadedFiles().filter((file) => file.parent instanceof TFolder);
+ const folders = this.app.vault
+ .getAllLoadedFiles()
+ .filter((file) => file.parent instanceof TFolder);
for (const folder of folders) {
if (folder instanceof TFolder) {
- const excludedFolder = getExcludedFolder(this.plugin, folder.path, true);
+ const excludedFolder = getExcludedFolder(
+ this.plugin, folder.path, true,
+ );
if (excludedFolder) continue;
if (folder.path === templateFolderPath) continue;
const folderNote = getFolderNote(this.plugin, folder.path);
@@ -83,7 +100,8 @@ export default class ConfirmationModal extends Modal {
});
});
}
- onClose() {
+
+ onClose(): void {
const { contentEl } = this;
contentEl.empty();
}
diff --git a/src/settings/modals/RenameFns.ts b/src/settings/modals/RenameFns.ts
index 56cd925..b2183c6 100644
--- a/src/settings/modals/RenameFns.ts
+++ b/src/settings/modals/RenameFns.ts
@@ -3,7 +3,13 @@ import type FolderNotesPlugin from 'src/main';
import { Setting } from 'obsidian';
export default class RenameFolderNotesModal extends BackupWarningModal {
- constructor(plugin: FolderNotesPlugin, title: string, description: string, callback: (...args: any[]) => void, args: any[] = []) {
+ constructor(
+ plugin: FolderNotesPlugin,
+ title: string,
+ description: string,
+ callback: (...args: unknown[]) => void,
+ args: unknown[] = [],
+ ) {
super(plugin, title, description, callback, args);
}
@@ -11,6 +17,7 @@ export default class RenameFolderNotesModal extends BackupWarningModal {
const { contentEl } = this;
new Setting(contentEl)
.setName('Old Folder Note Name')
+ // eslint-disable-next-line max-len
.setDesc('Every folder note that matches this name will be renamed to the new folder note name.')
.addText((text) => text
.setPlaceholder('Enter the old folder note name')
@@ -22,6 +29,7 @@ export default class RenameFolderNotesModal extends BackupWarningModal {
new Setting(contentEl)
.setName('New Folder Note Name')
+ // eslint-disable-next-line max-len
.setDesc('Every folder note that matches the old folder note name will be renamed to this name.')
.addText((text) => text
.setPlaceholder('Enter the new folder note name')
diff --git a/src/suggesters/FolderSuggester.ts b/src/suggesters/FolderSuggester.ts
index e3d07b6..ce4d34e 100644
--- a/src/suggesters/FolderSuggester.ts
+++ b/src/suggesters/FolderSuggester.ts
@@ -1,7 +1,6 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater
-import type { TAbstractFile } from 'obsidian';
-import { TFolder } from 'obsidian';
+import { TFolder, type TAbstractFile } from 'obsidian';
import { TextInputSuggest } from './Suggest';
import type FolderNotesPlugin from '../main';
export enum FileSuggestMode {
@@ -36,13 +35,18 @@ export class FolderSuggest extends TextInputSuggest {
if (this.folder) {
files = this.folder.children;
} else {
- files = this.plugin.app.vault.getAllLoadedFiles().slice(0,100);
+ const MAX_FILE_SUGGESTIONS = 100;
+ files = this.plugin.app.vault.getAllLoadedFiles().slice(0, MAX_FILE_SUGGESTIONS);
}
files.forEach((folder: TAbstractFile) => {
if (
folder instanceof TFolder &&
- folder.path.toLowerCase().contains(lower_input_str) &&
- (!this.plugin.settings.excludeFolders.find((f) => f.path === folder.path) || this.whitelistSuggester)
+ folder.path.toLowerCase().contains(lower_input_str) &&
+ (
+ !this.plugin.settings.excludeFolders.find(
+ (f) => f.path === folder.path,
+ ) || this.whitelistSuggester
+ )
) {
folders.push(folder);
}
diff --git a/src/suggesters/Suggest.ts b/src/suggesters/Suggest.ts
index 4a3fb02..9f2822b 100644
--- a/src/suggesters/Suggest.ts
+++ b/src/suggesters/Suggest.ts
@@ -1,10 +1,7 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater
-
-import type { ISuggestOwner } from 'obsidian';
-import { Scope } from 'obsidian';
-import type { Instance as PopperInstance } from '@popperjs/core';
-import { createPopper } from '@popperjs/core';
+import { Scope, type ISuggestOwner } from 'obsidian';
+import { createPopper, type Instance as PopperInstance } from '@popperjs/core';
import type FolderNotesPlugin from 'src/main';
const wrapAround = (value: number, size: number): number => {
@@ -73,7 +70,7 @@ class Suggest {
this.setSelectedItem(item, false);
}
- setSuggestions(values: T[]) {
+ setSuggestions(values: T[]): void {
this.containerEl.empty();
const suggestionEls: HTMLDivElement[] = [];
@@ -88,14 +85,14 @@ class Suggest {
this.setSelectedItem(0, false);
}
- useSelectedItem(event: MouseEvent | KeyboardEvent) {
+ useSelectedItem(event: MouseEvent | KeyboardEvent): void {
const currentValue = this.values[this.selectedItem];
if (currentValue) {
this.owner.selectSuggestion(currentValue, event);
}
}
- setSelectedItem(selectedIndex: number, scrollIntoView: boolean) {
+ setSelectedItem(selectedIndex: number, scrollIntoView: boolean): void {
const normalizedIndex = wrapAround(
selectedIndex,
this.suggestions.length,
@@ -157,7 +154,7 @@ export abstract class TextInputSuggest implements ISuggestOwner {
if (suggestions.length > 0) {
this.suggest.setSuggestions(suggestions);
- // @ts-ignore
+ // @ts-expect-error App may not exist
this.open(app.dom.appContainerEl, this.inputEl);
} else {
this.close();
@@ -175,7 +172,7 @@ export abstract class TextInputSuggest implements ISuggestOwner {
{
name: 'sameWidth',
enabled: true,
- fn: ({ state, instance }) => {
+ fn: ({ state, instance }): void => {
// Note: positioning needs to be calculated twice -
// first pass - positioning it according to the width of the popper
// second pass - position it with the width bound to the reference element
diff --git a/src/suggesters/TemplateSuggester.ts b/src/suggesters/TemplateSuggester.ts
index 0e26932..60b6f83 100644
--- a/src/suggesters/TemplateSuggester.ts
+++ b/src/suggesters/TemplateSuggester.ts
@@ -1,5 +1,4 @@
-import type { TAbstractFile } from 'obsidian';
-import { TFile, TFolder, Vault, AbstractInputSuggest } from 'obsidian';
+import { TFile, TFolder, Vault, AbstractInputSuggest, type TAbstractFile } from 'obsidian';
import type FolderNotesPlugin from '../main';
import { getTemplatePlugins } from 'src/template';
export enum FileSuggestMode {
@@ -39,12 +38,21 @@ export class TemplateSuggest extends AbstractInputSuggest {
let folder: TFolder | TAbstractFile | null = null;
if (templaterPlugin) {
folder = this.plugin.app.vault.getAbstractFileByPath(
- templaterPlugin.plugin?.settings?.templates_folder as string,
+ (templaterPlugin as unknown as {
+ plugin?: { settings?: { templates_folder?: string } }
+ }).plugin?.settings?.templates_folder as string,
);
if (!(folder instanceof TFolder)) {
- return [{ path: '', name: 'You need to set the Templates folder in the Templater settings first.' } as TFile];
+ return [
+ {
+ path: '',
+ name:
+ // eslint-disable-next-line max-len
+ 'You need to set the Templates folder in the Templater settings first.',
+ } as TFile,
+ ];
}
- } else {
+ } else if (templateFolder) {
folder = this.plugin.app.vault.getAbstractFileByPath(templateFolder) as TFolder;
}
diff --git a/src/template.ts b/src/template.ts
index 09753cb..48240fd 100644
--- a/src/template.ts
+++ b/src/template.ts
@@ -1,12 +1,41 @@
import { TFile, WorkspaceLeaf, type App } from 'obsidian';
import type FolderNotesPlugin from './main';
+interface TemplatesPlugin {
+ enabled: boolean;
+ instance: {
+ options: {
+ folder: string;
+ };
+ insertTemplate: (templateFile: TFile) => Promise;
+ };
+}
+
+interface TemplaterPlugin {
+ settings?: {
+ empty_file_template?: string;
+ template_folder?: string;
+ };
+ templater?: {
+ write_template_to_file: (templateFile: TFile, targetFile: TFile) => Promise;
+ };
+}
+
+interface TemplatePluginReturn {
+ templatesPlugin: TemplatesPlugin | null;
+ templatesEnabled: boolean;
+ templaterPlugin: TemplaterPlugin['templater'] | null;
+ templaterEnabled: boolean;
+ templaterEmptyFileTemplate?: string;
+ templateFolder?: string;
+}
+
export async function applyTemplate(
plugin: FolderNotesPlugin,
file: TFile,
leaf?: WorkspaceLeaf | null,
templatePath?: string,
-) {
+): Promise {
const fileContent = await plugin.app.vault.read(file).catch((err) => {
console.error(`Error reading file ${file.path}:`, err);
});
@@ -25,14 +54,15 @@ export async function applyTemplate(
templaterPlugin,
} = getTemplatePlugins(plugin.app);
const templateContent = await plugin.app.vault.read(templateFile);
+ // eslint-disable-next-line max-len
if (templateContent.includes('==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==')) {
return;
}
// Prioritize Templater if both plugins are enabled
- if (templaterEnabled) {
+ if (templaterEnabled && templaterPlugin) {
return await templaterPlugin.write_template_to_file(templateFile, file);
- } else if (templatesEnabled) {
+ } else if (templatesEnabled && templatesPlugin) {
if (leaf instanceof WorkspaceLeaf) {
await leaf.openFile(file);
}
@@ -47,23 +77,37 @@ export async function applyTemplate(
}
}
-export function getTemplatePlugins(app: App) {
- const templatesPlugin = (app as any).internalPlugins.plugins.templates;
- const templatesEnabled = templatesPlugin.enabled;
- const templaterPlugin = (app as any).plugins.plugins['templater-obsidian'];
- const templaterEnabled = (app as any).plugins.enabledPlugins.has('templater-obsidian');
+export function getTemplatePlugins(app: App): TemplatePluginReturn {
+ const appAsUnknown = app as unknown as {
+ internalPlugins: {
+ plugins: {
+ templates: TemplatesPlugin;
+ };
+ };
+ plugins: {
+ plugins: {
+ 'templater-obsidian': TemplaterPlugin;
+ };
+ enabledPlugins: Set;
+ };
+ };
+
+ const templatesPlugin = appAsUnknown.internalPlugins.plugins.templates;
+ const templatesEnabled = templatesPlugin?.enabled ?? false;
+ const templaterPlugin = appAsUnknown.plugins.plugins['templater-obsidian'];
+ const templaterEnabled = appAsUnknown.plugins.enabledPlugins.has('templater-obsidian');
const templaterEmptyFileTemplate =
templaterPlugin && templaterPlugin.settings?.empty_file_template;
const templateFolder = templatesEnabled
? templatesPlugin.instance.options.folder
- : templaterPlugin?.settings.template_folder;
+ : templaterPlugin?.settings?.template_folder;
return {
- templatesPlugin,
+ templatesPlugin: templatesPlugin || null,
templatesEnabled,
- templaterPlugin: templaterPlugin?.templater,
+ templaterPlugin: templaterPlugin?.templater || null,
templaterEnabled,
templaterEmptyFileTemplate,
templateFolder,