Support canvas as folder note

This commit is contained in:
Lost Paul 2023-05-18 20:57:18 +02:00
parent 05de0d8c64
commit 00ae688bdb
4 changed files with 165 additions and 84 deletions

View file

@ -34,13 +34,17 @@ export class Commands {
new Notice('Successfully excluded folder from folder notes');
});
});
const path = file.path + '/' + this.plugin.settings.folderNoteName.replace('{{folder_name}}', file.name) + '.md';
const folderNote = this.plugin.app.vault.getAbstractFileByPath(path);
if (folderNote && folderNote instanceof TFile) {
const path = file.path + '/' + this.plugin.settings.folderNoteName.replace('{{folder_name}}', file.name) + this.plugin.settings.folderNoteType;
let folderNote = this.plugin.app.vault.getAbstractFileByPath(path.slice(0, -this.plugin.settings.folderNoteType.length) + '.md') || this.plugin.app.vault.getAbstractFileByPath(path);
if (!folderNote) {
folderNote = this.plugin.app.vault.getAbstractFileByPath(`${file.path}/${file.name}.md`) || this.plugin.app.vault.getAbstractFileByPath(`${file.path}/${file.name}.md`);
}
if (folderNote instanceof TFile) {
menu.addItem((item) => {
item.setTitle('Delete folder note')
.setIcon('trash')
.onClick(() => {
// @ts-ignore
this.plugin.deleteFolderNote(folderNote);
});
});

View file

@ -45,7 +45,7 @@ export default class FolderNotesPlugin extends Plugin {
path += breadcrumb.innerText.trim() + '/';
breadcrumb.setAttribute('data-path', path.slice(0, -1));
const folderNotePath = path + this.settings.folderNoteName.replace('{{folder_name}}', this.getNameFromPathString(path.slice(0, -1))) + '.md';
if (this.app.vault.getAbstractFileByPath(folderNotePath)) {
if (this.app.vault.getAbstractFileByPath(folderNotePath) || this.app.vault.getAbstractFileByPath(folderNotePath.slice(0, -3) + '.canvas')) {
breadcrumb.classList.add('has-folder-note');
}
});
@ -83,8 +83,8 @@ export default class FolderNotesPlugin extends Plugin {
const excludedFolder = this.getExcludedFolderByPath(file.path);
if (excludedFolder?.disableAutoCreate) return;
const path = file.path + '/' + file.name + '.md';
const folderNote = this.app.vault.getAbstractFileByPath(path);
const path = file.path + '/' + file.name + this.settings.folderNoteType;
const folderNote = this.app.vault.getAbstractFileByPath(path.slice(0, -this.settings.folderNoteType.length) + '.md') || this.app.vault.getAbstractFileByPath(path.slice(0, -this.settings.folderNoteType.length) + '.canvas');
if (folderNote) return;
this.createFolderNote(path, true, true);
this.addCSSClassToTitleEL(file.path, 'has-folder-note');
@ -127,9 +127,9 @@ export default class FolderNotesPlugin extends Plugin {
if (!(event.target instanceof HTMLElement)) return;
event.stopImmediatePropagation();
const folder = event.target.parentElement?.getAttribute('data-path');
if (!folder) { return; }
const excludedFolder = this.getExcludedFolderByPath(folder);
const folderPath = event.target.parentElement?.getAttribute('data-path');
if (!folderPath) { return; }
const excludedFolder = this.getExcludedFolderByPath(folderPath);
if (excludedFolder?.disableFolderNote) {
event.target.onclick = null;
event.target.click();
@ -138,15 +138,19 @@ export default class FolderNotesPlugin extends Plugin {
event.target.onclick = null;
event.target.click();
}
const path = folder + '/' + this.settings.folderNoteName.replace('{{folder_name}}', event.target.innerText) + '.md';
if (this.app.vault.getAbstractFileByPath(path)) {
return this.openFolderNote(path, event);
const path = folderPath + '/' + this.settings.folderNoteName.replace('{{folder_name}}', event.target.innerText) + this.settings.folderNoteType;
let folderNote = this.app.vault.getAbstractFileByPath(path.slice(0, -this.settings.folderNoteType.length) + '.md') || this.app.vault.getAbstractFileByPath(path.slice(0, -this.settings.folderNoteType.length) + '.canvas');
if (!folderNote) {
folderNote = this.app.vault.getAbstractFileByPath(`${folderPath}/${event.target.innerText}.md`) || this.app.vault.getAbstractFileByPath(`${folderPath}/${event.target.innerText}.canvas`);
}
if (folderNote) {
return this.openFolderNote(folderNote, event);
} else if (event.altKey || Keymap.isModEvent(event) === 'tab') {
if ((this.settings.altKey && event.altKey) || (this.settings.ctrlKey && Keymap.isModEvent(event) === 'tab')) {
await this.createFolderNote(path, true, true);
this.addCSSClassToTitleEL(folder, 'has-folder-note');
this.removeCSSClassFromEL(folder, 'has-not-folder-note');
this.addCSSClassToTitleEL(folderPath, 'has-folder-note');
this.removeCSSClassFromEL(folderPath, 'has-not-folder-note');
return;
}
}
@ -157,6 +161,7 @@ export default class FolderNotesPlugin extends Plugin {
async handleViewHeaderClick(event: MouseEvent) {
if (!(event.target instanceof HTMLElement)) return;
if (!this.settings.openFolderNoteOnClickInPath) return;
const folder = event.target.getAttribute('data-path');
if (!folder) { return; }
const excludedFolder = this.getExcludedFolderByPath(folder);
@ -168,10 +173,13 @@ export default class FolderNotesPlugin extends Plugin {
event.target.onclick = null;
event.target.click();
}
const path = folder + '/' + event.target.innerText + '.md';
if (this.app.vault.getAbstractFileByPath(path)) {
return this.openFolderNote(path, event);
const path = folder + '/' + event.target.innerText + this.settings.folderNoteType;
let folderNote = this.app.vault.getAbstractFileByPath(path.slice(0, -this.settings.folderNoteType.length) + '.md') || this.app.vault.getAbstractFileByPath(path.slice(0, -this.settings.folderNoteType.length) + '.canvas');
if (!folderNote) {
folderNote = this.app.vault.getAbstractFileByPath(`${folder}/${event.target.innerText}.md`) || this.app.vault.getAbstractFileByPath(`${folder}/${event.target.innerText}.canvas`);
}
if (folderNote) {
return this.openFolderNote(folderNote, event);
} else if (event.altKey || Keymap.isModEvent(event) === 'tab') {
if ((this.settings.altKey && event.altKey) || (this.settings.ctrlKey && Keymap.isModEvent(event) === 'tab')) {
await this.createFolderNote(path, true, true);
@ -206,53 +214,43 @@ export default class FolderNotesPlugin extends Plugin {
excludedFolder.path = folders.join('/');
});
this.saveSettings();
const excludedFolder = this.getExcludedFolderByPath(oldPath);
if (excludedFolder?.disableSync) return;
const excludedFolder = this.getExcludedFolderByPath(file.path);
const newPath = folder.path + '/' + this.settings.folderNoteName.replace('{{folder_name}}', folder.name) + '.md';
const folderNotePath = oldPath + '/' + this.settings.folderNoteName.replace('{{folder_name}}', oldFileName) + '.md';
const note = this.app.vault.getAbstractFileByPath(folderNotePath);
if (!note) return;
if (!(note instanceof TFile)) return;
note.path = folder.path + '/' + this.settings.folderNoteName.replace('{{folder_name}}', oldFileName) + '.md';
let note = this.app.vault.getAbstractFileByPath(folderNotePath) || this.app.vault.getAbstractFileByPath(folderNotePath.slice(0, -3) + '.canvas');
if (!(note instanceof TFile)) {
note = this.app.vault.getAbstractFileByPath(`${oldPath}/${oldFileName}.md`) || this.app.vault.getAbstractFileByPath(`${oldPath}/${oldFileName}.canvas`);
if (!(note instanceof TFile)) { return; }
note.path = folder.path + '/' + oldFileName + this.getExtensionFromPathString(note.path);
} else {
note.path = folder.path + '/' + this.settings.folderNoteName.replace('{{folder_name}}', oldFileName) + this.getExtensionFromPathString(note.path);
}
const newPath = folder.path + '/' + this.settings.folderNoteName.replace('{{folder_name}}', folder.name) + this.getExtensionFromPathString(note.path);
if (excludedFolder?.disableSync && !this.app.vault.getAbstractFileByPath(newPath)) {
return this.removeCSSClassFromEL(file.path, 'has-folder-note');
}
this.app.vault.rename(note, newPath);
}
handleFileRename(file: TFile, oldPath: string) {
const oldFileName = this.getNameFromPathString(oldPath);
const oldFilePath = this.getPathFromString(oldPath);
const fileExtension = this.getExtensionFromPathString(oldPath);
const oldFolder = this.app.vault.getAbstractFileByPath(oldFilePath);
const newFilePath = this.getPathFromString(file.path);
const newFolder = this.app.vault.getAbstractFileByPath(newFilePath);
if (oldFolder) {
const excludedFolder = this.getExcludedFolderByPath(oldFolder.path);
if (excludedFolder?.disableSync) {
this.removeCSSClassFromEL(oldFolder.path, 'has-folder-note');
this.removeCSSClassFromEL(file.path, 'is-folder-note');
this.removeCSSClassFromEL(oldPath, 'is-folder-note');
}
// nothing else for us to do as we have no new folder to action on
if (!newFolder) { return; }
const excludedFolder = this.getExcludedFolderByPath(newFolder?.path || '');
const newExcludedFolder = this.getExcludedFolderByPath(newFolder.path);
// Nothing else for us to do
if (newExcludedFolder?.disableSync || newExcludedFolder?.disableFolderNote) { return; }
}
// file matched folder name before rename
// file hasnt moved just renamed
// Need to rename the folder
if (oldFolder && this.settings.folderNoteName.replace('{{folder_name}}', oldFolder.name) + '.md' === oldFileName && newFilePath === oldFilePath) {
return this.renameFolderOnFileRename(file, oldPath, oldFolder);
}
// the note has been moved somewhere and is no longer a folder note
// cleanup css on the folder and note
if (oldFolder && oldFolder.name + '.md' === oldFileName && newFilePath !== oldFilePath) {
this.removeCSSClassFromEL(oldFolder.path, 'has-folder-note');
if (excludedFolder?.disableSync && this.extractFolderName(this.settings.folderNoteName, file.name.slice(0, file.name.lastIndexOf('.'))) === newFolder?.name) {
this.addCSSClassToTitleEL(file.path, 'is-folder-note');
this.addCSSClassToTitleEL(newFolder.path, 'has-folder-note');
return;
} else if (excludedFolder?.disableSync) {
this.removeCSSClassFromEL(file.path, 'is-folder-note');
this.removeCSSClassFromEL(oldPath, 'is-folder-note');
this.removeCSSClassFromEL(newFolder?.path || '', 'has-folder-note');
return;
}
// file has been moved into position where it can be a folder note!
@ -260,14 +258,39 @@ export default class FolderNotesPlugin extends Plugin {
this.addCSSClassToTitleEL(newFolder.path, 'has-folder-note');
this.addCSSClassToTitleEL(file.path, 'is-folder-note', true);
}
// file matched folder name before rename
// file hasnt moved just renamed
// Need to rename the folder
if (!oldFolder) return;
if (this.settings.folderNoteName.replace('{{folder_name}}', oldFolder.name) + fileExtension === oldFileName && newFilePath === oldFilePath) {
return this.renameFolderOnFileRename(file, oldPath, oldFolder);
} else if (this.settings.folderNoteName.replace('{{folder_name}}', oldFolder.name) + fileExtension === file.name && newFilePath === oldFilePath) {
return this.renameFolderOnFileRename(file, oldPath, oldFolder);
}
// the note has been moved somewhere and is no longer a folder note
// cleanup css on the folder and note
if (oldFolder.name + this.getExtensionFromPathString(oldFilePath) === oldFileName && newFilePath !== oldFilePath) {
this.removeCSSClassFromEL(oldFolder.path, 'has-folder-note');
this.removeCSSClassFromEL(file.path, 'is-folder-note');
this.removeCSSClassFromEL(oldPath, 'is-folder-note');
}
}
async renameFolderOnFileRename(file: TFile, oldPath: string, oldFolder: TAbstractFile) {
const newFolderName = this.extractFolderName(this.settings.folderNoteName, file.basename);
if (!newFolderName) return;
if (!newFolderName) {
this.removeCSSClassFromEL(oldFolder.path, 'has-folder-note');
this.removeCSSClassFromEL(file.path, 'is-folder-note');
return;
} else if (newFolderName === oldFolder.name) {
this.addCSSClassToTitleEL(oldFolder.path, 'has-folder-note');
this.addCSSClassToTitleEL(file.path, 'is-folder-note');
return;
}
const newFolderPath = oldFolder.parent.path + '/' + this.extractFolderName(this.settings.folderNoteName, file.basename);
if (this.app.vault.getAbstractFileByPath(newFolderPath)) {
if (this.app.vault.getAbstractFileByPath(newFolderPath) || this.app.vault.getAbstractFileByPath(this.extractFolderName(this.settings.folderNoteName, file.basename) || '')) {
await this.app.vault.rename(file, oldPath);
return new Notice('A folder with the same name already exists');
}
@ -276,10 +299,13 @@ export default class FolderNotesPlugin extends Plugin {
extractFolderName(template: string, changedFileName: string) {
const [prefix, suffix] = template.split('{{folder_name}}');
if (changedFileName.startsWith(prefix)) {
if (!changedFileName.startsWith(prefix) || !changedFileName.endsWith(suffix)) {
return null;
}
if (changedFileName.startsWith(prefix) && prefix.trim() !== '') {
return changedFileName.slice(prefix.length).replace(suffix, '');
} else if (changedFileName.endsWith(suffix)) {
return changedFileName.slice(prefix.length, -suffix.length);
} else if (changedFileName.endsWith(suffix) && suffix.trim() !== '') {
return changedFileName.slice(0, -suffix.length);
}
return null;
}
@ -308,8 +334,8 @@ export default class FolderNotesPlugin extends Plugin {
this.addCSSClassToTitleEL(path, 'is-folder-note', true);
}
async openFolderNote(path: string, evt: MouseEvent) {
const file = this.app.vault.getAbstractFileByPath(path);
async openFolderNote(file: TAbstractFile, evt: MouseEvent) {
const path = file.path;
if (this.app.workspace.getActiveFile()?.path === path) { return; }
const leaf = this.app.workspace.getLeaf(Keymap.isModEvent(evt) || this.settings.openInNewTab);
if (file instanceof TFile) {
@ -328,6 +354,14 @@ export default class FolderNotesPlugin extends Plugin {
return path.substring(path.lastIndexOf('/' || '\\') >= 0 ? path.lastIndexOf('/' || '\\') + 1 : 0);
}
getFolderNameFromPathString(path: string): string {
return path.split('/').slice(-2)[0];
}
getExtensionFromPathString(path: string): string {
return path.slice(path.lastIndexOf('.') >= 0 ? path.lastIndexOf('.') : 0);
}
getPathFromString(path: string): string {
const subString = path.lastIndexOf('/' || '\\') >= 0 ? path.lastIndexOf('/') : path.length;
return path.substring(0, subString);
@ -392,8 +426,8 @@ export default class FolderNotesPlugin extends Plugin {
loadFileClasses(forceReload = false) {
if (this.activeFileExplorer === this.getFileExplorer() && !forceReload) { return; }
this.activeFileExplorer = this.getFileExplorer();
this.app.vault.getMarkdownFiles().forEach((file) => {
if (file.basename !== file.parent.name) { return; }
this.app.vault.getFiles().forEach((file) => {
if (this.extractFolderName(this.settings.folderNoteName, file.basename) !== file.parent.name) { return; }
const excludedFolder = this.getExcludedFolderByPath(file.parent.path);
// cleanup after ourselves
// Incase settings have changed

View file

@ -23,7 +23,7 @@ export default class FolderNameModal extends Modal {
.setName('Enter the name of the folder')
.addText((text) =>
text
.setValue(this.folder.name.replace('.md', ''))
.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())) {

View file

@ -20,6 +20,8 @@ export interface FolderNotesSettings {
openFolderNoteOnClickInPath: boolean;
openInNewTab: boolean;
folderNoteName: string;
folderNoteType: '.md' | '.canvas';
disableFolderHighlighting: boolean;
}
export const DEFAULT_SETTINGS: FolderNotesSettings = {
@ -38,6 +40,8 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = {
openFolderNoteOnClickInPath: true,
openInNewTab: false,
folderNoteName: '{{folder_name}}',
folderNoteType: '.md',
disableFolderHighlighting: false,
};
export class SettingsTab extends PluginSettingTab {
plugin: FolderNotesPlugin;
@ -64,20 +68,45 @@ export class SettingsTab extends PluginSettingTab {
this.plugin.settings.folderNoteName = value;
await this.plugin.saveSettings();
})
)
.addButton((button) =>
button
.setButtonText('Rename existing folder notes')
.setCta()
.onClick(async () => {
})
);
new Setting(containerEl)
.setName('Disable folder collapsing')
.setDesc('Disable the ability to collapse folders by clicking exactly on the folder name')
.addToggle((toggle) =>
toggle
.setValue(!this.plugin.settings.enableCollapsing)
.onChange(async (value) => {
this.plugin.settings.enableCollapsing = !value;
.setName('Folder note type')
.setDesc('Choose the file type of the folder note (markdown or canvas)')
.addDropdown((dropdown) =>
dropdown
.addOption('.md', 'markdown')
.addOption('.canvas', 'canvas')
.setValue(this.plugin.settings.folderNoteType)
.onChange(async (value: '.md' | '.canvas') => {
this.plugin.settings.folderNoteType = value;
await this.plugin.saveSettings();
})
);
const disableSetting = new Setting(containerEl);
disableSetting.setName('Disable folder collapsing');
disableSetting.setDesc('Disable the ability to collapse folders by clicking exactly on the folder name');
disableSetting.addToggle((toggle) =>
toggle
.setValue(!this.plugin.settings.enableCollapsing)
.onChange(async (value) => {
this.plugin.settings.enableCollapsing = !value;
await this.plugin.saveSettings();
})
);
disableSetting.infoEl.appendText('Requires a restart to take effect');
disableSetting.infoEl.style.color = this.app.vault.getConfig('accentColor') as string || '#7d5bed';
new Setting(containerEl)
.setName('Only open folder notes through the name')
.setDesc('Only open folder notes in the file explorer by clicking on the folder name')
@ -125,18 +154,20 @@ export class SettingsTab extends PluginSettingTab {
})
);
if (Platform.isDesktop) {
new Setting(containerEl)
.setName('Open folder note in a new tab by default')
.setDesc('Always open folder notes in a new tab (except when you try to open the same note) instead of having to use ctrl/cmd + click to open in a new tab')
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.openInNewTab)
.onChange(async (value) => {
this.plugin.settings.openInNewTab = value;
await this.plugin.saveSettings();
this.display();
})
);
const setting3 = new Setting(containerEl);
setting3.setName('Open folder note in a new tab by default');
setting3.setDesc('Always open folder notes in a new tab (except when you try to open the same note) instead of having to use ctrl/cmd + click to open in a new tab');
setting3.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.openInNewTab)
.onChange(async (value) => {
this.plugin.settings.openInNewTab = value;
await this.plugin.saveSettings();
this.display();
})
);
setting3.infoEl.appendText('Requires a restart to take effect');
setting3.infoEl.style.color = '#7d5bed';
}
new Setting(containerEl)
.setName('Automatically create folder notes')
@ -216,7 +247,19 @@ export class SettingsTab extends PluginSettingTab {
await this.plugin.saveSettings();
})
);
/*
new Setting(containerEl)
.setName('Disable folder highlighting through path')
.setDesc('Disable the highlighting of folders in the path that have a folder note when you click on a folder name in the path')
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.disableFolderHighlighting)
.onChange(async (value) => {
this.plugin.settings.disableFolderHighlighting = value;
await this.plugin.saveSettings();
})
);
*/
const setting = new Setting(containerEl);
const desc = document.createDocumentFragment();
@ -226,7 +269,7 @@ export class SettingsTab extends PluginSettingTab {
'Obsidian should also be restarted if the template path was removed.'
);
setting.setName('Template path');
setting.setDesc(desc).descEl.style.color = 'red';
setting.setDesc(desc).descEl.style.color = this.app.vault.getConfig('accentColor') as string || '#7d5bed';
setting.addSearch((cb) => {
new TemplateSuggest(cb.inputEl, this.plugin);
cb.setPlaceholder('Template path');