import { Plugin , MenuItem } from 'obsidian' ; import IconFolderSettingsTab from './iconFolderSettingsTab' ; import IconsPickerModal , { Icon } from './iconsPickerModal' ; import { DEFAULT_SETTINGS , IconFolderSettings } from './settings' ; import { addIconsToDOM , addInheritanceForFolder , addInheritanceIconToFile , removeFromDOM , removeInheritanceForFolder , } from './util' ; export interface FolderIconObject { iconName : string | null ; inheritanceIcon : string ; } export default class IconFolderPlugin extends Plugin { private data : Record < string , string | IconFolderSettings | FolderIconObject > ; private registeredFileExplorers = new WeakMap ( ) ; async onload ( ) { console . log ( 'loading obsidian-icon-folder' ) ; await this . loadIconFolderData ( ) ; await this . checkRecentlyUsedIcons ( ) ; this . app . workspace . onLayoutReady ( ( ) => this . handleChangeLayout ( ) ) ; this . registerEvent ( this . app . workspace . on ( 'layout-change' , ( ) => this . handleChangeLayout ( ) ) ) ; this . registerEvent ( this . app . workspace . on ( 'file-menu' , ( menu , file ) => { const addIconMenuItem = ( item ) => { item . setTitle ( 'Change icon' ) ; item . setIcon ( 'hashtag' ) ; item . onClick ( ( ) => { const modal = new IconsPickerModal ( this . app , this , file . path ) ; modal . open ( ) ; } ) ; } ; const removeIconMenuItem = ( item ) => { item . setTitle ( 'Remove icon' ) ; item . setIcon ( 'trash' ) ; item . onClick ( ( ) => { this . removeFolderIcon ( file . path ) ; removeFromDOM ( file . path ) ; } ) ; } ; menu . addItem ( addIconMenuItem ) ; const node = document . querySelector ( `[data-path="${ file . path }"]` ) ; const iconNode = node . querySelector ( '.obsidian-icon-folder-icon' ) ; if ( iconNode ) { menu . addItem ( removeIconMenuItem ) ; } const inheritIcon = ( item ) => { if ( typeof this . data [ file . path ] === 'object' ) { item . setTitle ( 'Remove inherit icon' ) ; item . onClick ( ( ) => { removeInheritanceForFolder ( this , file . path ) ; this . saveInheritanceData ( file . path , null ) ; } ) ; } else { item . setTitle ( 'Inherit icon' ) ; item . onClick ( ( ) => { const modal = new IconsPickerModal ( this . app , this , file . path ) ; modal . open ( ) ; modal . onChooseItem = ( icon ) => { this . saveInheritanceData ( file . path , icon ) ; addInheritanceForFolder ( this , file . path ) ; } ; } ) ; } item . setIcon ( 'vertical-three-dots' ) ; } ; menu . addItem ( inheritIcon ) ; } ) ) ; this . registerEvent ( this . app . vault . on ( 'delete' , ( file ) => { const path = file . path ; this . removeFolderIcon ( path ) ; } ) ) ; this . registerEvent ( this . app . vault . on ( 'rename' , ( file , oldPath ) => { this . renameFolder ( file . path , oldPath ) ; } ) ) ; this . addSettingTab ( new IconFolderSettingsTab ( this . app , this ) ) ; } private handleChangeLayout ( ) { const data = Object . entries ( this . data ) as [ string , string | FolderIconObject ] [ ] ; addIconsToDOM ( this , data , this . registeredFileExplorers , ( ) => { this . registerEvent ( this . app . vault . on ( 'create' , ( file ) => { const inheritanceFolders = Object . entries ( this . data ) . filter ( ( [ k , v ] ) => k !== 'settings' && typeof v === 'object' ) ; if ( file . parent . path === '/' ) return ; inheritanceFolders . forEach ( ( [ path , obj ] ) => { if ( file . parent . path . includes ( path ) ) { addInheritanceIconToFile ( this , this . registeredFileExplorers , file . path , obj . inheritanceIcon ) ; } } ) ; } ) ) ; } ) ; } private saveInheritanceData ( folderPath , icon ) { const currentValue = this . data [ folderPath ] ; if ( icon === null && currentValue && typeof currentValue === 'object' ) { const folderObject = currentValue as FolderIconObject ; if ( folderObject . iconName ) { this . data [ folderPath ] = folderObject . iconName ; } else { delete this . data [ folderPath ] ; } } else { if ( currentValue ) { if ( typeof currentValue === 'string' ) { this . data [ folderPath ] = { iconName : currentValue as string , inheritanceIcon : typeof icon === 'object' ? icon . name : icon , } ; } else if ( folderPath !== 'settings' ) { this . data [ folderPath ] = { ... ( currentValue as FolderIconObject ) , inheritanceIcon : typeof icon === 'object' ? icon . name : icon , } ; } } else { this . data [ folderPath ] = { iconName : null , inheritanceIcon : typeof icon === 'object' ? icon . name : icon , } ; } } this . saveIconFolderData ( ) ; } onunload ( ) { console . log ( 'unloading obsidian-icon-folder' ) ; } renameFolder ( newPath , oldPath ) { if ( ! this . data [ oldPath ] || newPath === oldPath ) { return ; } Object . defineProperty ( this . data , newPath , Object . getOwnPropertyDescriptor ( this . data , oldPath ) ) ; delete this . data [ oldPath ] ; this . saveIconFolderData ( ) ; } removeFolderIcon ( path ) { if ( ! this . data [ path ] ) { return ; } if ( typeof this . data [ path ] === 'object' ) { const currentValue = this . data [ path ] as FolderIconObject ; this . data [ path ] = { ... currentValue , iconName : null , } ; } else { delete this . data [ path ] ; } this . saveIconFolderData ( ) ; } addFolderIcon ( path , icon ) { const iconName = typeof icon === 'object' ? icon . name : icon ; this . data [ path ] = iconName ; if ( ! this . getSettings ( ) . recentlyUsedIcons . includes ( iconName ) ) { if ( this . getSettings ( ) . recentlyUsedIcons . length >= this . getSettings ( ) . recentlyUsedIconsSize ) { this . getSettings ( ) . recentlyUsedIcons . pop ( ) ; } this . getSettings ( ) . recentlyUsedIcons . unshift ( iconName ) ; this . checkRecentlyUsedIcons ( ) ; } this . saveIconFolderData ( ) ; } public getSettings ( ) { return this . data . settings as IconFolderSettings ; } async loadIconFolderData ( ) { const data = await this . loadData ( ) ; if ( data ) { Object . entries ( DEFAULT_SETTINGS ) . forEach ( ( [ k , v ] ) => { if ( ! data . settings [ k ] ) { data . settings [ k ] = v ; } } ) ; } this . data = Object . assign ( { settings : { ... DEFAULT_SETTINGS } } , { } , data ) ; } async saveIconFolderData ( ) { await this . saveData ( this . data ) ; } async checkRecentlyUsedIcons ( ) { if ( this . getSettings ( ) . recentlyUsedIcons . length > this . getSettings ( ) . recentlyUsedIconsSize ) { this . getSettings ( ) . recentlyUsedIcons = this . getSettings ( ) . recentlyUsedIcons . slice ( 0 , this . getSettings ( ) . recentlyUsedIconsSize ) ; await this . saveIconFolderData ( ) ; } } getData ( ) { return this . data ; } }