From 103145c45a9d4b370d1454a4840af46f9882e410 Mon Sep 17 00:00:00 2001 From: Lost Paul <70213368+LostPaul@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:11:37 +0200 Subject: [PATCH] Support front matter title plugin --- package.json | 1 + src/events/frontMatterTitle.ts | 84 ++++++++++++++++++ src/folderOverview.ts | 1 - src/globals.d.ts | 3 + src/main.ts | 69 +++++++++++++-- src/settings.ts | 154 ++++++++++++++++++++++++--------- styles.css | 6 +- 7 files changed, 267 insertions(+), 51 deletions(-) create mode 100644 src/events/frontMatterTitle.ts diff --git a/package.json b/package.json index cdd8d82..85e4c38 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@typescript-eslint/parser": "5.29.0", "builtin-modules": "3.3.0", "esbuild": "0.14.47", + "front-matter-plugin-api-provider": "^0.1.4-alpha", "obsidian": "latest", "tslib": "2.4.0", "typescript": "4.7.4" diff --git a/src/events/frontMatterTitle.ts b/src/events/frontMatterTitle.ts new file mode 100644 index 0000000..16b8034 --- /dev/null +++ b/src/events/frontMatterTitle.ts @@ -0,0 +1,84 @@ +import FolderNotesPlugin from 'src/main'; +import { getDefer, Listener, Events, ApiInterface, DeferInterface, ListenerRef, EventDispatcherInterface } from 'front-matter-plugin-api-provider'; +import { App, TFile, TFolder } from 'obsidian'; +import { extractFolderName, getFolder } from 'src/functions/folderNoteFunctions'; +export class FrontMatterTitlePluginHandler { + plugin: FolderNotesPlugin; + app: App; + api: ApiInterface | null = null; + deffer: DeferInterface | null = null; + modifiedFolders: Map = new Map(); + eventRef: ListenerRef<'manager:update'>; + dispatcher: EventDispatcherInterface + constructor(plugin: FolderNotesPlugin) { + this.plugin = plugin; + this.app = plugin.app; + + (async () => { + this.deffer = getDefer(this.app); + if (this.deffer.isPluginReady()) { + this.api = this.deffer.getApi(); + } else { + await this.deffer.awaitPlugin(); + this.api = this.deffer.getApi(); + // if you want to wait features you can use the following chain + if (!this.deffer.isFeaturesReady()) { + await this.deffer.awaitFeatures(); + } + } + const dispatcher = this.api?.getEventDispatcher(); + if (dispatcher) { + this.dispatcher = dispatcher; + } + const event: Listener = { + name: 'manager:update', + cb: (data) => { + this.handleRename(data as any, true); + }, + }; + // Keep ref to remove listener + const ref = dispatcher?.addListener(event); + if (ref) { + this.eventRef = ref; + } + this.plugin.app.vault.getFiles().forEach((file) => { + this.handleRename({ id: '', result: false, path: file.path }, false); + }); + this.plugin.updateBreadcrumbs(); + })(); + } + deleteEvent() { + if (this.eventRef) { + this.dispatcher.removeListener(this.eventRef); + } + } + async handleRename(data: { + id: string; + result: boolean; + path: string; + }, isEvent: boolean) { + if ((data as any).data) data = (data as any).data + const file = this.app.vault.getAbstractFileByPath(data.path); + if (!(file instanceof TFile)) { return; } + + const resolver = this.api?.getResolverFactory()?.createResolver('#feature-id#'); + const newName = resolver?.resolve(file?.path ?? '') + const folder = getFolder(this.plugin, file); + + if ((extractFolderName(this.plugin.settings.folderNoteName, file.basename) || file.basename) !== folder?.name) { return; } + if (!(folder instanceof TFolder)) { return; } + if (isEvent) { + this.plugin.changeName(folder, newName, true); + } else { + this.plugin.changeName(folder, newName, false); + } + if (newName) { + folder.newName = newName; + this.modifiedFolders.set(folder.path, folder); + } else { + folder.newName = null; + this.modifiedFolders.delete(folder.path); + } + + } +} diff --git a/src/folderOverview.ts b/src/folderOverview.ts index 1293624..a05bc9c 100644 --- a/src/folderOverview.ts +++ b/src/folderOverview.ts @@ -128,7 +128,6 @@ export function createOverview(plugin: FolderNotesPlugin, source: string, el: HT */ } if (includeTypes.length > 1 && style !== 'grid' && (!showEmptyFolders || yaml.onlyIncludeSubfolders)) { - console.log('depth', depth, yaml.onlyIncludeSubfolders); removeEmptyFolders(ul, 1, yaml); } } diff --git a/src/globals.d.ts b/src/globals.d.ts index 762ff2a..d07e452 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -18,6 +18,9 @@ declare module 'obsidian' { interface Setting { createList: (list: ListComponent | ((list: ListComponent) => void)) => ListComponent; } + interface TFolder { + newName: string | null; + } class ListComponent { containerEl: HTMLElement; diff --git a/src/main.ts b/src/main.ts index 408722d..011ad7e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,7 +6,7 @@ import { handleViewHeaderClick, handleFolderClick } from './events/handleClick'; import { handleFileRename, handleFolderRename } from './events/handleRename'; import { createFolderNote, extractFolderName, getFolderNote, getFolder } from './functions/folderNoteFunctions'; import { getExcludedFolder } from './excludedFolder'; -// import { FrontMatterTitlePluginHandler } from './events/frontMatterTitle'; +import { FrontMatterTitlePluginHandler } from './events/frontMatterTitle'; import { createOverview as createFolderOverview } from './folderOverview'; import { FolderOverviewSettings } from './modals/folderOverview'; import './functions/ListComponent'; @@ -16,7 +16,7 @@ export default class FolderNotesPlugin extends Plugin { settingsTab: SettingsTab; activeFolderDom: HTMLElement | null; activeFileExplorer: FileExplorerWorkspaceLeaf; - + fmtpHandler: FrontMatterTitlePluginHandler | null = null; async onload() { console.log('loading folder notes plugin'); await this.loadSettings(); @@ -33,7 +33,9 @@ export default class FolderNotesPlugin extends Plugin { new Commands(this.app, this).registerCommands(); - // new FrontMatterTitlePluginHandler(this.app, this); + if (this.settings.frontMatterTitle.enabled) { + this.fmtpHandler = new FrontMatterTitlePluginHandler(this); + } this.observer = new MutationObserver((mutations: MutationRecord[]) => { mutations.forEach((rec) => { @@ -50,9 +52,18 @@ export default class FolderNotesPlugin extends Plugin { if (!breadcrumbs) return; let path = ''; breadcrumbs.forEach((breadcrumb: HTMLElement) => { - path += breadcrumb.innerText.trim() + '/'; + if (breadcrumb.hasAttribute('old-name')) { + path += breadcrumb.getAttribute('old-name') + '/'; + } else { + path += breadcrumb.innerText.trim() + '/'; + } const folderPath = path.slice(0, -1); breadcrumb.setAttribute('data-path', folderPath); + const folder = this.fmtpHandler?.modifiedFolders.get(folderPath); + if (folder) { + breadcrumb.setAttribute('old-name', folder.name || ''); + breadcrumb.innerText = folder.newName || ''; + } const folderNote = getFolderNote(this, folderPath); if (folderNote) { breadcrumb.classList.add('has-folder-note'); @@ -131,7 +142,6 @@ export default class FolderNotesPlugin extends Plugin { return handleFileRename(file, oldPath, this); } })); - this.registerEvent(this.app.vault.on('delete', (file: TAbstractFile) => { if (file instanceof TFile) { const folder = getFolder(this, file); @@ -228,6 +238,51 @@ export default class FolderNotesPlugin extends Plugin { }); } + async changeName(folder: TFolder, name: string | null | undefined, replacePath: boolean, waitForCreate = false, count = 0) { + if (!name) name = folder.name; + let fileExplorerItem = this.getEL(folder.path); + if (!fileExplorerItem) { + if (waitForCreate && count < 5) { + await new Promise((r) => setTimeout(r, 500)); + this.changeName(folder, name, replacePath, waitForCreate, count + 1); + return; + } + return; + } + fileExplorerItem = fileExplorerItem.querySelector('div.nav-folder-title-content') + if (!fileExplorerItem) { return; } + if (this.settings.frontMatterTitle.explorer) { + fileExplorerItem.innerText = name; + fileExplorerItem.setAttribute('old-name', folder.name); + } else { + fileExplorerItem.innerText = folder.name; + fileExplorerItem.removeAttribute('old-name'); + } + if (replacePath) { + this.updateBreadcrumbs(); + } + } + + updateBreadcrumbs(remove?: boolean) { + 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); + viewHeaderItems.forEach((item) => { + if (!item.hasAttribute('data-path')) { return; } + const path = item.getAttribute('data-path'); + const folder = files.find((file) => file.path === path); + if (!(folder instanceof TFolder)) { return; } + if (remove) { + item.textContent = folder.name; + item.removeAttribute('old-name'); + } else { + item.textContent = folder.newName || folder.name; + item.setAttribute('old-name', folder.name); + item.setAttribute('data-path', folder.path); + } + }); + } + removeCSSClassFromEL(path: string | undefined, cssClass: string) { if (!path) return; const fileExplorerItem = this.getEL(path); @@ -288,6 +343,10 @@ export default class FolderNotesPlugin extends Plugin { document.body.classList.remove('hide-folder-note'); document.body.classList.remove('fn-whitespace-stop-collapsing'); if (this.activeFolderDom) { this.activeFolderDom.removeClass('is-active'); } + if (this.fmtpHandler) { + this.fmtpHandler.deleteEvent(); + } + } async loadSettings() { diff --git a/src/settings.ts b/src/settings.ts index b06f4bd..d518caf 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -3,6 +3,7 @@ import FolderNotesPlugin from './main'; import { TemplateSuggest } from './suggesters/templateSuggester'; import { extractFolderName, getFolderNote } from './functions/folderNoteFunctions'; import { addExcludeFolderListItem, ExcludedFolder, addExcludedFolder, ExcludePattern, addExcludePatternListItem } from './excludedFolder'; +import { FrontMatterTitlePluginHandler } from './events/frontMatterTitle'; // import ConfirmationModal from "./modals/confirmCreation"; import { yamlSettings } from './folderOverview'; export interface FolderNotesSettings { @@ -30,6 +31,11 @@ export interface FolderNotesSettings { defaultOverview: yamlSettings; useSubmenus: boolean; syncMove: boolean; + frontMatterTitle: { + enabled: boolean; + explorer: boolean; + path: boolean; + } } export const DEFAULT_SETTINGS: FolderNotesSettings = { @@ -64,6 +70,11 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = { }, useSubmenus: true, syncMove: true, + frontMatterTitle: { + enabled: false, + explorer: true, + path: true, + } }; export class SettingsTab extends PluginSettingTab { plugin: FolderNotesPlugin; @@ -116,6 +127,29 @@ export class SettingsTab extends PluginSettingTab { }) ); + const setting = new Setting(containerEl); + const desc = document.createDocumentFragment(); + desc.append( + 'After setting the template path, restart Obsidian if the template folder path (from templater/templates) had been changed beforehand.', + desc.createEl('br'), + 'Obsidian should also be restarted if the template path was removed.' + ); + setting.setName('Template path'); + 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'); + cb.setValue(this.plugin.app.vault.getAbstractFileByPath(this.plugin.settings.templatePath)?.name.replace('.md', '') || ''); + cb.onChange(async (value) => { + if (value.trim() === '') { + this.plugin.settings.templatePath = ''; + await this.plugin.saveSettings(); + this.display(); + return; + } + }); + }); + const storageLocation = new Setting(containerEl) .setName('Storage location') .setDesc('Choose where to store the folder notes') @@ -195,6 +229,26 @@ export class SettingsTab extends PluginSettingTab { disableSetting.infoEl.appendText('Requires a restart to take effect'); disableSetting.infoEl.style.color = this.app.vault.getConfig('accentColor') as string || '#7d5bed'; + if (Platform.isDesktopApp) { + new Setting(containerEl) + .setName('Key for creating folder note') + .setDesc('The key combination to create a folder note') + .addDropdown((dropdown) => { + if (!Platform.isMacOS) { + dropdown.addOption('ctrl', 'Ctrl + Click'); + } else { + dropdown.addOption('ctrl', 'Cmd + Click'); + } + dropdown.addOption('alt', 'Alt + Click'); + dropdown.setValue(this.plugin.settings.ctrlKey ? 'ctrl' : 'alt'); + dropdown.onChange(async (value) => { + this.plugin.settings.ctrlKey = value === 'ctrl'; + this.plugin.settings.altKey = value === 'alt'; + await this.plugin.saveSettings(); + this.display(); + }); + }); + } new Setting(containerEl) .setName('Only open folder notes through the name') @@ -284,27 +338,6 @@ export class SettingsTab extends PluginSettingTab { }) ); - if (Platform.isDesktopApp) { - new Setting(containerEl) - .setName('Key for creating folder note') - .setDesc('The key combination to create a folder note') - .addDropdown((dropdown) => { - if (!Platform.isMacOS) { - dropdown.addOption('ctrl', 'Ctrl + Click'); - } else { - dropdown.addOption('ctrl', 'Cmd + Click'); - } - dropdown.addOption('alt', 'Alt + Click'); - dropdown.setValue(this.plugin.settings.ctrlKey ? 'ctrl' : 'alt'); - dropdown.onChange(async (value) => { - this.plugin.settings.ctrlKey = value === 'ctrl'; - this.plugin.settings.altKey = value === 'alt'; - await this.plugin.saveSettings(); - this.display(); - }); - }); - } - new Setting(containerEl) .setName('Add underline to folders with folder notes') .setDesc('Add an underline to folders that have a folder note in the file explorer') @@ -355,28 +388,63 @@ export class SettingsTab extends PluginSettingTab { ); } - const setting = new Setting(containerEl); - const desc = document.createDocumentFragment(); - desc.append( - 'After setting the template path, restart Obsidian if the template folder path (from templater/templates) had been changed beforehand.', - desc.createEl('br'), - 'Obsidian should also be restarted if the template path was removed.' - ); - setting.setName('Template path'); - 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'); - cb.setValue(this.plugin.app.vault.getAbstractFileByPath(this.plugin.settings.templatePath)?.name.replace('.md', '') || ''); - cb.onChange(async (value) => { - if (value.trim() === '') { - this.plugin.settings.templatePath = ''; - await this.plugin.saveSettings(); - this.display(); - return; - } - }); - }); + new Setting(containerEl) + .setName('Enable front matter title plugin integration') + .setDesc('Automatically rename a folder name when the folder note is renamed') + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.frontMatterTitle.enabled) + .onChange(async (value) => { + this.plugin.settings.frontMatterTitle.enabled = value; + await this.plugin.saveSettings(); + if (value) { + this.plugin.fmtpHandler = new FrontMatterTitlePluginHandler(this.plugin); + } else { + if (this.plugin.fmtpHandler) { + this.plugin.updateBreadcrumbs(true); + } + this.plugin.fmtpHandler?.deleteEvent(); + this.plugin.fmtpHandler = null; + } + this.display(); + }) + ); + + if (this.plugin.settings.frontMatterTitle.enabled) { + new Setting(containerEl) + .setName('Include file explorer') + .setDesc('Automatically rename a folder name in the file explorer when the folder note is renamed') + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.frontMatterTitle.explorer) + .onChange(async (value) => { + this.plugin.settings.frontMatterTitle.explorer = value; + await this.plugin.saveSettings(); + this.plugin.app.vault.getFiles().forEach((file) => { + this.plugin.fmtpHandler?.handleRename({ id: '', result: false, path: file.path }, false); + }); + this.display(); + }) + ); + + new Setting(containerEl) + .setName('Include path above note') + .setDesc('Automatically rename a folder name in the path above a note when the folder note is renamed') + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.frontMatterTitle.path) + .onChange(async (value) => { + this.plugin.settings.frontMatterTitle.path = value; + await this.plugin.saveSettings(); + if (value) { + this.plugin.updateBreadcrumbs(); + } else { + this.plugin.updateBreadcrumbs(true); + } + this.display(); + }) + ); + } // Due to an issue with templater it has been disabled for now // If you want to try it yourself make a pr diff --git a/styles.css b/styles.css index d72ad36..92ac79e 100644 --- a/styles.css +++ b/styles.css @@ -110,10 +110,10 @@ } .folder-overview-list { - margin-top : 0 !important; + margin-top: 0 !important; margin-bottom: 0 !important; padding-bottom: 1.200 !important; - padding-top: 1.200 !important; + padding-top: 1.200 !important; } .folder-overview-list-item { @@ -142,9 +142,11 @@ flex-direction: column; justify-content: space-between; } + .folder-overview-grid-item-article a { text-decoration: none !important; } + .folder-overview-grid-item-article h1 { font-size: 1.2rem; }