Fix eslint issues 1/2

This commit is contained in:
Paul 2026-05-15 15:40:29 +02:00
parent 2683ee1a1a
commit 1e99850592
38 changed files with 5759 additions and 648 deletions

View file

@ -31,7 +31,6 @@ esbuild.build({
'@lezer/common',
'@lezer/highlight',
'@lezer/lr',
"f:/Obsidian/test/.obsidian/plugins/obsidian-folder-notes/node_modules/obsidian",
...builtins],
format: 'cjs',
watch: !prod,

View file

@ -1,17 +1,61 @@
import tseslint from "typescript-eslint";
import obsidianmd from "eslint-plugin-obsidianmd";
import json from "@eslint/json";
export default [
...obsidianmd.configs.recommended,
{
files: ["manifest.json"],
language: "json/json",
plugins: {
json
},
rules: {
"no-irregular-whitespace": "off",
"obsidianmd/no-plugin-as-component": "off",
"obsidianmd/no-view-references-in-plugin": "off",
"obsidianmd/no-unsupported-api": "off",
"obsidianmd/prefer-file-manager-trash-file": "off",
"obsidianmd/prefer-instanceof": "off",
"obsidianmd/validate-manifest": "error"
}
},
{
files: ["package.json"],
rules: {
"no-irregular-whitespace": "off",
"depend/ban-dependencies": "off",
"obsidianmd/no-plugin-as-component": "off",
"obsidianmd/no-view-references-in-plugin": "off",
"obsidianmd/no-unsupported-api": "off",
"obsidianmd/prefer-file-manager-trash-file": "off",
"obsidianmd/prefer-instanceof": "off"
}
},
{
files: ["**/*.{js,mjs,cjs}"],
...tseslint.configs.disableTypeChecked,
rules: {
...tseslint.configs.disableTypeChecked.rules,
"obsidianmd/no-plugin-as-component": "off",
"obsidianmd/no-view-references-in-plugin": "off",
"obsidianmd/no-unsupported-api": "off",
"obsidianmd/prefer-file-manager-trash-file": "off",
"obsidianmd/prefer-instanceof": "off"
}
},
{
files: ["**/*.ts"],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
sourceType: "module"
sourceType: "module",
project: "./tsconfig.json"
}
},
ignores: [
"**/node_modules/**",
"**/dist/**",
"**/dist/**"
],
plugins: {
"@typescript-eslint": tseslint.plugin
@ -143,7 +187,7 @@ export default [
],
'max-len': [
'warn', {
code: 100,
code: 110,
ignoreComments: true,
}
],

View file

@ -1,12 +1,12 @@
{
"id": "folder-notes",
"name": "Folder notes",
"version": "1.8.19",
"minAppVersion": "0.15.0",
"description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.",
"author": "Lost Paul",
"authorUrl": "https://github.com/LostPaul",
"fundingUrl": "https://ko-fi.com/paul305844",
"helpUrl": "https://lostpaul.github.io/obsidian-folder-notes/",
"isDesktopOnly": false
}
"id": "folder-notes",
"name": "Folder notes",
"version": "1.8.21",
"minAppVersion": "1.4.10",
"description": "Create notes within folders that can be accessed without collapsing the folder, similar to the functionality offered in Notion.",
"author": "Lost Paul",
"authorUrl": "https://github.com/LostPaul",
"fundingUrl": "https://ko-fi.com/paul305844",
"helpUrl": "https://lostpaul.github.io/obsidian-folder-notes/",
"isDesktopOnly": false
}

5500
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,7 @@
"builtin-modules": "3.3.0",
"esbuild": "0.14.47",
"eslint": "^9.32.0",
"eslint-plugin-obsidianmd": "^0.3.0",
"front-matter-plugin-api-provider": "^0.1.4-alpha",
"globals": "^16.3.0",
"jiti": "^2.5.1",

View file

@ -8,6 +8,7 @@ import {
type TAbstractFile,
type Editor,
type MarkdownView,
type MarkdownFileInfo,
} from 'obsidian';
import type FolderNotesPlugin from './main';
import {
@ -31,6 +32,7 @@ import {
showFolderNoteInFileExplorer,
} from './functions/styleFunctions';
type MarkdownEditorContext = MarkdownView | MarkdownFileInfo;
export class Commands {
@ -60,7 +62,7 @@ export class Commands {
const folderNote = getFolderNote(this.plugin, folder.path);
if (folderNote instanceof TFile && folderNote === file) return false;
if (checking) return true;
turnIntoFolderNote(this.plugin, file, folder, folderNote);
void turnIntoFolderNote(this.plugin, file, folder, folderNote);
},
});
@ -80,19 +82,19 @@ export class Commands {
const automaticallyCreateFolderNote =
this.plugin.settings.autoCreate;
this.plugin.settings.autoCreate = false;
this.plugin.saveSettings();
void this.plugin.saveSettings();
await this.plugin.app.vault.createFolder(newPath);
const folder = this.plugin.app.vault.getAbstractFileByPath(newPath);
if (!(folder instanceof TFolder)) return;
createFolderNote(this.plugin, folder.path, true, '.' + file.extension, false, file);
await createFolderNote(this.plugin, folder.path, true, '.' + file.extension, false, file);
this.plugin.settings.autoCreate = automaticallyCreateFolderNote;
this.plugin.saveSettings();
void this.plugin.saveSettings();
},
});
this.plugin.addCommand({
id: 'create-folder-note-for-current-folder',
name: 'Create markdown folder note for this folder',
name: 'Create Markdown folder note for this folder',
checkCallback: (checking) => {
const file = this.app.workspace.getActiveFile();
if (!(file instanceof TFile)) return false;
@ -100,7 +102,7 @@ export class Commands {
if (!(folder instanceof TFolder)) return false;
if (folder.path === '' || folder.path === '/') return false;
if (checking) return true;
createFolderNote(this.plugin, folder.path, true, '.md', false);
void createFolderNote(this.plugin, folder.path, true, '.md', false);
},
});
@ -116,7 +118,7 @@ export class Commands {
if (!(folder instanceof TFolder)) return false;
if (folder.path === '' || folder.path === '/') return false;
if (checking) return true;
createFolderNote(this.plugin, folder.path, true, '.' + fileType, false);
void createFolderNote(this.plugin, folder.path, true, '.' + fileType, false);
},
});
});
@ -136,7 +138,7 @@ export class Commands {
// Everything is fine and not checking, let's create the folder note.
const ext = '.' + fileType;
const { path } = folder;
createFolderNote(this.plugin, path, true, ext, false);
void createFolderNote(this.plugin, path, true, ext, false);
},
});
});
@ -152,7 +154,7 @@ export class Commands {
const folderNote = getFolderNote(this.plugin, folder.path);
if (!(folderNote instanceof TFile)) return false;
if (checking) return true;
deleteFolderNote(this.plugin, folderNote, true);
void deleteFolderNote(this.plugin, folderNote, true);
},
});
@ -168,7 +170,7 @@ export class Commands {
if (checking) return true;
// Everything is fine and not checking, let's delete the folder note.
deleteFolderNote(this.plugin, folderNote, true);
void deleteFolderNote(this.plugin, folderNote, true);
},
});
this.plugin.addCommand({
@ -182,7 +184,7 @@ export class Commands {
const folderNote = getFolderNote(this.plugin, folder.path);
if (!(folderNote instanceof TFile)) return false;
if (checking) return true;
openFolderNote(this.plugin, folderNote);
void openFolderNote(this.plugin, folderNote);
},
});
this.plugin.addCommand({
@ -197,14 +199,14 @@ export class Commands {
if (checking) return true;
// Everything is fine and not checking, let's open the folder note.
openFolderNote(this.plugin, folderNote);
void openFolderNote(this.plugin, folderNote);
},
});
this.plugin.addCommand({
id: 'create-folder-note-from-selected-text',
name: 'Create folder note from selection',
editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView) => {
editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownEditorContext) => {
const text = editor.getSelection().trim();
const { file } = view;
if (!(file instanceof TFile)) return false;
@ -230,8 +232,8 @@ export class Commands {
new Notice('Folder note already exists');
return false;
}
this.plugin.app.vault.createFolder(text);
createFolderNote(this.plugin, text, false);
void this.plugin.app.vault.createFolder(text);
void createFolderNote(this.plugin, text, false);
} else {
const folderFullPath = folderPath + '/' + text;
@ -250,8 +252,8 @@ export class Commands {
return false;
}
}
this.plugin.app.vault.createFolder(folderPath + '/' + text);
createFolderNote(this.plugin, folderPath + '/' + text, false);
void this.plugin.app.vault.createFolder(folderPath + '/' + text);
void createFolderNote(this.plugin, folderPath + '/' + text, false);
}
const { folderNoteName } = this.plugin.settings;
@ -317,7 +319,7 @@ export class Commands {
const automaticallyCreateFolderNote =
this.plugin.settings.autoCreate;
this.plugin.settings.autoCreate = false;
this.plugin.saveSettings();
void this.plugin.saveSettings();
await this.plugin.app.vault.createFolder(newPath);
const newFolder = this.plugin.app.vault
.getAbstractFileByPath(newPath);
@ -331,7 +333,7 @@ export class Commands {
file,
);
this.plugin.settings.autoCreate = automaticallyCreateFolderNote;
this.plugin.saveSettings();
void this.plugin.saveSettings();
});
});
@ -347,7 +349,7 @@ export class Commands {
item.onClick(() => {
if (!folder || !(folder instanceof TFolder)) return;
const folderNote = getFolderNote(this.plugin, folder.path);
turnIntoFolderNote(this.plugin, file, folder, folderNote);
void turnIntoFolderNote(this.plugin, file, folder, folderNote);
});
});
}
@ -380,7 +382,7 @@ export class Commands {
(excluded) =>
(excluded.path !== file.path) || excluded.detached,
);
this.plugin.saveSettings(true);
void this.plugin.saveSettings(true);
new Notice('Successfully removed folder from excluded folders');
});
});
@ -393,7 +395,7 @@ export class Commands {
item.setTitle('Remove folder from detached folders');
item.setIcon('trash');
item.onClick(() => {
deleteExcludedFolder(this.plugin, detachedExcludedFolder);
void deleteExcludedFolder(this.plugin, detachedExcludedFolder);
});
});
}
@ -411,7 +413,7 @@ export class Commands {
this.plugin,
);
this.plugin.settings.excludeFolders.push(newExcludedFolder);
this.plugin.saveSettings(true);
void this.plugin.saveSettings(true);
new Notice('Successfully excluded folder from folder notes');
});
});
@ -425,7 +427,7 @@ export class Commands {
item.setTitle('Delete folder note');
item.setIcon('trash');
item.onClick(() => {
deleteFolderNote(this.plugin, folderNote, true);
void deleteFolderNote(this.plugin, folderNote, true);
});
});
@ -433,7 +435,7 @@ export class Commands {
item.setTitle('Open folder note');
item.setIcon('chevron-right-square');
item.onClick(() => {
openFolderNote(this.plugin, folderNote);
void openFolderNote(this.plugin, folderNote);
});
});
@ -474,10 +476,10 @@ export class Commands {
}
} else {
folderMenu.addItem((item) => {
item.setTitle('Create markdown folder note');
item.setTitle('Create Markdown folder note');
item.setIcon('edit');
item.onClick(() => {
createFolderNote(this.plugin, file.path, true, '.md');
void createFolderNote(this.plugin, file.path, true, '.md');
});
});
@ -487,7 +489,7 @@ export class Commands {
item.setTitle(`Create ${fileType} folder note`);
item.setIcon('edit');
item.onClick(() => {
createFolderNote(this.plugin, file.path, true, '.' + fileType);
void createFolderNote(this.plugin, file.path, true, '.' + fileType);
});
});
});
@ -500,8 +502,8 @@ export class Commands {
this.plugin.settings.useSubmenus
) {
menu.addItem(async (item) => {
item.setTitle('Folder Note Commands').setIcon('folder-edit');
let subMenu: Menu = item.setSubmenu() as Menu;
item.setTitle('Folder note commands').setIcon('folder-edit');
let subMenu: Menu = item.setSubmenu();
addFolderNoteActions(subMenu);
});
} else {
@ -512,7 +514,7 @@ export class Commands {
editorCommands(): void {
// eslint-disable-next-line max-len
this.plugin.registerEvent(this.plugin.app.workspace.on('editor-menu', (menu: Menu, editor: Editor, view: MarkdownView) => {
this.plugin.registerEvent(this.plugin.app.workspace.on('editor-menu', (menu: Menu, editor: Editor, view: MarkdownEditorContext) => {
const text = editor.getSelection().trim();
if (!text || text.trim() === '') return;
menu.addItem((item) => {
@ -543,8 +545,8 @@ export class Commands {
if (folder instanceof TFolder) {
return new Notice('Folder note already exists');
}
this.plugin.app.vault.createFolder(text);
createFolderNote(this.plugin, text, false);
void this.plugin.app.vault.createFolder(text);
void createFolderNote(this.plugin, text, false);
} else {
folder = this.plugin.app.vault.getAbstractFileByPath(
@ -565,8 +567,8 @@ export class Commands {
return new Notice('File already exists');
}
}
this.plugin.app.vault.createFolder(folderPath + '/' + text);
createFolderNote(this.plugin, folderPath + '/' + text, false);
void this.plugin.app.vault.createFolder(folderPath + '/' + text);
void createFolderNote(this.plugin, folderPath + '/' + text, false);
}
if (fileName !== text) {
editor.replaceSelection(`[[${fileName}]]`);

View file

@ -49,9 +49,9 @@ function aggregateFlags(
for (const property of propertiesToCopy) {
const value = (matchedFolder as Partial<ExcludedFolder>)[property];
if (value === true) {
(result as Partial<ExcludedFolder>)[property] = true as never;
(result)[property] = true as never;
} else if (!value) {
(result as Partial<ExcludedFolder>)[property] = false as never;
(result)[property] = false as never;
}
}
}
@ -117,7 +117,7 @@ export function getExcludedFolder(
const whitelist = getWhitelistedFolder(
plugin,
path,
) as WhitelistedFolder | WhitelistedPattern | undefined;
);
let skipWhitelist = ignoreWhitelist ?? false;
if (excluded?.detached) skipWhitelist = true;

View file

@ -25,7 +25,7 @@ export default class PatternSettings extends Modal {
new Setting(contentEl)
.setName('Disable folder name sync')
// eslint-disable-next-line max-len
.setDesc('Choose if the folder name should be renamed when the file name has been changed')
.addToggle((toggle) =>
toggle

View file

@ -34,7 +34,7 @@ export default class WhitelistFolderSettings extends Modal {
new Setting(contentEl)
.setName('Enable folder name sync')
// eslint-disable-next-line max-len
.setDesc('Choose if the name of a folder note should be renamed when the folder name is changed')
.addToggle((toggle) =>
toggle

View file

@ -23,7 +23,7 @@ export default class WhitelistPatternSettings extends Modal {
contentEl.createEl('h2', { text: 'Whitelisted pattern settings' });
new Setting(contentEl)
.setName('Enable folder name sync')
// eslint-disable-next-line max-len
.setDesc('Choose if the name of a folder note should be renamed when the folder name is changed')
.addToggle((toggle) =>
toggle

View file

@ -43,10 +43,10 @@ function initializeFolderNoteFeatures(plugin: FolderNotesPlugin): void {
}
function initializeBreadcrumbs(plugin: FolderNotesPlugin): void {
const titleContainers = document.querySelectorAll('.view-header-title-container');
const titleContainers = activeDocument.querySelectorAll('.view-header-title-container');
if (!titleContainers.length) return;
titleContainers.forEach((container) => {
if (!(container instanceof HTMLElement)) return;
if (!(container.instanceOf(HTMLElement))) return;
scheduleIdle(() => updateFolderNamesInPath(plugin, container), { timeout: 1000 });
});
}
@ -62,7 +62,7 @@ function observeFolderTitleMutations(plugin: FolderNotesPlugin): void {
fileExplorerMutationObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of Array.from(mutation.addedNodes)) {
if (!(node instanceof HTMLElement)) continue;
if (!(node.instanceOf(HTMLElement))) continue;
processAddedFolders(node, plugin);
}
}
@ -72,7 +72,7 @@ function observeFolderTitleMutations(plugin: FolderNotesPlugin): void {
}
function initializeAllFolderTitles(plugin: FolderNotesPlugin): void {
const allTitles = document.querySelectorAll('.nav-folder-title-content');
const allTitles = activeDocument.querySelectorAll('.nav-folder-title-content');
for (const title of Array.from(allTitles)) {
const folderTitle = title as HTMLElement;
const folderEl = folderTitle.closest('.nav-folder-title');
@ -97,7 +97,7 @@ function processAddedFolders(node: HTMLElement, plugin: FolderNotesPlugin): void
const folderPath = folderEl?.getAttribute('data-path') || '';
const RETRY_TIMEOUT = 50;
if (!folderEl || !folderPath) {
setTimeout(() => {
window.setTimeout(() => {
const retryFolderEl = folderTitle.closest('.nav-folder-title');
const retryFolderPath = retryFolderEl?.getAttribute('data-path') || '';
if (retryFolderEl && retryFolderPath) {
@ -171,7 +171,7 @@ async function updateFolderNamesInPath(
}
// eslint-disable-next-line complexity
titleParent?.childNodes.forEach(async (breadcrumb: HTMLElement) => {
if (!(breadcrumb instanceof HTMLElement)) return;
if (!(breadcrumb.instanceOf(HTMLElement))) return;
if (breadcrumb.classList.contains('view-header-breadcrumb-separator')) {
if (breadcrumb.nextSibling === null) {
breadcrumb.classList.add('is-last-separator');
@ -179,7 +179,7 @@ async function updateFolderNamesInPath(
return;
}
path += breadcrumb.getAttribute('old-name') ?? (breadcrumb as HTMLElement).innerText.trim();
path += breadcrumb.getAttribute('old-name') ?? (breadcrumb).innerText.trim();
path += '/';
const folderPath = path.slice(0, -TRAILING_SLASH_LENGTH);
@ -206,7 +206,7 @@ async function updateFolderNamesInPath(
breadcrumb?.setAttribute('data-path', path.slice(0, -TRAILING_SLASH_LENGTH));
if (!breadcrumb.onclick) {
breadcrumb.addEventListener('click', (e) => {
handleViewHeaderClick(e as MouseEvent, plugin);
handleViewHeaderClick(e, plugin);
}, { capture: true });
}
@ -230,6 +230,6 @@ function scheduleIdle(callback: () => void, options?: { timeout: number }): void
};
windowWithIdle.requestIdleCallback(callback, options);
} else {
setTimeout(callback, options?.timeout || DEFAULT_IDLE_TIMEOUT);
window.setTimeout(callback, options?.timeout || DEFAULT_IDLE_TIMEOUT);
}
}

View file

@ -33,8 +33,8 @@ export async function handleViewHeaderClick(
} else if (event.altKey || Keymap.isModEvent(event) === 'tab') {
if (await handleFolderNoteCreation(event, plugin, folderPath)) return;
}
(event.target as HTMLElement).onclick = null;
(event.target as HTMLElement).click();
(event.target).onclick = null;
(event.target).click();
}
async function isExcludedFolder(
@ -58,7 +58,7 @@ async function handleFolderNoteReveal(plugin: FolderNotesPlugin, folderNote: TFi
const fileExplorerPlugin = plugin.app.internalPlugins.getEnabledPluginById('file-explorer');
if (fileExplorerPlugin && Platform.isMobile && plugin.settings.openSidebar.mobile) {
const OPEN_SIDEBAR_DELAY = 200;
setTimeout(() => { fileExplorerPlugin.revealInFolder(folderNote); }, OPEN_SIDEBAR_DELAY);
window.setTimeout(() => { fileExplorerPlugin.revealInFolder(folderNote); }, OPEN_SIDEBAR_DELAY);
} else if (fileExplorerPlugin && Platform.isDesktop && plugin.settings.openSidebar.desktop) {
fileExplorerPlugin.revealInFolder(folderNote);
}

View file

@ -78,6 +78,6 @@ async function handleFolderCreation(folder: TFolder, plugin: FolderNotesPlugin):
const folderNote = getFolderNote(plugin, folder.path);
if (folderNote) return;
createFolderNote(plugin, folder.path, openFile, undefined, true);
void createFolderNote(plugin, folder.path, openFile, undefined, true);
addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin);
}

View file

@ -4,9 +4,9 @@ export class ListComponent {
emitter: CustomEventEmitter;
containerEl: HTMLElement;
controlEl: HTMLElement;
emptyStateEl: HTMLElement;
emptyStateEl!: HTMLElement;
listEl: HTMLElement;
values: string[];
values!: string[];
defaultValues: string[];
constructor(containerEl: HTMLElement, values: string[] = [], defaultValues: string[] = []) {
this.emitter = new CustomEventEmitter();
@ -48,17 +48,23 @@ export class ListComponent {
addElement(value: string): void {
this.listEl.createSpan('setting-hotkey', (span) => {
if (value.toLocaleLowerCase() === 'md') {
span.innerText = 'markdown';
const markdown = 'markdown';
span.innerText = markdown;
} else {
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;
const parser = new DOMParser();
const svgDoc = parser.parseFromString(svg, 'image/svg+xml');
const svgNode = svgDoc.documentElement;
if (svgNode) {
svgElement.appendChild(activeDocument.importNode(svgNode, true));
}
removeSpan.onClickEvent(() => {
this.removeValue(value);
span.remove();
@ -78,7 +84,13 @@ export class ListComponent {
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;
const svgElement = resetButton.createEl('span', { cls: 'ofn-list-item-remove-icon' });
const parser = new DOMParser();
const svgDoc = parser.parseFromString(svg, 'image/svg+xml');
const svgNode = svgDoc.documentElement;
if (svgNode) {
svgElement.appendChild(activeDocument.importNode(svgNode, true));
}
resetButton.onClickEvent(() => {
this.setValues(this.defaultValues);
});

View file

@ -176,7 +176,7 @@ async function handleCreateFolderNote(plugin: FolderNotesPlugin, folderNoteType:
if (
extension === '.excalidraw' &&
!content.includes(
'==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==',
'==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this activeDocument. ⚠==',
)
) {
content = await getDefaultTemplate(plugin.app);
@ -303,12 +303,12 @@ export async function turnIntoFolderNote(
}
if (detachedExcludedFolder) {
deleteExcludedFolder(plugin, detachedExcludedFolder);
void deleteExcludedFolder(plugin, detachedExcludedFolder);
}
await plugin.app.fileManager.renameFile(file, path);
addCSSClassToFileExplorerEl(path, 'is-folder-note', false, plugin, true);
addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin);
void addCSSClassToFileExplorerEl(path, 'is-folder-note', false, plugin, true);
void addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin);
removeActiveFolder(plugin);
setActiveFolder(folder.path, plugin);
@ -502,7 +502,7 @@ export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile): void {
excludedFolder.detached = true;
excludedFolder.detachedFilePath = file.path;
addExcludedFolder(plugin, excludedFolder);
updateCSSClassesForFolderNote(file.path, plugin);
void updateCSSClassesForFolderNote(file.path, plugin);
}

View file

@ -151,7 +151,7 @@ export async function addCSSClassToFileExplorerEl(
if (!fileExplorerItem) {
if (waitForCreate && count < MAX_RETRIES) {
await new Promise((r) => setTimeout(r, RETRY_DELAY));
await new Promise((r) => window.setTimeout(r, RETRY_DELAY));
addCSSClassToFileExplorerEl(path, cssClass, parent, plugin, waitForCreate, count + 1);
return;
}
@ -164,7 +164,7 @@ export async function addCSSClassToFileExplorerEl(
}
} else {
fileExplorerItem.addClass(cssClass);
document.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => {
activeDocument.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => {
item.addClass(cssClass);
});
}
@ -183,7 +183,7 @@ export function removeCSSClassFromFileExplorerEL(
): void {
if (!path) return;
const fileExplorerItem = getFileExplorerElement(path, plugin);
document.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => {
activeDocument.querySelectorAll(`[data-path='${CSS.escape(path)}']`).forEach((item) => {
item.removeClass(cssClass);
});
if (!fileExplorerItem) { return; }

View file

@ -80,7 +80,7 @@ export function getFileExplorerActiveFolder(): TFolder | null {
const activeFileOrFolder =
fe.tree.focusedItem?.file ?? fe.activeDom?.file;
if (!(activeFileOrFolder instanceof TFolder)) return null;
return activeFileOrFolder as TFolder;
return activeFileOrFolder;
}
export function getFileExplorerActiveFolderNote(): TFile | null {

View file

@ -1,5 +1,5 @@
import {
type TAbstractFile,
type App, type TAbstractFile,
type MarkdownPostProcessorContext,
type WorkspaceLeaf,
Plugin, TFile, TFolder,
@ -9,6 +9,7 @@ import {
import {
type FolderNotesSettings, DEFAULT_SETTINGS, SettingsTab,
} from './settings/SettingsTab';
import { FolderOverview, type defaultOverviewSettings } from './obsidian-folder-overview/src/FolderOverview';
import { Commands } from './Commands';
import type { FileExplorerWorkspaceLeaf } from './globals';
import {
@ -21,7 +22,6 @@ import {
import { handleCreate } from './events/handleCreate';
import { FrontMatterTitlePluginHandler } from './events/FrontMatterTitle';
import { FolderOverviewSettings } from './obsidian-folder-overview/src/modals/Settings';
import { FolderOverview } from './obsidian-folder-overview/src/FolderOverview';
import { TabManager } from './events/TabManager';
import './functions/ListComponent';
import { handleDelete } from './events/handleDelete';
@ -36,26 +36,65 @@ import { updateOverviewView, updateViewDropdown } from './obsidian-folder-overvi
import { FvIndexDB } from './obsidian-folder-overview/src/utils/IndexDB';
import { updateAllOverviews } from './obsidian-folder-overview/src/utils/functions';
interface FileExplorerPluginLike extends Plugin {
revealInFolder: (file: TAbstractFile) => void;
}
interface DragManagerLike {
draggable?: {
file?: TAbstractFile;
type?: string;
} | null;
setAction(action: string): void;
}
interface ClipboardManagerLike {
app: App & {
dragManager?: DragManagerLike;
};
handleDragOver: (evt: DragEvent, ...args: unknown[]) => void;
handleDrop: (evt: DragEvent, ...args: unknown[]) => void;
}
interface EditModeLike {
clipboardManager: ClipboardManagerLike;
}
interface ViewWithEditModes {
editMode?: EditModeLike;
sourceMode?: EditModeLike;
}
interface ActiveEditorLike {
editMode?: EditModeLike;
}
type LegacySettingsData = Partial<FolderNotesSettings> & {
allowWhitespaceCollapsing?: boolean;
defaultOverview?: defaultOverviewSettings;
};
export default class FolderNotesPlugin extends Plugin {
settings: FolderNotesSettings;
settingsTab: SettingsTab;
activeFolderDom: HTMLElement | null;
activeFileExplorer: FileExplorerWorkspaceLeaf;
settings!: FolderNotesSettings;
settingsTab!: SettingsTab;
activeFolderDom!: HTMLElement | null;
activeFileExplorer!: FileExplorerWorkspaceLeaf;
fmtpHandler: FrontMatterTitlePluginHandler | null = null;
hoveredElement: HTMLElement | null = null;
mouseEvent: MouseEvent | null = null;
hoverLinkTriggered = false;
tabManager: TabManager;
tabManager!: TabManager;
settingsOpened = false;
askModalCurrentlyOpen = false;
fvIndexDB: FvIndexDB;
fvIndexDB!: FvIndexDB;
async onload(): Promise<void> {
// eslint-disable-next-line obsidianmd/rule-custom-message
console.log('loading folder notes plugin');
await this.loadSettings();
this.settingsTab = new SettingsTab(this.app, this);
this.addSettingTab(this.settingsTab);
this.saveSettings();
await this.saveSettings();
this.fvIndexDB = new FvIndexDB(this);
// Add CSS Classes
@ -64,7 +103,7 @@ export default class FolderNotesPlugin extends Plugin {
new Commands(this.app, this).registerCommands();
registerOverviewCommands(this);
this.app.workspace.onLayoutReady(this.onLayoutReady.bind(this));
this.app.workspace.onLayoutReady(() => this.onLayoutReady());
if (!this.settings.persistentSettingsTab.afterRestart) {
this.settings.settingsTab = 'general';
@ -93,7 +132,7 @@ export default class FolderNotesPlugin extends Plugin {
this.hoverLinkTriggered = true;
});
this.registerEvent(this.app.workspace.on('file-open', async (openFile: TFile | null) => {
this.registerEvent(this.app.workspace.on('file-open', (openFile: TFile | null) => {
removeActiveFolder(this);
if (!openFile || !openFile.basename) { return; }
@ -109,7 +148,9 @@ export default class FolderNotesPlugin extends Plugin {
}));
this.registerEvent(this.app.vault.on('create', (file: TAbstractFile) => {
handleCreate(file, this);
handleCreate(file, this).catch((err) => {
console.error(err); new Notice('Error handling file creation');
});
this.handleVaultChange();
}));
@ -132,39 +173,43 @@ export default class FolderNotesPlugin extends Plugin {
}
addSettingCssClasses(): void {
document.body.classList.add('folder-notes-plugin');
if (this.settings.hideFolderNote) { document.body.classList.add('hide-folder-note'); }
activeDocument.body.classList.add('folder-notes-plugin');
if (this.settings.hideFolderNote) { activeDocument.body.classList.add('hide-folder-note'); }
if (this.settings.hideCollapsingIconForEmptyFolders) {
document.body.classList.add('fn-hide-empty-collapse-icon');
activeDocument.body.classList.add('fn-hide-empty-collapse-icon');
}
if (this.settings.hideFolderNoteNameInPath) {
document.body.classList.add('folder-note-hide-name-path');
activeDocument.body.classList.add('folder-note-hide-name-path');
}
if (this.settings.underlineFolder) {
activeDocument.body.classList.add('folder-note-underline');
}
if (this.settings.boldName) { activeDocument.body.classList.add('folder-note-bold'); }
if (this.settings.cursiveName) { activeDocument.body.classList.add('folder-note-cursive'); }
if (this.settings.boldNameInPath) {
activeDocument.body.classList.add('folder-note-bold-path');
}
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');
activeDocument.body.classList.add('folder-note-cursive-path');
}
if (this.settings.underlineFolderInPath) {
document.body.classList.add('folder-note-underline-path');
activeDocument.body.classList.add('folder-note-underline-path');
}
if (this.settings.stopWhitespaceCollapsing) {
document.body.classList.add('fn-whitespace-stop-collapsing');
activeDocument.body.classList.add('fn-whitespace-stop-collapsing');
}
if (this.settings.hideCollapsingIcon) {
document.body.classList.add('fn-hide-collapse-icon');
activeDocument.body.classList.add('fn-hide-collapse-icon');
}
if (this.settings.ignoreAttachmentFolder) {
document.body.classList.add('fn-ignore-attachment-folder');
activeDocument.body.classList.add('fn-ignore-attachment-folder');
}
if (!this.settings.highlightFolder) {
document.body.classList.add('disable-folder-highlight');
activeDocument.body.classList.add('disable-folder-highlight');
}
if (requireApiVersion('1.7.2')) {
document.body.classList.add('version-1-7-2');
activeDocument.body.classList.add('version-1-7-2');
}
}
@ -188,12 +233,12 @@ export default class FolderNotesPlugin extends Plugin {
this.tabManager = new TabManager(this);
this.tabManager.updateTabs();
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
this.registerDomEvent(activeDocument, 'click', (evt: MouseEvent) => {
this.handleFileExplorerClick(evt);
}, true);
// Handle middle mouse button clicks
this.registerDomEvent(document, 'auxclick', (evt: MouseEvent) => {
this.registerDomEvent(activeDocument, 'auxclick', (evt: MouseEvent) => {
const rightClick = 2;
if (evt.button === rightClick) return;
this.handleFileExplorerClick(evt);
@ -201,26 +246,32 @@ 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): void => {
const fileExplorer = fileExplorerPlugin as unknown as FileExplorerPluginLike;
const originalRevealInFolder =
fileExplorer.revealInFolder as unknown as FileExplorerPluginLike['revealInFolder'];
fileExplorer.revealInFolder = (file: TAbstractFile): void => {
if (file instanceof TFile) {
const folder = getFolder(this, file);
if (folder instanceof TFolder) {
const folderNote = getFolderNote(this, folder.path);
if (!folderNote || folderNote.path !== file.path) {
return originalRevealInFolder.call(fileExplorerPlugin, file);
originalRevealInFolder(file);
return;
}
document.body.classList.remove('hide-folder-note');
originalRevealInFolder.call(fileExplorerPlugin, folder);
activeDocument.body.classList.remove('hide-folder-note');
originalRevealInFolder(folder);
const FOLDER_REVEAL_DELAY = 100;
setTimeout(() => {
document.body.classList.add('hide-folder-note');
window.setTimeout(() => {
activeDocument.body.classList.add('hide-folder-note');
}, FOLDER_REVEAL_DELAY);
return;
}
}
return originalRevealInFolder.call(fileExplorerPlugin, file);
if (file instanceof TFolder || file instanceof TFile) {
originalRevealInFolder(file);
return;
}
};
}
@ -229,25 +280,27 @@ export default class FolderNotesPlugin extends Plugin {
if (!view) { return; }
// @ts-expect-error use internal API
const editMode = view.editMode ?? view.sourceMode
// @ts-expect-error use internal API
?? this.app.workspace.activeEditor?.editMode;
const viewWithEditModes = view as ViewWithEditModes;
const activeEditor = this.app.workspace.activeEditor as ActiveEditorLike | undefined;
const editMode = viewWithEditModes.editMode ?? viewWithEditModes.sourceMode
?? activeEditor?.editMode;
const plugin = this;
if (!editMode) { return; }
const clipboardProto = editMode.clipboardManager.constructor.prototype;
const { clipboardManager } = editMode;
const clipboardProto = Object.getPrototypeOf(clipboardManager) as ClipboardManagerLike;
const originalHandleDragOver = clipboardProto.handleDragOver;
const originalHandleDrop = clipboardProto.handleDrop;
const originalHandleDragOver =
clipboardProto.handleDragOver as unknown as ClipboardManagerLike['handleDragOver'];
const originalHandleDrop =
clipboardProto.handleDrop as unknown as ClipboardManagerLike['handleDrop'];
clipboardProto.handleDragOver = function (evt: DragEvent, ...args: unknown[]): void {
const { dragManager } = this.app;
clipboardProto.handleDragOver = (evt: DragEvent, ...args: unknown[]): void => {
const { dragManager } = clipboardManager.app;
const draggable = dragManager?.draggable;
if (draggable?.file instanceof TFolder) {
const folderNote = getFolderNote(plugin, draggable.file.path);
const folderNote = getFolderNote(this, draggable.file.path);
if (folderNote) {
dragManager.setAction(
window.i18next.t('interface.drag-and-drop.insert-link-here'),
@ -256,22 +309,22 @@ export default class FolderNotesPlugin extends Plugin {
}
}
return originalHandleDragOver.call(this, evt, ...args);
originalHandleDragOver(evt, ...args);
};
clipboardProto.handleDrop = function (evt: DragEvent, ...args: unknown[]): void {
const { dragManager } = this.app;
clipboardProto.handleDrop = (evt: DragEvent, ...args: unknown[]): void => {
const { dragManager } = clipboardManager.app;
const draggable = dragManager?.draggable;
if (draggable?.file instanceof TFolder) {
const folderNote = getFolderNote(plugin, draggable.file.path);
const folderNote = getFolderNote(this, draggable.file.path);
if (folderNote) {
draggable.file = folderNote;
draggable.type = 'file';
}
}
return originalHandleDrop.call(this, evt, ...args);
originalHandleDrop(evt, ...args);
};
if (this.settings.fvGlobalSettings.autoUpdateLinks) {
@ -283,7 +336,7 @@ export default class FolderNotesPlugin extends Plugin {
if (!this.settings.fvGlobalSettings.autoUpdateLinks) return;
const DEBOUNCE_DELAY = 2000;
debounce(() => {
updateAllOverviews(this);
void updateAllOverviews(this);
}, DEBOUNCE_DELAY, true)();
}
@ -314,7 +367,7 @@ export default class FolderNotesPlugin extends Plugin {
evt.stopImmediatePropagation();
}
openFolderNote(this, folderNote, evt);
void openFolderNote(this, folderNote, evt);
}
private isMobileClickDisabled(): boolean {
@ -325,9 +378,12 @@ export default class FolderNotesPlugin extends Plugin {
folderTitleEl: HTMLElement | null;
onlyClickedOnFolderTitle: boolean;
} {
const folderTitleEl = target.closest('.nav-folder-title') as HTMLElement | null;
const folderTitleEl = target.closest('.nav-folder-title');
const onlyClickedOnFolderTitle = !!target.closest('.nav-folder-title-content');
return { folderTitleEl, onlyClickedOnFolderTitle };
return {
folderTitleEl: folderTitleEl instanceof HTMLElement ? folderTitleEl : null,
onlyClickedOnFolderTitle,
};
}
private shouldIgnoreClickByWhitespaceOrCollapse(
@ -358,9 +414,9 @@ export default class FolderNotesPlugin extends Plugin {
}
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);
void createFolderNote(this, folderPath, true, undefined, true);
void addCSSClassToFileExplorerEl(folderPath, 'has-folder-note', false, this);
void removeCSSClassFromFileExplorerEL(folderPath, 'has-not-folder-note', false, this);
}
private shouldOpenNote(usedCtrl: boolean, evt: MouseEvent): boolean {
@ -380,7 +436,7 @@ export default class FolderNotesPlugin extends Plugin {
new FolderOverviewSettings(
this.app,
this,
parseYaml(source),
parseYaml(source) as defaultOverviewSettings,
ctx,
el,
this.settings.defaultOverview,
@ -404,7 +460,7 @@ export default class FolderNotesPlugin extends Plugin {
el,
defaultOverview,
);
folderOverview.create(this, el, ctx);
void folderOverview.create(this, el, ctx);
} else {
this.app.workspace.onLayoutReady(() => {
const folderOverview = new FolderOverview(
@ -414,7 +470,7 @@ export default class FolderNotesPlugin extends Plugin {
el,
this.settings.defaultOverview,
);
folderOverview.create(this, el, ctx);
void folderOverview.create(this, el, ctx);
});
}
} catch (e) {
@ -438,7 +494,7 @@ export default class FolderNotesPlugin extends Plugin {
}
if (!leaf) return;
workspace.revealLeaf(leaf);
void workspace.revealLeaf(leaf);
}
updateOverviewView: typeof updateOverviewView = updateOverviewView;
@ -451,12 +507,12 @@ export default class FolderNotesPlugin extends Plugin {
|| attachmentFolderPath === '';
const threshold = this.settings.storageLocation === 'insideFolder' ? 1 : 0;
if (folder.children.length === 0) {
addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this);
void addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this);
}
attachmentFolderPath = `${folder.path}/${cleanAttachmentFolderPath}`;
if (folder.children.length === threshold) {
addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this);
void addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this);
return true;
} else if (folder.children.length > threshold) {
if (attachmentsAreInRootFolder) {
@ -469,12 +525,12 @@ export default class FolderNotesPlugin extends Plugin {
attachmentFolder instanceof TFolder &&
folder.children.length <= threshold + 1
) {
addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this);
addCSSClassToFileExplorerEl(
void addCSSClassToFileExplorerEl(folder.path, 'fn-empty-folder', false, this);
void addCSSClassToFileExplorerEl(
folder.path, 'fn-has-attachment-folder',
false, this,
);
addCSSClassToFileExplorerEl(
void addCSSClassToFileExplorerEl(
folder.path, 'fn-has-attachment-folder',
true, this,
);
@ -499,7 +555,7 @@ export default class FolderNotesPlugin extends Plugin {
let fileExplorerItem = getFileExplorerElement(folder.path, this);
if (!fileExplorerItem) {
if (waitForCreate && count < MAX_RETRY_COUNT) {
await new Promise<void>((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
await new Promise<void>((resolve) => window.setTimeout(resolve, RETRY_DELAY_MS));
void this.changeFolderNameInExplorer(folder, newName, waitForCreate, count + 1);
return;
}
@ -509,11 +565,11 @@ 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 as HTMLElement).innerText = newName;
(fileExplorerItem as HTMLElement).setAttribute('old-name', folder.name);
(fileExplorerItem).innerText = newName;
(fileExplorerItem).setAttribute('old-name', folder.name);
} else {
(fileExplorerItem as HTMLElement).innerText = folder.name;
(fileExplorerItem as HTMLElement).removeAttribute('old-name');
(fileExplorerItem).innerText = folder.name;
(fileExplorerItem).removeAttribute('old-name');
}
}
@ -534,7 +590,7 @@ export default class FolderNotesPlugin extends Plugin {
*/
updateAllBreadcrumbs(remove?: boolean): void {
if (!this.settings.frontMatterTitle.path && !remove) { return; }
const viewHeaderItems = document.querySelectorAll('span.view-header-breadcrumb');
const viewHeaderItems = activeDocument.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; }
@ -554,10 +610,10 @@ export default class FolderNotesPlugin extends Plugin {
onunload(): void {
unregisterFileExplorerObserver();
document.body.classList.remove('folder-notes-plugin');
document.body.classList.remove('folder-note-underline');
document.body.classList.remove('hide-folder-note');
document.body.classList.remove('fn-whitespace-stop-collapsing');
activeDocument.body.classList.remove('folder-notes-plugin');
activeDocument.body.classList.remove('folder-note-underline');
activeDocument.body.classList.remove('hide-folder-note');
activeDocument.body.classList.remove('fn-whitespace-stop-collapsing');
removeActiveFolder(this);
if (this.fmtpHandler) {
this.fmtpHandler.deleteEvent();
@ -565,7 +621,7 @@ export default class FolderNotesPlugin extends Plugin {
}
async loadSettings(): Promise<void> {
const data = await this.loadData();
const data = await this.loadData() as LegacySettingsData | null;
if (data) {
if (data.allowWhitespaceCollapsing === true) {
data.stopWhitespaceCollapsing = false;

View file

@ -38,7 +38,7 @@ export default class AddSupportedFileModal extends Modal {
}),
);
}
async onClose(): Promise<void> {
onClose(): void {
if (this.name.toLocaleLowerCase() === 'markdown') {
this.name = 'md';
}
@ -50,10 +50,13 @@ export default class AddSupportedFileModal extends Modal {
new Notice('This extension is already supported');
return;
} else {
await this.list.addValue(this.name.toLowerCase());
this.settingsTab.display();
this.plugin.saveSettings();
contentEl.empty();
// Run async operations without returning a Promise from onClose
void (async (): Promise<void> => {
await this.list.addValue(this.name.toLowerCase());
this.settingsTab.display();
await this.plugin.saveSettings();
contentEl.empty();
})();
}
}
}

View file

@ -39,7 +39,7 @@ export class AskForExtensionModal extends FuzzySuggestModal<string> {
onChooseItem(item: string, _evt: MouseEvent | KeyboardEvent): void {
this.plugin.askModalCurrentlyOpen = false;
this.extension = '.' + item;
createFolderNote(
void createFolderNote(
this.plugin,
this.folderPath,
this.openFile,

View file

@ -46,9 +46,9 @@ export default class ExistingFolderNoteModal extends Modal {
text: 'Rename and don\'t ask again',
});
confirmButton.classList.add('mod-warning', 'fn-confirmation-modal-button');
confirmButton.addEventListener('click', async () => {
confirmButton.addEventListener('click', () => {
this.plugin.settings.showRenameConfirmation = false;
this.plugin.saveSettings();
void this.plugin.saveSettings();
this.close();
turnIntoFolderNote(this.plugin, this.file, this.folder, this.folderNote, true);
});
@ -69,14 +69,14 @@ export default class ExistingFolderNoteModal extends Modal {
}
const button = buttonContainer.createEl('button', { text: 'Rename' });
button.classList.add('mod-warning', 'fn-confirmation-modal-button');
button.addEventListener('click', async () => {
this.plugin.saveSettings();
button.addEventListener('click', () => {
void this.plugin.saveSettings();
this.close();
turnIntoFolderNote(this.plugin, this.file, this.folder, this.folderNote, true);
});
button.focus();
const cancelButton = buttonContainer.createEl('button', { text: 'Cancel' });
cancelButton.addEventListener('click', async () => {
cancelButton.addEventListener('click', () => {
this.close();
});

View file

@ -17,7 +17,7 @@ export async function renderExcludeFolders(settingsTab: SettingsTab): Promise<vo
.setHeading()
.setClass('fn-excluded-folder-heading')
.setName('Manage excluded folders');
const desc3 = document.createDocumentFragment();
const desc3 = activeDocument.createDocumentFragment();
desc3.append(
'Add {regex} at the beginning of the folder name to use a regex pattern.',
desc3.createEl('br'),
@ -28,12 +28,12 @@ export async function renderExcludeFolders(settingsTab: SettingsTab): Promise<vo
'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';

View file

@ -14,9 +14,9 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise<void
settingsTab.plugin.settings.hideFolderNote = value;
await settingsTab.plugin.saveSettings();
if (value) {
document.body.classList.add('hide-folder-note');
activeDocument.body.classList.add('hide-folder-note');
} else {
document.body.classList.remove('hide-folder-note');
activeDocument.body.classList.remove('hide-folder-note');
}
settingsTab.display();
}),
@ -46,9 +46,9 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise<void
.setValue(!settingsTab.plugin.settings.stopWhitespaceCollapsing)
.onChange(async (value) => {
if (!value) {
document.body.classList.add('fn-whitespace-stop-collapsing');
activeDocument.body.classList.add('fn-whitespace-stop-collapsing');
} else {
document.body.classList.remove('fn-whitespace-stop-collapsing');
activeDocument.body.classList.remove('fn-whitespace-stop-collapsing');
}
settingsTab.plugin.settings.stopWhitespaceCollapsing = !value;
await settingsTab.plugin.saveSettings();
@ -119,9 +119,9 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise<void
.onChange(async (value) => {
settingsTab.plugin.settings.highlightFolder = value;
if (!value) {
document.body.classList.add('disable-folder-highlight');
activeDocument.body.classList.add('disable-folder-highlight');
} else {
document.body.classList.remove('disable-folder-highlight');
activeDocument.body.classList.remove('disable-folder-highlight');
}
await settingsTab.plugin.saveSettings();
}),
@ -136,9 +136,9 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise<void
.onChange(async (value) => {
settingsTab.plugin.settings.hideCollapsingIcon = value;
if (value) {
document.body.classList.add('fn-hide-collapse-icon');
activeDocument.body.classList.add('fn-hide-collapse-icon');
} else {
document.body.classList.remove('fn-hide-collapse-icon');
activeDocument.body.classList.remove('fn-hide-collapse-icon');
}
await settingsTab.plugin.saveSettings();
settingsTab.display();
@ -155,9 +155,9 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise<void
settingsTab.plugin.settings.hideCollapsingIconForEmptyFolders = value;
await settingsTab.plugin.saveSettings();
if (value) {
document.body.classList.add('fn-hide-empty-collapse-icon');
activeDocument.body.classList.add('fn-hide-empty-collapse-icon');
} else {
document.body.classList.remove('fn-hide-empty-collapse-icon');
activeDocument.body.classList.remove('fn-hide-empty-collapse-icon');
}
settingsTab.display();
},
@ -171,9 +171,9 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise<void
.setValue(settingsTab.plugin.settings.ignoreAttachmentFolder)
.onChange(async (value) => {
if (value) {
document.body.classList.add('fn-ignore-attachment-folder');
activeDocument.body.classList.add('fn-ignore-attachment-folder');
} else {
document.body.classList.remove('fn-ignore-attachment-folder');
activeDocument.body.classList.remove('fn-ignore-attachment-folder');
}
settingsTab.plugin.settings.ignoreAttachmentFolder = value;
await settingsTab.plugin.saveSettings();
@ -190,9 +190,9 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise<void
.onChange(async (value) => {
settingsTab.plugin.settings.underlineFolder = value;
if (value) {
document.body.classList.add('folder-note-underline');
activeDocument.body.classList.add('folder-note-underline');
} else {
document.body.classList.remove('folder-note-underline');
activeDocument.body.classList.remove('folder-note-underline');
}
await settingsTab.plugin.saveSettings();
}),
@ -207,9 +207,9 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise<void
.onChange(async (value) => {
settingsTab.plugin.settings.boldName = value;
if (value) {
document.body.classList.add('folder-note-bold');
activeDocument.body.classList.add('folder-note-bold');
} else {
document.body.classList.remove('folder-note-bold');
activeDocument.body.classList.remove('folder-note-bold');
}
await settingsTab.plugin.saveSettings();
}),
@ -224,9 +224,9 @@ export async function renderFileExplorer(settingsTab: SettingsTab): Promise<void
.onChange(async (value) => {
settingsTab.plugin.settings.cursiveName = value;
if (value) {
document.body.classList.add('folder-note-cursive');
activeDocument.body.classList.add('folder-note-cursive');
} else {
document.body.classList.remove('folder-note-cursive');
activeDocument.body.classList.remove('folder-note-cursive');
}
await settingsTab.plugin.saveSettings();
}),

View file

@ -36,11 +36,12 @@ export async function renderFolderOverview(settingsTab: SettingsTab): Promise<vo
span.setAttr('style', `color: ${accentColor};`);
pEl.appendChild(span);
createOverviewSettings(
void createOverviewSettings(
containerEl,
defaultOverviewSettings,
plugin,
plugin.settings.defaultOverview,
// eslint-disable-next-line @typescript-eslint/unbound-method
settingsTab.display,
undefined,
undefined,

View file

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/unbound-method */
/* eslint-disable max-len */
import { Setting, Platform } from 'obsidian';
import type { SettingsTab } from './SettingsTab';
@ -10,7 +11,7 @@ import { refreshAllFolderStyles } from '../functions/styleFunctions';
import BackupWarningModal from './modals/BackupWarning';
import RenameFolderNotesModal from './modals/RenameFns';
let debounceTimer: NodeJS.Timeout;
let debounceTimer: ReturnType<typeof setTimeout>;
// eslint-disable-next-line complexity
export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
@ -26,9 +27,9 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
settingsTab.plugin.settings.folderNoteName = value;
await settingsTab.plugin.saveSettings();
clearTimeout(debounceTimer);
window.clearTimeout(debounceTimer);
const FOLDER_NOTE_NAME_DEBOUNCE_MS = 2000;
debounceTimer = setTimeout(() => {
debounceTimer = window.setTimeout(() => {
if (!value.includes('{{folder_name}}')) {
if (!settingsTab.showFolderNameInTabTitleSetting) {
settingsTab.display();
@ -62,8 +63,8 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
if (!settingsTab.plugin.settings.folderNoteName.includes('{{folder_name}}')) {
new Setting(containerEl)
.setName('Display Folder Name in Tab Title')
.setDesc('Use the actual folder name in the tab title instead of the custom folder note name (e.g., "Folder Note").')
.setName('Display folder name in tab title')
.setDesc('Use the actual folder name in the tab title instead of the custom folder note name (e.g., "folder note").')
.addToggle((toggle) =>
toggle
.setValue(settingsTab.plugin.settings.tabManagerEnabled)
@ -83,12 +84,12 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
new Setting(containerEl)
.setName('Default file type for new folder notes')
.setDesc('Choose the default file type (canvas, markdown, ...) used when creating new folder notes.')
.setDesc('Choose the default file type (canvas, Markdown, ...) used when creating new folder notes.')
.addDropdown((dropdown) => {
dropdown.addOption('.ask', 'ask for file type');
dropdown.addOption('.ask', 'Ask for file type');
settingsTab.plugin.settings.supportedFileTypes.forEach((type) => {
if (type === '.md' || type === 'md') {
dropdown.addOption('.md', 'markdown');
dropdown.addOption('.md', 'Markdown');
} else {
dropdown.addOption('.' + type, type);
}
@ -101,7 +102,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
settingsTab.plugin.settings.folderNoteType !== '.ask'
) {
settingsTab.plugin.settings.folderNoteType = '.md';
settingsTab.plugin.saveSettings();
void settingsTab.plugin.saveSettings();
}
let defaultType = settingsTab.plugin.settings.folderNoteType.startsWith('.')
@ -118,16 +119,16 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
dropdown
.setValue(defaultType)
.onChange(async (value: '.md' | '.canvas') => {
settingsTab.plugin.settings.folderNoteType = value;
settingsTab.plugin.saveSettings();
settingsTab.display();
.onChange(async (value: string) => {
settingsTab.plugin.settings.folderNoteType = value as '.md' | '.canvas' | '.ask';
void settingsTab.plugin.saveSettings();
void settingsTab.display();
});
});
const setting0 = new Setting(containerEl);
setting0.setName('Supported file types');
const desc0 = document.createDocumentFragment();
const desc0 = activeDocument.createDocumentFragment();
desc0.append(
'Specify which file types are allowed as folder notes. Applies to both new and existing folders. Adding many types may affect performance.',
);
@ -137,10 +138,10 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
settingsTab.plugin.settings.supportedFileTypes || [],
['md', 'canvas'],
);
list.on('update', async (values: string[]) => {
settingsTab.plugin.settings.supportedFileTypes = values;
await settingsTab.plugin.saveSettings();
settingsTab.display();
list.on('update', (values: unknown) => {
settingsTab.plugin.settings.supportedFileTypes = values as string[];
void settingsTab.plugin.saveSettings();
void settingsTab.display();
});
if (
@ -170,12 +171,12 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
settingsTab.app,
settingsTab.plugin,
settingsTab,
list as ListComponent,
list,
).open();
}
await list.addValue(value.toLowerCase());
settingsTab.display();
settingsTab.plugin.saveSettings();
void settingsTab.display();
void settingsTab.plugin.saveSettings();
});
});
} else {
@ -188,7 +189,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
settingsTab.app,
settingsTab.plugin,
settingsTab,
list as ListComponent,
list,
).open();
}),
);
@ -226,7 +227,10 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
.addOption('insideFolder', 'Inside the folder')
.addOption('parentFolder', 'In the parent folder')
.setValue(settingsTab.plugin.settings.storageLocation)
.onChange(async (value: 'insideFolder' | 'parentFolder' | 'vaultFolder') => {
.onChange(async (value: string) => {
if (value !== 'insideFolder' && value !== 'parentFolder' && value !== 'vaultFolder') {
return;
}
settingsTab.plugin.settings.storageLocation = value;
await settingsTab.plugin.saveSettings();
settingsTab.display();
@ -282,7 +286,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
);
}
if (Platform.isDesktopApp) {
settingsTab.settingsPage.createEl('h3', { text: 'Keyboard Shortcuts' });
settingsTab.settingsPage.createEl('h3', { text: 'Keyboard shortcuts' });
new Setting(containerEl)
.setName('Key for creating folder note')
@ -308,7 +312,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
.setName('Key for opening folder note')
.setDesc('Select the combination to open a folder note')
.addDropdown((dropdown) => {
dropdown.addOption('click', 'Mouse Click');
dropdown.addOption('click', 'Mouse click');
if (!Platform.isMacOS) {
dropdown.addOption('ctrl', 'Ctrl + Click');
dropdown.addOption('alt', 'Alt + Click');
@ -356,8 +360,9 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
dropdown.addOption('obsidianTrash', 'Move to Obsidian trash (.trash folder)');
dropdown.addOption('delete', 'Delete permanently');
dropdown.setValue(settingsTab.plugin.settings.deleteFilesAction);
dropdown.onChange(async (value: 'trash' | 'delete' | 'obsidianTrash') => {
settingsTab.plugin.settings.deleteFilesAction = value;
dropdown.onChange(async (value) => {
const v = value as 'trash' | 'delete' | 'obsidianTrash';
settingsTab.plugin.settings.deleteFilesAction = v;
await settingsTab.plugin.saveSettings();
settingsTab.display();
});
@ -450,7 +455,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
new Setting(containerEl)
.setName('Auto-create for attachment folders')
.setDesc('Also automatically create folder notes for attachment folders (e.g., "Attachments", "Media", etc.).')
.setDesc('Also automatically create folder notes for attachment folders (e.g., "attachments", "media", etc.).')
.addToggle((toggle) =>
toggle
.setValue(settingsTab.plugin.settings.autoCreateForAttachmentFolder)
@ -475,13 +480,13 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
}),
);
settingsTab.settingsPage.createEl('h3', { text: 'Integration & Compatibility' });
settingsTab.settingsPage.createEl('h3', { text: 'Integration & compatibility' });
const desc1 = document.createDocumentFragment();
const desc1 = activeDocument.createDocumentFragment();
const link = document.createElement('a');
const link = activeDocument.createElement('a');
link.href = 'https://github.com/snezhig/obsidian-front-matter-title';
link.textContent = 'front matter title plugin';
link.textContent = 'Front matter title plugin';
link.target = '_blank';
desc1.append(
@ -507,7 +512,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
settingsTab.plugin.updateAllBreadcrumbs(true);
}
settingsTab.plugin.app.vault.getFiles().forEach((file) => {
settingsTab.plugin.fmtpHandler?.fmptUpdateFileName(
void settingsTab.plugin.fmtpHandler?.fmptUpdateFileName(
{
id: '',
result: false,
@ -527,7 +532,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
fmtpSetting.infoEl.appendText('Requires a restart to take effect');
fmtpSetting.infoEl.style.color = settingsTab.app.vault.getConfig('accentColor') as string || '#7d5bed';
settingsTab.settingsPage.createEl('h3', { text: 'Session & Persistence' });
settingsTab.settingsPage.createEl('h3', { text: 'Session & persistence' });
new Setting(containerEl)
.setName('Persist tab after restart')

View file

@ -18,7 +18,7 @@ export async function renderPath(settingsTab: SettingsTab): Promise<void> {
if (settingsTab.plugin.settings.openFolderNoteOnClickInPath) {
new Setting(containerEl)
.setName('Open sidebar when opening a folder note through path (Mobile only)')
.setName('Open sidebar when opening a folder note through path (mobile only)')
.setDesc('Open the sidebar when opening a folder note through the path on mobile')
.addToggle((toggle) =>
toggle
@ -30,7 +30,7 @@ export async function renderPath(settingsTab: SettingsTab): Promise<void> {
);
new Setting(containerEl)
.setName('Open sidebar when opening a folder note through path (Desktop only)')
.setName('Open sidebar when opening a folder note through path (desktop only)')
.setDesc('Open the sidebar when opening a folder note through the path on desktop')
.addToggle((toggle) =>
toggle
@ -72,9 +72,9 @@ export async function renderPath(settingsTab: SettingsTab): Promise<void> {
.onChange(async (value) => {
settingsTab.plugin.settings.underlineFolderInPath = value;
if (value) {
document.body.classList.add('folder-note-underline-path');
activeDocument.body.classList.add('folder-note-underline-path');
} else {
document.body.classList.remove('folder-note-underline-path');
activeDocument.body.classList.remove('folder-note-underline-path');
}
await settingsTab.plugin.saveSettings();
}),
@ -89,9 +89,9 @@ export async function renderPath(settingsTab: SettingsTab): Promise<void> {
.onChange(async (value) => {
settingsTab.plugin.settings.boldNameInPath = value;
if (value) {
document.body.classList.add('folder-note-bold-path');
activeDocument.body.classList.add('folder-note-bold-path');
} else {
document.body.classList.remove('folder-note-bold-path');
activeDocument.body.classList.remove('folder-note-bold-path');
}
await settingsTab.plugin.saveSettings();
}),
@ -106,9 +106,9 @@ export async function renderPath(settingsTab: SettingsTab): Promise<void> {
.onChange(async (value) => {
settingsTab.plugin.settings.cursiveNameInPath = value;
if (value) {
document.body.classList.add('folder-note-cursive-path');
activeDocument.body.classList.add('folder-note-cursive-path');
} else {
document.body.classList.remove('folder-note-cursive-path');
activeDocument.body.classList.remove('folder-note-cursive-path');
}
await settingsTab.plugin.saveSettings();
}),
@ -121,7 +121,7 @@ export async function renderPath(settingsTab: SettingsTab): Promise<void> {
toggle
.setValue(settingsTab.plugin.settings.hideFolderNoteNameInPath)
.onChange(async (value) => {
document.body.classList.toggle('folder-note-hide-name-path', value);
activeDocument.body.classList.toggle('folder-note-hide-name-path', value);
settingsTab.plugin.settings.hideFolderNoteNameInPath = value;
await settingsTab.plugin.saveSettings();
}),

View file

@ -211,11 +211,11 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = {
};
export class SettingsTab extends PluginSettingTab {
plugin: FolderNotesPlugin;
app: App;
excludeFolders: ExcludedFolder[];
plugin!: FolderNotesPlugin;
app!: App;
excludeFolders!: ExcludedFolder[];
settingsPage!: HTMLElement;
showFolderNameInTabTitleSetting: boolean;
showFolderNameInTabTitleSetting!: boolean;
constructor(app: App, plugin: FolderNotesPlugin) {
super(app, plugin);
}
@ -246,19 +246,19 @@ export class SettingsTab extends PluginSettingTab {
this.settingsPage.empty();
switch (tabId.toLocaleLowerCase()) {
case this.TABS.GENERAL.id:
renderGeneral(this);
void renderGeneral(this);
break;
case this.TABS.FOLDER_OVERVIEW.id:
renderFolderOverview(this);
void renderFolderOverview(this);
break;
case this.TABS.EXCLUDE_FOLDERS.id:
renderExcludeFolders(this);
void renderExcludeFolders(this);
break;
case this.TABS.FILE_EXPLORER.id:
renderFileExplorer(this);
void renderFileExplorer(this);
break;
case this.TABS.PATH.id:
renderPath(this);
void renderPath(this);
break;
}
}
@ -298,12 +298,11 @@ export class SettingsTab extends PluginSettingTab {
tabEl.addClass('fn-settings-tab-active');
}
tabEl.addEventListener('click', () => {
// @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');
(child as HTMLElement).classList.remove('fn-settings-tab-active');
if (!plugin) { return; }
plugin.settings.settingsTab = tabId.toLocaleLowerCase();
plugin.saveSettings();
void plugin.saveSettings();
}
tabEl.addClass('fn-settings-tab-active');
if (!settingsTab) { return; }
@ -344,19 +343,19 @@ export class SettingsTab extends PluginSettingTab {
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') {
newPath = `${folder.path}/${newFolderNoteName}.${folderNote.extension}`;
}
this.app.fileManager.renameFile(folderNote, newPath);
void this.app.fileManager.renameFile(folderNote, newPath);
}
}
this.plugin.settings.oldFolderNoteName = this.plugin.settings.folderNoteName;
this.plugin.saveSettings();
void this.plugin.saveSettings();
new Notice('Finished updating folder notes');
}
@ -373,13 +372,13 @@ export class SettingsTab extends PluginSettingTab {
} else {
newPath = `${getFolderPathFromString(file.path)}/${folderNote.name}`;
}
this.plugin.app.fileManager.renameFile(folderNote, newPath);
void this.plugin.app.fileManager.renameFile(folderNote, newPath);
} else if (this.plugin.settings.storageLocation === 'insideFolder') {
if (getFolderPathFromString(folderNote.path) === file.path) {
return;
}
const newPath = `${file.path}/${folderNote.name}`;
this.plugin.app.fileManager.renameFile(folderNote, newPath);
void this.plugin.app.fileManager.renameFile(folderNote, newPath);
}
}

View file

@ -32,7 +32,7 @@ export default class BackupWarningModal extends Modal {
contentEl.createEl('p', { text: this.desc });
// eslint-disable-next-line max-len
contentEl.createEl('p', { text: 'Make sure to backup your vault before using this feature.' }).style.color = '#fb464c';
contentEl.createEl('p', { text: 'Make sure to backup your vault before using this feature.' }).addClass('fn-warning-text');
const buttonContainer = contentEl.createDiv({ cls: 'fn-modal-button-container' });
const confirmButton = new ButtonComponent(buttonContainer);

View file

@ -6,7 +6,7 @@ import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions'
export default class ConfirmationModal extends Modal {
plugin: FolderNotesPlugin;
app: App;
folder: TFolder;
folder!: TFolder;
extension: string;
constructor(app: App, plugin: FolderNotesPlugin) {
super(app);
@ -36,7 +36,7 @@ export default class ConfirmationModal extends Modal {
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';
setting.infoEl.createEl('p', { text: 'Make sure to backup your vault before using this feature.' }).addClass('fn-warning-text');
// 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

View file

@ -16,8 +16,7 @@ export default class RenameFolderNotesModal extends BackupWarningModal {
insertCustomHtml(): void {
const { contentEl } = this;
new Setting(contentEl)
.setName('Old Folder Note Name')
// eslint-disable-next-line max-len
.setName('Old folder note name')
.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')
@ -28,8 +27,7 @@ export default class RenameFolderNotesModal extends BackupWarningModal {
);
new Setting(contentEl)
.setName('New Folder Note Name')
// eslint-disable-next-line max-len
.setName('New folder note name')
.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')

View file

@ -1,17 +1,19 @@
import { type TAbstractFile, TFile } from 'obsidian';
import { TextInputSuggest } from './Suggest';
import { AbstractInputSuggest, type TAbstractFile, TFile } from 'obsidian';
import type FolderNotesPlugin from '../main';
export enum FileSuggestMode {
TemplateFiles,
ScriptFiles,
}
export class FileSuggest extends TextInputSuggest<TFile> {
export class FileSuggest extends AbstractInputSuggest<TFile> {
plugin: FolderNotesPlugin;
constructor(
public inputEl: HTMLInputElement,
plugin: FolderNotesPlugin,
public inputEl: HTMLInputElement,
plugin: FolderNotesPlugin,
) {
super(inputEl, plugin);
super(plugin.app, inputEl);
this.plugin = plugin;
}
get_error_msg(mode: FileSuggestMode): string {

View file

@ -1,21 +1,23 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater
import { TFolder, type TAbstractFile } from 'obsidian';
import { TextInputSuggest } from './Suggest';
import { AbstractInputSuggest, TFolder, type TAbstractFile } from 'obsidian';
import type FolderNotesPlugin from '../main';
export enum FileSuggestMode {
TemplateFiles,
ScriptFiles,
}
export class FolderSuggest extends TextInputSuggest<TFolder> {
export class FolderSuggest extends AbstractInputSuggest<TFolder> {
plugin: FolderNotesPlugin;
constructor(
public inputEl: HTMLInputElement,
plugin: FolderNotesPlugin,
public inputEl: HTMLInputElement,
plugin: FolderNotesPlugin,
private whitelistSuggester: boolean,
public folder?: TFolder,
) {
super(inputEl, plugin);
super(plugin.app, inputEl);
this.plugin = plugin;
}

View file

@ -1,205 +0,0 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater
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 => {
return ((value % size) + size) % size;
};
class Suggest<T> {
private owner: ISuggestOwner<T>;
private values: T[];
private suggestions: HTMLDivElement[];
private selectedItem: number;
private containerEl: HTMLElement;
plugin: FolderNotesPlugin;
constructor(
owner: ISuggestOwner<T>,
containerEl: HTMLElement,
scope: Scope,
) {
this.owner = owner;
this.containerEl = containerEl;
containerEl.on(
'click',
'.suggestion-item',
this.onSuggestionClick.bind(this),
);
containerEl.on(
'mousemove',
'.suggestion-item',
this.onSuggestionMouseover.bind(this),
);
scope.register([], 'ArrowUp', (event) => {
if (!event.isComposing) {
this.setSelectedItem(this.selectedItem - 1, true);
return false;
}
});
scope.register([], 'ArrowDown', (event) => {
if (!event.isComposing) {
this.setSelectedItem(this.selectedItem + 1, true);
return false;
}
});
scope.register([], 'Enter', (event) => {
if (!event.isComposing) {
this.useSelectedItem(event);
return false;
}
});
}
onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void {
event.preventDefault();
const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
this.useSelectedItem(event);
}
onSuggestionMouseover(_event: MouseEvent, el: HTMLDivElement): void {
const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
}
setSuggestions(values: T[]): void {
this.containerEl.empty();
const suggestionEls: HTMLDivElement[] = [];
values.forEach((value) => {
const suggestionEl = this.containerEl.createDiv('suggestion-item');
this.owner.renderSuggestion(value, suggestionEl);
suggestionEls.push(suggestionEl);
});
this.values = values;
this.suggestions = suggestionEls;
this.setSelectedItem(0, false);
}
useSelectedItem(event: MouseEvent | KeyboardEvent): void {
const currentValue = this.values[this.selectedItem];
if (currentValue) {
this.owner.selectSuggestion(currentValue, event);
}
}
setSelectedItem(selectedIndex: number, scrollIntoView: boolean): void {
const normalizedIndex = wrapAround(
selectedIndex,
this.suggestions.length,
);
const prevSelectedSuggestion = this.suggestions[this.selectedItem];
const selectedSuggestion = this.suggestions[normalizedIndex];
prevSelectedSuggestion?.removeClass('is-selected');
selectedSuggestion?.addClass('is-selected');
this.selectedItem = normalizedIndex;
if (scrollIntoView) {
selectedSuggestion.scrollIntoView(false);
}
}
}
export abstract class TextInputSuggest<T> implements ISuggestOwner<T> {
protected inputEl: HTMLInputElement | HTMLTextAreaElement;
private popper: PopperInstance;
private scope: Scope;
private suggestEl: HTMLElement;
private suggest: Suggest<T>;
plugin: FolderNotesPlugin;
constructor(inputEl: HTMLInputElement | HTMLTextAreaElement, plugin: FolderNotesPlugin) {
this.inputEl = inputEl;
this.plugin = plugin;
this.scope = new Scope();
this.suggestEl = createDiv('suggestion-container');
const suggestion = this.suggestEl.createDiv('suggestion');
this.suggest = new Suggest(this, suggestion, this.scope);
this.scope.register([], 'Escape', this.close.bind(this));
this.inputEl.addEventListener('input', this.onInputChanged.bind(this));
this.inputEl.addEventListener('focus', this.onInputChanged.bind(this));
this.inputEl.addEventListener('blur', this.close.bind(this));
this.suggestEl.on(
'mousedown',
'.suggestion-container',
(event: MouseEvent) => {
event.preventDefault();
},
);
}
onInputChanged(): void {
const inputStr = this.inputEl.value;
const suggestions = this.getSuggestions(inputStr);
if (!suggestions) {
this.close();
return;
}
if (suggestions.length > 0) {
this.suggest.setSuggestions(suggestions);
// @ts-expect-error App may not exist
this.open(app.dom.appContainerEl, this.inputEl);
} else {
this.close();
}
}
open(container: HTMLElement, inputEl: HTMLElement): void {
this.plugin.app.keymap.pushScope(this.scope);
container.appendChild(this.suggestEl);
this.popper = createPopper(inputEl, this.suggestEl, {
placement: 'bottom-start',
modifiers: [
{
name: 'sameWidth',
enabled: true,
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
// we need to early exit to avoid an infinite loop
const targetWidth = `${state.rects.reference.width}px`;
if (state.styles.popper.width === targetWidth) {
return;
}
state.styles.popper.width = targetWidth;
instance.update();
},
phase: 'beforeWrite',
requires: ['computeStyles'],
},
],
});
}
close(): void {
this.plugin.app.keymap.popScope(this.scope);
this.suggest.setSuggestions([]);
if (this.popper) this.popper.destroy();
this.suggestEl.detach();
}
abstract getSuggestions(inputStr: string): T[];
abstract renderSuggestion(item: T, el: HTMLElement): void;
abstract selectSuggestion(item: T): void;
}

View file

@ -47,13 +47,13 @@ export class TemplateSuggest extends AbstractInputSuggest<TFile> {
{
path: '',
name:
// eslint-disable-next-line max-len
'You need to set the Templates folder in the Templater settings first.',
// eslint-disable-next-line obsidianmd/no-tfile-tfolder-cast
} as TFile,
];
}
} else if (templateFolder) {
folder = this.plugin.app.vault.getAbstractFileByPath(templateFolder) as TFolder;
folder = this.plugin.app.vault.getAbstractFileByPath(templateFolder);
}
if (!(folder instanceof TFolder)) {
@ -86,7 +86,7 @@ export class TemplateSuggest extends AbstractInputSuggest<TFile> {
this.inputEl.value = file.name.replace('.md', '');
this.inputEl.trigger('input');
this.plugin.settings.templatePath = file.path;
this.plugin.saveSettings();
void this.plugin.saveSettings();
this.close();
}
}

View file

@ -55,7 +55,7 @@ export async function applyTemplate(
} = 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. ⚠==')) {
if (templateContent.includes('==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this activeDocument. ⚠==')) {
return;
}

View file

@ -28,6 +28,10 @@
}
.fn-warning-text {
color: #fb464c
}
/* ==========================================================================
Tree Items
========================================================================== */

View file

@ -3,11 +3,11 @@
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"module": "node16",
"target": "ES6",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"moduleResolution": "node16",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
@ -17,7 +17,10 @@
"ES6",
"ES7",
"ES2021"
]
],
"paths": {
"@app/*": ["./src/*"]
}
},
"include": [
"**/*.ts"

View file

@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import { readFileSync, writeFileSync } from "fs";
const targetVersion = process.env.npm_package_version;