mirror of
https://github.com/lostpaul/obsidian-folder-notes.git
synced 2026-07-22 07:40:24 +00:00
Every remaining file
This commit is contained in:
parent
113cf5f40c
commit
7c31c7cec5
27 changed files with 1333 additions and 619 deletions
496
src/Commands.ts
496
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');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></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<void> {
|
||||
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 = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-rotate-ccw"><path d="M3 2v6h6"></path><path d="M3 13a9 9 0 1 0 3-7.7L3 8"></path></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);
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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<string, unknown>;
|
||||
enabledPlugins: Set<string>;
|
||||
};
|
||||
};
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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<TFile> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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');
|
||||
|
|
|
|||
|
|
@ -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<FileTreeItem> | null {
|
||||
const fileExplorer = getFileExplorer(plugin) as unknown as FileExplorerLeaf;
|
||||
const { focusedItem } = fileExplorer.view.tree;
|
||||
return focusedItem;
|
||||
}
|
||||
|
|
|
|||
2
src/globals.d.ts
vendored
2
src/globals.d.ts
vendored
|
|
@ -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;
|
||||
|
|
|
|||
292
src/main.ts
292
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void>((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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await this.saveData(this.settings);
|
||||
// cleanup any css if we need too
|
||||
if ((!this.settingsOpened || reloadStyles === true) && reloadStyles !== false) {
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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<string> {
|
||||
|
|
@ -9,7 +8,14 @@ export class AskForExtensionModal extends FuzzySuggestModal<string> {
|
|||
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<string> {
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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,
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
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();
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
||||
const containerEl = settingsTab.settingsPage;
|
||||
new Setting(containerEl)
|
||||
.setName('Open folder note through path')
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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<TFolder> {
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<T> {
|
|||
this.setSelectedItem(item, false);
|
||||
}
|
||||
|
||||
setSuggestions(values: T[]) {
|
||||
setSuggestions(values: T[]): void {
|
||||
this.containerEl.empty();
|
||||
const suggestionEls: HTMLDivElement[] = [];
|
||||
|
||||
|
|
@ -88,14 +85,14 @@ class Suggest<T> {
|
|||
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<T> implements ISuggestOwner<T> {
|
|||
|
||||
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<T> implements ISuggestOwner<T> {
|
|||
{
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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<TFile> {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<void>;
|
||||
};
|
||||
}
|
||||
|
||||
interface TemplaterPlugin {
|
||||
settings?: {
|
||||
empty_file_template?: string;
|
||||
template_folder?: string;
|
||||
};
|
||||
templater?: {
|
||||
write_template_to_file: (templateFile: TFile, targetFile: TFile) => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
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<void> {
|
||||
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<string>;
|
||||
};
|
||||
};
|
||||
|
||||
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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue