Merge pull request #158 from SebastianMC/156-indexmd-based-folder-note-metadata
Index-based folder note metadata support
This commit is contained in:
commit
d2bf6e30b2
|
@ -0,0 +1,7 @@
|
|||
import {
|
||||
Plugin
|
||||
} from 'obsidian'
|
||||
|
||||
export interface CustomSortPluginAPI extends Plugin {
|
||||
indexNoteBasename(): string|undefined
|
||||
}
|
|
@ -34,10 +34,11 @@ import {
|
|||
import {
|
||||
BookmarksPluginInterface
|
||||
} from "../utils/BookmarksCorePluginSignature";
|
||||
import {CustomSortPluginAPI} from "../custom-sort-plugin";
|
||||
|
||||
export interface ProcessingContext {
|
||||
// For internal transient use
|
||||
plugin?: Plugin // to hand over the access to App instance to the sorting engine
|
||||
plugin?: CustomSortPluginAPI // to hand over the access to App instance to the sorting engine
|
||||
_mCache?: MetadataCache
|
||||
starredPluginInstance?: Starred_PluginInstance
|
||||
bookmarksPluginInstance?: BookmarksPluginInterface,
|
||||
|
@ -371,6 +372,15 @@ export const matchGroupRegex = (theRegex: RegExpSpec, nameForMatching: string):
|
|||
return [false, undefined, undefined]
|
||||
}
|
||||
|
||||
const mdataValueFromFMCaches = (mdataFieldName: string, fc?: FrontMatterCache, fcPrio?: FrontMatterCache): any => {
|
||||
let prioValue = undefined
|
||||
if (fcPrio) {
|
||||
prioValue = fcPrio?.[mdataFieldName]
|
||||
}
|
||||
|
||||
return prioValue ?? fc?.[mdataFieldName]
|
||||
}
|
||||
|
||||
export const determineSortingGroup = function (entry: TFile | TFolder, spec: CustomSortSpec, ctx?: ProcessingContext): FolderItemForSorting {
|
||||
let groupIdx: number
|
||||
let determined: boolean = false
|
||||
|
@ -466,10 +476,18 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
|
|||
case CustomSortGroupType.HasMetadataField:
|
||||
if (group.withMetadataFieldName) {
|
||||
if (ctx?._mCache) {
|
||||
// For folders - scan metadata of 'folder note'
|
||||
// For folders - scan metadata of 'folder note' in same-name-as-parent-folder mode
|
||||
const notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
|
||||
const frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
|
||||
const hasMetadata: boolean | undefined = frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
|
||||
let frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
|
||||
let hasMetadata: boolean | undefined = frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
|
||||
// For folders, if index-based folder note mode, scan the index file, giving it the priority
|
||||
if (aFolder) {
|
||||
const indexNoteBasename = ctx?.plugin?.indexNoteBasename()
|
||||
if (indexNoteBasename) {
|
||||
frontMatterCache = ctx._mCache.getCache(`${entry.path}/${indexNoteBasename}.md`)?.frontmatter
|
||||
hasMetadata = hasMetadata || frontMatterCache?.hasOwnProperty(group.withMetadataFieldName)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMetadata) {
|
||||
determined = true
|
||||
|
@ -554,12 +572,24 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
|
|||
if (isPrimaryOrderByMetadata || isSecondaryOrderByMetadata || isDerivedPrimaryByMetadata || isDerivedSecondaryByMetadata) {
|
||||
if (ctx?._mCache) {
|
||||
// For folders - scan metadata of 'folder note'
|
||||
// and if index-based folder note mode, scan the index file, giving it the priority
|
||||
const notePathToScan: string = aFile ? entry.path : `${entry.path}/${entry.name}.md`
|
||||
const frontMatterCache: FrontMatterCache | undefined = ctx._mCache.getCache(notePathToScan)?.frontmatter
|
||||
if (isPrimaryOrderByMetadata) metadataValueToSortBy = frontMatterCache?.[group?.byMetadataField || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING]
|
||||
if (isSecondaryOrderByMetadata) metadataValueSecondaryToSortBy = frontMatterCache?.[group?.byMetadataFieldSecondary || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING]
|
||||
if (isDerivedPrimaryByMetadata) metadataValueDerivedPrimaryToSortBy = frontMatterCache?.[spec.byMetadataField || DEFAULT_METADATA_FIELD_FOR_SORTING]
|
||||
if (isDerivedSecondaryByMetadata) metadataValueDerivedSecondaryToSortBy = frontMatterCache?.[spec.byMetadataFieldSecondary || DEFAULT_METADATA_FIELD_FOR_SORTING]
|
||||
let prioFrontMatterCache: FrontMatterCache | undefined = undefined
|
||||
if (aFolder) {
|
||||
const indexNoteBasename = ctx?.plugin?.indexNoteBasename()
|
||||
if (indexNoteBasename) {
|
||||
prioFrontMatterCache = ctx._mCache.getCache(`${entry.path}/${indexNoteBasename}.md`)?.frontmatter
|
||||
}
|
||||
}
|
||||
if (isPrimaryOrderByMetadata) metadataValueToSortBy =
|
||||
mdataValueFromFMCaches (group?.byMetadataField || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING, frontMatterCache, prioFrontMatterCache)
|
||||
if (isSecondaryOrderByMetadata) metadataValueSecondaryToSortBy =
|
||||
mdataValueFromFMCaches (group?.byMetadataFieldSecondary || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING, frontMatterCache, prioFrontMatterCache)
|
||||
if (isDerivedPrimaryByMetadata) metadataValueDerivedPrimaryToSortBy =
|
||||
mdataValueFromFMCaches (spec.byMetadataField || DEFAULT_METADATA_FIELD_FOR_SORTING, frontMatterCache, prioFrontMatterCache)
|
||||
if (isDerivedSecondaryByMetadata) metadataValueDerivedSecondaryToSortBy =
|
||||
mdataValueFromFMCaches (spec.byMetadataFieldSecondary || DEFAULT_METADATA_FIELD_FOR_SORTING, frontMatterCache, prioFrontMatterCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
227
src/main.ts
227
src/main.ts
|
@ -50,44 +50,26 @@ import {
|
|||
getBookmarksPlugin,
|
||||
groupNameForPath
|
||||
} from "./utils/BookmarksCorePluginSignature";
|
||||
import {getIconFolderPlugin} from "./utils/ObsidianIconFolderPluginSignature";
|
||||
import {lastPathComponent} from "./utils/utils";
|
||||
import {
|
||||
getIconFolderPlugin
|
||||
} from "./utils/ObsidianIconFolderPluginSignature";
|
||||
import {
|
||||
extractBasename,
|
||||
lastPathComponent
|
||||
} from "./utils/utils";
|
||||
import {
|
||||
collectSortingAndGroupingTypes,
|
||||
hasOnlyByBookmarkOrStandardObsidian,
|
||||
HasSortingOrGrouping,
|
||||
ImplicitSortspecForBookmarksIntegration
|
||||
} from "./custom-sort/custom-sort-utils";
|
||||
|
||||
interface CustomSortPluginSettings {
|
||||
additionalSortspecFile: string
|
||||
suspended: boolean
|
||||
statusBarEntryEnabled: boolean
|
||||
notificationsEnabled: boolean
|
||||
mobileNotificationsEnabled: boolean
|
||||
automaticBookmarksIntegration: boolean
|
||||
customSortContextSubmenu: boolean
|
||||
bookmarksContextMenus: boolean
|
||||
bookmarksGroupToConsumeAsOrderingReference: string
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: CustomSortPluginSettings = {
|
||||
additionalSortspecFile: '',
|
||||
suspended: true, // if false by default, it would be hard to handle the auto-parse after plugin install
|
||||
statusBarEntryEnabled: true,
|
||||
notificationsEnabled: true,
|
||||
mobileNotificationsEnabled: false,
|
||||
customSortContextSubmenu: true,
|
||||
automaticBookmarksIntegration: false,
|
||||
bookmarksContextMenus: false,
|
||||
bookmarksGroupToConsumeAsOrderingReference: 'sortspec'
|
||||
}
|
||||
|
||||
// On API 1.2.x+ enable the bookmarks integration by default
|
||||
const DEFAULT_SETTING_FOR_1_2_0_UP: Partial<CustomSortPluginSettings> = {
|
||||
automaticBookmarksIntegration: true,
|
||||
bookmarksContextMenus: true
|
||||
}
|
||||
import {
|
||||
CustomSortPluginSettings,
|
||||
CustomSortSettingTab,
|
||||
DEFAULT_SETTING_FOR_1_2_0_UP,
|
||||
DEFAULT_SETTINGS
|
||||
} from "./settings";
|
||||
import {CustomSortPluginAPI} from "./custom-sort-plugin";
|
||||
|
||||
const SORTSPEC_FILE_NAME: string = 'sortspec.md'
|
||||
const SORTINGSPEC_YAML_KEY: string = 'sorting-spec'
|
||||
|
@ -99,7 +81,10 @@ type MonkeyAroundUninstaller = () => void
|
|||
|
||||
type ContextMenuProvider = (item: MenuItem) => void
|
||||
|
||||
export default class CustomSortPlugin extends Plugin {
|
||||
export default class CustomSortPlugin
|
||||
extends Plugin
|
||||
implements CustomSortPluginAPI
|
||||
{
|
||||
settings: CustomSortPluginSettings
|
||||
statusBarItemEl: HTMLElement
|
||||
ribbonIconEl: HTMLElement // On small-screen mobile devices this is useless (ribbon is re-created on-the-fly)
|
||||
|
@ -149,10 +134,14 @@ export default class CustomSortPlugin extends Plugin {
|
|||
if (aFile.name === SORTSPEC_FILE_NAME || // file name == sortspec.md ?
|
||||
aFile.name === `${SORTSPEC_FILE_NAME}.md` || // file name == sortspec.md.md ?
|
||||
aFile.basename === parent.name || // Folder Note mode: inside folder, same name
|
||||
|
||||
aFile.basename === this.settings.additionalSortspecFile || // when user configured _about_
|
||||
aFile.name === this.settings.additionalSortspecFile || // when user configured _about_.md
|
||||
aFile.path === this.settings.additionalSortspecFile || // when user configured Inbox/sort.md
|
||||
aFile.path === `${this.settings.additionalSortspecFile}.md` // when user configured Inbox/sort
|
||||
aFile.path === `${this.settings.additionalSortspecFile}.md` || // when user configured Inbox/sort
|
||||
|
||||
aFile.basename === this.settings.indexNoteNameForFolderNotes || // when user configured as index
|
||||
aFile.name === this.settings.indexNoteNameForFolderNotes // when user configured as index.md
|
||||
) {
|
||||
const sortingSpecTxt: string = mCache.getCache(aFile.path)?.frontmatter?.[SORTINGSPEC_YAML_KEY]
|
||||
// Warning: newer Obsidian versions can return objects as well, hence the explicit check for string value
|
||||
|
@ -737,7 +726,7 @@ export default class CustomSortPlugin extends Plugin {
|
|||
const data: any = await this.loadData() || {}
|
||||
const isFreshInstall: boolean = Object.keys(data).length === 0
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, data);
|
||||
if (requireApiVersion('1.2.0')) {
|
||||
if (requireApiVersion('1.2.0') && isFreshInstall) {
|
||||
this.settings = Object.assign(this.settings, DEFAULT_SETTING_FOR_1_2_0_UP)
|
||||
}
|
||||
}
|
||||
|
@ -745,166 +734,16 @@ export default class CustomSortPlugin extends Plugin {
|
|||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
|
||||
const pathToFlatString = (path: string): string => {
|
||||
return path.replace(/\//g,'_').replace(/\\/g, '_')
|
||||
}
|
||||
// API
|
||||
derivedIndexNoteNameForFolderNotes: string | undefined
|
||||
indexNoteNameForFolderNotesDerivedFrom: any
|
||||
|
||||
class CustomSortSettingTab extends PluginSettingTab {
|
||||
plugin: CustomSortPlugin;
|
||||
|
||||
constructor(app: App, plugin: CustomSortPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
// containerEl.createEl('h2', {text: 'Settings for Custom File Explorer Sorting Plugin'});
|
||||
|
||||
const additionalSortspecFileDescr: DocumentFragment = sanitizeHTMLToDom(
|
||||
'A note name or note path to scan (YAML frontmatter) for sorting specification in addition to the `sortspec` notes and Folder Notes<sup><b>*</b></sup>.'
|
||||
+ '<br>'
|
||||
+ ' The `.md` filename suffix is optional.'
|
||||
+ '<p><b>(*)</b> if you employ the <i>Index-File based</i> approach to folder notes (as documented in '
|
||||
+ '<a href="https://github.com/aidenlx/alx-folder-note/wiki/folder-note-pref"'
|
||||
+ '>Aidenlx Folder Note preferences</a>'
|
||||
+ ') you can enter here the index note name, e.g. <b>_about_</b>'
|
||||
+ '<br>'
|
||||
+ 'The <i>Inside Folder, with Same Name Recommended</i> mode of Folder Notes is handled automatically, no additional configuration needed.'
|
||||
+ '</p>'
|
||||
+ '<p>NOTE: After updating this setting remember to refresh the custom sorting via clicking on the ribbon icon or via the <b>sort-on</b> command'
|
||||
+ ' or by restarting Obsidian or reloading the vault</p>'
|
||||
)
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Path or name of additional note(s) containing sorting specification')
|
||||
.setDesc(additionalSortspecFileDescr)
|
||||
.addText(text => text
|
||||
.setPlaceholder('e.g. _about_')
|
||||
.setValue(this.plugin.settings.additionalSortspecFile)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.additionalSortspecFile = value.trim() ? normalizePath(value) : '';
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable the status bar entry')
|
||||
.setDesc('The status bar entry shows the label `Custom sort:ON` or `Custom sort:OFF`, representing the current state of the plugin.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.statusBarEntryEnabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.statusBarEntryEnabled = value;
|
||||
if (value) {
|
||||
// Enabling
|
||||
if (this.plugin.statusBarItemEl) {
|
||||
// for sanity
|
||||
this.plugin.statusBarItemEl.detach()
|
||||
}
|
||||
this.plugin.statusBarItemEl = this.plugin.addStatusBarItem();
|
||||
this.plugin.updateStatusBar()
|
||||
|
||||
} else { // disabling
|
||||
if (this.plugin.statusBarItemEl) {
|
||||
this.plugin.statusBarItemEl.detach()
|
||||
}
|
||||
}
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable notifications of plugin state changes')
|
||||
.setDesc('The plugin can show notifications about its state changes: e.g. when successfully parsed and applied'
|
||||
+ ' the custom sorting specification, or, when the parsing failed. If the notifications are disabled,'
|
||||
+ ' the only indicator of plugin state is the ribbon button icon. The developer console presents the parsing'
|
||||
+ ' error messages regardless if the notifications are enabled or not.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.notificationsEnabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.notificationsEnabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable notifications of plugin state changes for mobile devices only')
|
||||
.setDesc('See above.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.mobileNotificationsEnabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.mobileNotificationsEnabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable File Explorer context submenu`Custom sort:`')
|
||||
.setDesc('Gives access to operations relevant for custom sorting, e.g. applying custom sorting.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.customSortContextSubmenu)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.customSortContextSubmenu = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
containerEl.createEl('h2', {text: 'Bookmarks integration'});
|
||||
const bookmarksIntegrationDescription: DocumentFragment = sanitizeHTMLToDom(
|
||||
'If enabled, order of files and folders in File Explorer will reflect the order '
|
||||
+ 'of bookmarked items in the bookmarks (core plugin) view. Automatically, without any '
|
||||
+ 'need for sorting configuration. At the same time, it integrates seamlessly with'
|
||||
+ ' <pre style="display: inline;">sorting-spec:</pre> configurations and they can nicely cooperate.'
|
||||
+ '<br>'
|
||||
+ '<p>To separate regular bookmarks from the bookmarks created for sorting, you can put '
|
||||
+ 'the latter in a separate dedicated bookmarks group. The default name of the group is '
|
||||
+ "'<i>" + DEFAULT_SETTINGS.bookmarksGroupToConsumeAsOrderingReference + "</i>' "
|
||||
+ 'and you can change the group name in the configuration field below.'
|
||||
+ '<br>'
|
||||
+ 'If left empty, all the bookmarked items will be used to impose the order in File Explorer.</p>'
|
||||
+ '<p>More information on this functionality in the '
|
||||
+ '<a href="https://github.com/SebastianMC/obsidian-custom-sort/blob/master/docs/manual.md#bookmarks-plugin-integration">'
|
||||
+ 'manual</a> of this custom-sort plugin.'
|
||||
+ '</p>'
|
||||
)
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Automatic integration with core Bookmarks plugin (for indirect drag & drop ordering)')
|
||||
.setDesc(bookmarksIntegrationDescription)
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.automaticBookmarksIntegration)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.automaticBookmarksIntegration = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Name of the group in Bookmarks from which to read the order of items')
|
||||
.setDesc('See above.')
|
||||
.addText(text => text
|
||||
.setPlaceholder('e.g. Group for sorting')
|
||||
.setValue(this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
|
||||
.onChange(async (value) => {
|
||||
value = groupNameForPath(value.trim()).trim()
|
||||
this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference = value ? pathToFlatString(normalizePath(value)) : '';
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
const bookmarksIntegrationContextMenusDescription: DocumentFragment = sanitizeHTMLToDom(
|
||||
'Enable <i>Custom-sort: bookmark for sorting</i> and <i>Custom-sort: bookmark+siblings for sorting</i> (and related) entries '
|
||||
+ 'in context menu in File Explorer'
|
||||
)
|
||||
new Setting(containerEl)
|
||||
.setName('Context menus for Bookmarks integration')
|
||||
.setDesc(bookmarksIntegrationContextMenusDescription)
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.bookmarksContextMenus)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.bookmarksContextMenus = value;
|
||||
if (value) {
|
||||
this.plugin.settings.customSortContextSubmenu = true; // automatically enable custom sort context submenu
|
||||
}
|
||||
await this.plugin.saveSettings();
|
||||
}))
|
||||
indexNoteBasename(): string | undefined {
|
||||
if (!(this.indexNoteNameForFolderNotesDerivedFrom === this.settings.indexNoteNameForFolderNotes)) {
|
||||
this.derivedIndexNoteNameForFolderNotes = extractBasename(this.settings.indexNoteNameForFolderNotes)
|
||||
this.indexNoteNameForFolderNotesDerivedFrom = this.settings.indexNoteNameForFolderNotes
|
||||
}
|
||||
return this.derivedIndexNoteNameForFolderNotes
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
import {App, normalizePath, PluginSettingTab, sanitizeHTMLToDom, Setting} from "obsidian";
|
||||
import {groupNameForPath} from "./utils/BookmarksCorePluginSignature";
|
||||
import CustomSortPlugin from "./main";
|
||||
|
||||
export interface CustomSortPluginSettings {
|
||||
additionalSortspecFile: string
|
||||
indexNoteNameForFolderNotes: string
|
||||
suspended: boolean
|
||||
statusBarEntryEnabled: boolean
|
||||
notificationsEnabled: boolean
|
||||
mobileNotificationsEnabled: boolean
|
||||
automaticBookmarksIntegration: boolean
|
||||
customSortContextSubmenu: boolean
|
||||
bookmarksContextMenus: boolean
|
||||
bookmarksGroupToConsumeAsOrderingReference: string
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: CustomSortPluginSettings = {
|
||||
additionalSortspecFile: '',
|
||||
indexNoteNameForFolderNotes: '',
|
||||
suspended: true, // if false by default, it would be hard to handle the auto-parse after plugin install
|
||||
statusBarEntryEnabled: true,
|
||||
notificationsEnabled: true,
|
||||
mobileNotificationsEnabled: false,
|
||||
customSortContextSubmenu: true,
|
||||
automaticBookmarksIntegration: false,
|
||||
bookmarksContextMenus: false,
|
||||
bookmarksGroupToConsumeAsOrderingReference: 'sortspec'
|
||||
}
|
||||
|
||||
// On API 1.2.x+ enable the bookmarks integration by default
|
||||
export const DEFAULT_SETTING_FOR_1_2_0_UP: Partial<CustomSortPluginSettings> = {
|
||||
automaticBookmarksIntegration: true,
|
||||
bookmarksContextMenus: true
|
||||
}
|
||||
|
||||
const pathToFlatString = (path: string): string => {
|
||||
return path.replace(/\//g,'_').replace(/\\/g, '_')
|
||||
}
|
||||
|
||||
export class CustomSortSettingTab extends PluginSettingTab {
|
||||
plugin: CustomSortPlugin;
|
||||
|
||||
constructor(app: App, plugin: CustomSortPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
// containerEl.createEl('h2', {text: 'Settings for Custom File Explorer Sorting Plugin'});
|
||||
|
||||
const additionalSortspecFileDescr: DocumentFragment = sanitizeHTMLToDom(
|
||||
'A note name or note path to scan (YAML frontmatter) for sorting specification in addition to the `sortspec` notes and Folder Notes.'
|
||||
+ '<br>'
|
||||
+ ' The `.md` filename suffix is optional.'
|
||||
+ '<br>'
|
||||
+ '<p>NOTE: After updating this setting remember to refresh the custom sorting via clicking on the ribbon icon or via the <b>sort-on</b> command'
|
||||
+ ' or by restarting Obsidian or reloading the vault</p>'
|
||||
)
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Path or name of additional note(s) containing sorting specification')
|
||||
.setDesc(additionalSortspecFileDescr)
|
||||
.addText(text => text
|
||||
.setPlaceholder('e.g. sorting-configuration')
|
||||
.setValue(this.plugin.settings.additionalSortspecFile)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.additionalSortspecFile = value.trim() ? normalizePath(value) : '';
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
const indexNoteNameDescr: DocumentFragment = sanitizeHTMLToDom(
|
||||
'If you employ the <i>Index-File based</i> approach to folder notes (as documented in '
|
||||
+ '<a href="https://github.com/aidenlx/alx-folder-note/wiki/folder-note-pref"'
|
||||
+ '>Aidenlx Folder Note preferences</a>'
|
||||
+ ') enter here the index note name, e.g. <b>_about_</b> or <b>index</b>'
|
||||
+ '<br>'
|
||||
+ ' The `.md` filename suffix is optional.'
|
||||
+ '<br>'
|
||||
+ 'This will tell the plugin to read sorting specs and also folders metadata from these files.'
|
||||
+ '<br></br>'
|
||||
+ 'The <i>Inside Folder, with Same Name Recommended</i> mode of Folder Notes is handled automatically, no additional configuration needed.'
|
||||
+ '</p>'
|
||||
+ '<p>NOTE: After updating this setting remember to refresh the custom sorting via clicking on the ribbon icon or via the <b>sort-on</b> command'
|
||||
+ ' or by restarting Obsidian or reloading the vault</p>'
|
||||
)
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Name of index note (Folder Notes support)')
|
||||
.setDesc(indexNoteNameDescr)
|
||||
.addText(text => text
|
||||
.setPlaceholder('e.g. _about_ or index')
|
||||
.setValue(this.plugin.settings.indexNoteNameForFolderNotes)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.indexNoteNameForFolderNotes = value.trim() ? normalizePath(value) : '';
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable the status bar entry')
|
||||
.setDesc('The status bar entry shows the label `Custom sort:ON` or `Custom sort:OFF`, representing the current state of the plugin.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.statusBarEntryEnabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.statusBarEntryEnabled = value;
|
||||
if (value) {
|
||||
// Enabling
|
||||
if (this.plugin.statusBarItemEl) {
|
||||
// for sanity
|
||||
this.plugin.statusBarItemEl.detach()
|
||||
}
|
||||
this.plugin.statusBarItemEl = this.plugin.addStatusBarItem();
|
||||
this.plugin.updateStatusBar()
|
||||
|
||||
} else { // disabling
|
||||
if (this.plugin.statusBarItemEl) {
|
||||
this.plugin.statusBarItemEl.detach()
|
||||
}
|
||||
}
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable notifications of plugin state changes')
|
||||
.setDesc('The plugin can show notifications about its state changes: e.g. when successfully parsed and applied'
|
||||
+ ' the custom sorting specification, or, when the parsing failed. If the notifications are disabled,'
|
||||
+ ' the only indicator of plugin state is the ribbon button icon. The developer console presents the parsing'
|
||||
+ ' error messages regardless if the notifications are enabled or not.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.notificationsEnabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.notificationsEnabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable notifications of plugin state changes for mobile devices only')
|
||||
.setDesc('See above.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.mobileNotificationsEnabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.mobileNotificationsEnabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable File Explorer context submenu`Custom sort:`')
|
||||
.setDesc('Gives access to operations relevant for custom sorting, e.g. applying custom sorting.')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.customSortContextSubmenu)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.customSortContextSubmenu = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
containerEl.createEl('h2', {text: 'Bookmarks integration'});
|
||||
const bookmarksIntegrationDescription: DocumentFragment = sanitizeHTMLToDom(
|
||||
'If enabled, order of files and folders in File Explorer will reflect the order '
|
||||
+ 'of bookmarked items in the bookmarks (core plugin) view. Automatically, without any '
|
||||
+ 'need for sorting configuration. At the same time, it integrates seamlessly with'
|
||||
+ ' <pre style="display: inline;">sorting-spec:</pre> configurations and they can nicely cooperate.'
|
||||
+ '<br>'
|
||||
+ '<p>To separate regular bookmarks from the bookmarks created for sorting, you can put '
|
||||
+ 'the latter in a separate dedicated bookmarks group. The default name of the group is '
|
||||
+ "'<i>" + DEFAULT_SETTINGS.bookmarksGroupToConsumeAsOrderingReference + "</i>' "
|
||||
+ 'and you can change the group name in the configuration field below.'
|
||||
+ '<br>'
|
||||
+ 'If left empty, all the bookmarked items will be used to impose the order in File Explorer.</p>'
|
||||
+ '<p>More information on this functionality in the '
|
||||
+ '<a href="https://github.com/SebastianMC/obsidian-custom-sort/blob/master/docs/manual.md#bookmarks-plugin-integration">'
|
||||
+ 'manual</a> of this custom-sort plugin.'
|
||||
+ '</p>'
|
||||
)
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Automatic integration with core Bookmarks plugin (for indirect drag & drop ordering)')
|
||||
.setDesc(bookmarksIntegrationDescription)
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.automaticBookmarksIntegration)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.automaticBookmarksIntegration = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Name of the group in Bookmarks from which to read the order of items')
|
||||
.setDesc('See above.')
|
||||
.addText(text => text
|
||||
.setPlaceholder('e.g. Group for sorting')
|
||||
.setValue(this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference)
|
||||
.onChange(async (value) => {
|
||||
value = groupNameForPath(value.trim()).trim()
|
||||
this.plugin.settings.bookmarksGroupToConsumeAsOrderingReference = value ? pathToFlatString(normalizePath(value)) : '';
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
const bookmarksIntegrationContextMenusDescription: DocumentFragment = sanitizeHTMLToDom(
|
||||
'Enable <i>Custom-sort: bookmark for sorting</i> and <i>Custom-sort: bookmark+siblings for sorting</i> (and related) entries '
|
||||
+ 'in context menu in File Explorer'
|
||||
)
|
||||
new Setting(containerEl)
|
||||
.setName('Context menus for Bookmarks integration')
|
||||
.setDesc(bookmarksIntegrationContextMenusDescription)
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.bookmarksContextMenus)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.bookmarksContextMenus = value;
|
||||
if (value) {
|
||||
this.plugin.settings.customSortContextSubmenu = true; // automatically enable custom sort context submenu
|
||||
}
|
||||
await this.plugin.saveSettings();
|
||||
}))
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
lastPathComponent,
|
||||
extractParentFolderPath
|
||||
extractParentFolderPath,
|
||||
extractBasename
|
||||
} from "../../utils/utils";
|
||||
|
||||
describe('lastPathComponent and extractParentFolderPath', () => {
|
||||
|
@ -25,3 +26,20 @@ describe('lastPathComponent and extractParentFolderPath', () => {
|
|||
}
|
||||
)
|
||||
})
|
||||
|
||||
describe('extractBasename', () => {
|
||||
const params: Array<(string|undefined)[]> = [
|
||||
// Obvious
|
||||
['index', 'index'],
|
||||
['index.md', 'index'],
|
||||
// Edge cases
|
||||
['',''],
|
||||
[undefined,undefined],
|
||||
['.','.'],
|
||||
['.md',''],
|
||||
['.md.md','.md']
|
||||
];
|
||||
it.each(params)('>%s< should result in %s', (s: string|undefined, out: string|undefined) => {
|
||||
expect(extractBasename(s)).toBe(out)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -16,3 +16,11 @@ export function extractParentFolderPath(path: string): string {
|
|||
const lastPathSeparatorIdx = (path ?? '').lastIndexOf('/')
|
||||
return lastPathSeparatorIdx > 0 ? path.substring(0, lastPathSeparatorIdx) : ''
|
||||
}
|
||||
|
||||
export function extractBasename (configEntry: string | undefined): string | undefined {
|
||||
if (typeof configEntry === 'string' && configEntry.endsWith('.md')) {
|
||||
return configEntry.slice(0, -'.md'.length)
|
||||
} else {
|
||||
return configEntry
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue