Compare commits

..

No commits in common. "main" and "1.8.21" have entirely different histories.
main ... 1.8.21

23 changed files with 1459 additions and 4853 deletions

View file

@ -8,7 +8,7 @@ on:
permissions:
id-token: write
contents: write
contents: read
attestations: write
jobs:

View file

@ -1,12 +1,12 @@
{
"id": "folder-notes",
"name": "Folder notes",
"version": "1.8.26",
"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
}
"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
}

6134
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -18,11 +18,11 @@
"devDependencies": {
"@eslint/js": "^8.57.1",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "^8.60.0",
"@typescript-eslint/parser": "^8.60.0",
"@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.38.0",
"builtin-modules": "3.3.0",
"esbuild": "0.14.47",
"eslint": "^10.4.1",
"eslint": "^9.32.0",
"eslint-plugin-obsidianmd": "^0.3.0",
"front-matter-plugin-api-provider": "^0.1.4-alpha",
"globals": "^16.3.0",
@ -30,7 +30,7 @@
"obsidian": "latest",
"obsidian-typings": "^2.2.0",
"tslib": "2.4.0",
"typescript": "^6.0.3",
"typescript": "^4.8.4",
"typescript-eslint": "^8.38.0"
},
"dependencies": {

View file

@ -12,7 +12,7 @@ export class ExcludedFolder {
position: number;
excludeFromFolderOverview: boolean;
hideInSettings: boolean;
detached: boolean = false;
detached: boolean;
detachedFilePath?: string;
showFolderNote: boolean;
constructor(path: string, position: number, id: string | undefined, plugin: FolderNotesPlugin) {

View file

@ -12,7 +12,7 @@ export class ExcludePattern {
enableCollapsing: boolean;
excludeFromFolderOverview: boolean;
hideInSettings: boolean;
detached: boolean = false;
detached: boolean;
detachedFilePath?: string;
showFolderNote: boolean;
constructor(

View file

@ -5,14 +5,14 @@ export class WhitelistedFolder {
path: string;
string: string;
subFolders: boolean;
enableSync: boolean = false;
enableAutoCreate: boolean = false;
enableFolderNote: boolean = false;
disableCollapsing: boolean = false;
showInFolderOverview: boolean = false;
hideInFileExplorer: boolean = false;
enableSync: boolean;
enableAutoCreate: boolean;
enableFolderNote: boolean;
disableCollapsing: boolean;
showInFolderOverview: boolean;
hideInFileExplorer: boolean;
position: number;
hideInSettings: boolean = false;
hideInSettings: boolean;
constructor(path: string, position: number, id: string | undefined, plugin: FolderNotesPlugin) {
this.type = 'folder';
this.id = id || crypto.randomUUID();

View file

@ -6,13 +6,13 @@ export class WhitelistedPattern {
path: string;
position: number;
subFolders: boolean;
enableSync: boolean = false;
enableAutoCreate: boolean = false;
enableFolderNote: boolean = false;
disableCollapsing: boolean = false;
showInFolderOverview: boolean = false;
hideInFileExplorer: boolean = false;
hideInSettings: boolean = false;
enableSync: boolean;
enableAutoCreate: boolean;
enableFolderNote: boolean;
disableCollapsing: boolean;
showInFolderOverview: boolean;
hideInFileExplorer: boolean;
hideInSettings: boolean;
constructor(
pattern: string,
position: number,

View file

@ -235,6 +235,7 @@ export function addExcludeFolderListItem(
plugin,
false,
);
// @ts-expect-error Obsidian's public types don't include this property
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Folder path');
cb.setValue(excludedFolder.path || '');

View file

@ -95,6 +95,7 @@ export function addExcludePatternListItem(
const setting = new Setting(containerEl);
setting.setClass('fn-exclude-folder-list');
setting.addSearch((cb) => {
// @ts-expect-error Obsidian's public types don't include containerEl on this control
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Pattern');
cb.setValue(pattern.string);

View file

@ -132,6 +132,7 @@ export function addWhitelistFolderListItem(
plugin,
true,
);
// @ts-expect-error Obsidian's public types don't include this property
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Folder path');
cb.setValue(whitelistedFolder.path);

View file

@ -97,6 +97,7 @@ export function addWhitelistedPatternListItem(
const setting = new Setting(containerEl);
setting.setClass('fn-exclude-folder-list');
setting.addSearch((cb) => {
// @ts-expect-error Obsidian's public types don't expose containerEl on this control
cb.containerEl.addClass('fn-exclude-folder-path');
cb.setPlaceholder('Pattern');
cb.setValue(pattern.string);

View file

@ -25,12 +25,12 @@ interface WrappedUpdateData {
export class FrontMatterTitlePluginHandler {
plugin: FolderNotesPlugin;
app!: App;
app: App;
api: ApiInterface | null = null;
deffer: DeferInterface | null = null;
modifiedFolders: Map<string, TFolder> = new Map();
eventRef: ListenerRef<'manager:update'> | null = null;
dispatcher: EventDispatcherInterface<Events> | null = null;
eventRef: ListenerRef<'manager:update'>;
dispatcher: EventDispatcherInterface<Events>;
constructor(plugin: FolderNotesPlugin) {
this.plugin = plugin;
this.app = plugin.app;
@ -66,13 +66,13 @@ export class FrontMatterTitlePluginHandler {
// this.plugin.app.vault.getFiles().forEach((file) => {
// this.handleRename({ id: '', result: false, path: file.path }, false);
// });
plugin.updateAllBreadcrumbs();
this.plugin.updateAllBreadcrumbs();
}
})();
}
deleteEvent(): void {
if (this.eventRef && this.dispatcher) {
if (this.eventRef) {
this.dispatcher.removeListener(this.eventRef);
}
}

View file

@ -158,7 +158,6 @@ async function setupFolderTitle(
});
}
// eslint-disable-next-line complexity
async function updateFolderNamesInPath(
plugin: FolderNotesPlugin,
titleContainer: HTMLElement,
@ -170,14 +169,14 @@ async function updateFolderNamesInPath(
if (titleParent?.childNodes.length === 0) {
titleContainer.classList.remove('hide-folder-note-title-in-path');
}
for (const breadcrumb of Array.from(titleParent?.childNodes ?? [])) {
if (!(breadcrumb instanceof HTMLElement)) continue;
// eslint-disable-next-line complexity
titleParent?.childNodes.forEach(async (breadcrumb: HTMLElement) => {
if (!(breadcrumb.instanceOf(HTMLElement))) return;
if (breadcrumb.classList.contains('view-header-breadcrumb-separator')) {
if (breadcrumb.nextSibling === null) {
breadcrumb.classList.add('is-last-separator');
}
continue;
return;
}
path += breadcrumb.getAttribute('old-name') ?? (breadcrumb).innerText.trim();
@ -202,12 +201,8 @@ async function updateFolderNamesInPath(
viewHeaderTitle.classList.remove('path-is-folder-note');
}
}
if (!folderNote) {
breadcrumb.classList.remove('has-folder-note');
breadcrumb.removeAttribute('data-path');
continue;
}
breadcrumb.classList.add('has-folder-note');
if (!folderNote) return;
if (folderNote) breadcrumb.classList.add('has-folder-note');
breadcrumb?.setAttribute('data-path', path.slice(0, -TRAILING_SLASH_LENGTH));
if (!breadcrumb.onclick) {
breadcrumb.addEventListener('click', (e) => {
@ -221,7 +216,7 @@ async function updateFolderNamesInPath(
true,
);
}
}
});
}
// Schedules a callback to run when the browser is idle, or after a timeout as a fallback.
@ -235,6 +230,6 @@ function scheduleIdle(callback: () => void, options?: { timeout: number }): void
};
windowWithIdle.requestIdleCallback(callback, options);
} else {
globalThis.setTimeout(callback, options?.timeout || DEFAULT_IDLE_TIMEOUT);
window.setTimeout(callback, options?.timeout || DEFAULT_IDLE_TIMEOUT);
}
}

View file

@ -1,15 +1,10 @@
import { TFolder, TFile, View, type TAbstractFile } from 'obsidian';
import { TFolder, TFile, View, TAbstractFile } from 'obsidian';
import type { FileExplorerWorkspaceLeaf, FileExplorerView } from 'src/globals';
import { getFolderNote } from './folderNoteFunctions';
import type FolderNotesPlugin from 'src/main';
import type { FileTreeItem } from 'obsidian-typings';
import type { FileExplorerLeaf, FileTreeItem, TreeNode } from 'obsidian-typings';
import type FolderOverviewPlugin from 'src/obsidian-folder-overview/src/main';
function isFileExplorerWorkspaceLeaf(leaf: unknown): leaf is FileExplorerWorkspaceLeaf {
const tabHeaderEl = (leaf as { tabHeaderEl?: HTMLElement | null } | null)?.tabHeaderEl;
return tabHeaderEl?.dataset?.type === 'file-explorer';
}
export function getFileNameFromPathString(path: string): string {
return path.substring(path.lastIndexOf('/') >= 0 ? path.lastIndexOf('/') + 1 : 0);
}
@ -48,28 +43,29 @@ export function getParentFolderPath(path: string): string {
export function getFileExplorer(
plugin: FolderNotesPlugin | FolderOverviewPlugin,
): FileExplorerWorkspaceLeaf | undefined {
let leaf = plugin.app.workspace.getLeavesOfType('file-explorer')[0];
// eslint-disable-next-line max-len
let leaf = plugin.app.workspace.getLeavesOfType('file-explorer')[0] as unknown as FileExplorerWorkspaceLeaf;
if (!leaf) { return undefined; }
/* make.md plugin integration */
if ((leaf.containerEl?.lastChild as HTMLElement)?.dataset?.type === 'mk-path-view') {
plugin.app.workspace.iterateAllLeaves((x) => {
if (isFileExplorerWorkspaceLeaf(x)) {
leaf = x;
if ((x as FileExplorerWorkspaceLeaf).tabHeaderEl?.dataset?.type === 'file-explorer') {
leaf = x as FileExplorerWorkspaceLeaf;
}
});
}
return isFileExplorerWorkspaceLeaf(leaf) ? leaf : undefined;
return leaf;
}
export function getFileExplorerView(plugin: FolderNotesPlugin): FileExplorerView | undefined {
return getFileExplorer(plugin)?.view;
}
export function getFocusedItem(plugin: FolderNotesPlugin): FileTreeItem | null {
const fileExplorer = getFileExplorer(plugin);
export function getFocusedItem(plugin: FolderNotesPlugin): TreeNode<FileTreeItem> | null {
const fileExplorer = getFileExplorer(plugin) as unknown as FileExplorerLeaf | undefined;
if (!fileExplorer) { return null; }
const { focusedItem } = fileExplorer.view.tree;
return focusedItem;

View file

@ -221,13 +221,9 @@ export default class FolderNotesPlugin extends Plugin {
registerFileExplorerObserver(this);
const fileExplorer = getFileExplorer(this);
// @ts-expect-error use internal API
const infinityScroll = fileExplorer?.view?.tree?.infinityScroll;
if (infinityScroll) {
// increase infinity scroll buffer to show hidden folder notes
// Issue: https://github.com/LostPaul/obsidian-folder-notes/issues/274
infinityScroll.rootMargin = 1.5;
if (fileExplorer) {
// @ts-expect-error use internal API
fileExplorer.view.tree.infinityScroll.rootMargin = 1.5;
}
this.registerView(FOLDER_OVERVIEW_VIEW, (leaf: WorkspaceLeaf) => {
@ -306,13 +302,13 @@ export default class FolderNotesPlugin extends Plugin {
clipboardProto.handleDragOver as unknown as ClipboardManagerLike['handleDragOver'];
const originalHandleDrop =
clipboardProto.handleDrop as unknown as ClipboardManagerLike['handleDrop'];
const folderNotePlugin = this;
clipboardProto.handleDragOver = function (evt: DragEvent, ...args: unknown[]): void {
const { dragManager } = (this as ClipboardManagerLike).app;
clipboardProto.handleDragOver = (evt: DragEvent, ...args: unknown[]): void => {
const { dragManager } = clipboardManager.app;
const draggable = dragManager?.draggable;
if (draggable?.file instanceof TFolder) {
const folderNote = getFolderNote(folderNotePlugin, draggable.file.path);
const folderNote = getFolderNote(this, draggable.file.path);
if (folderNote) {
dragManager.setAction(
window.i18next.t('interface.drag-and-drop.insert-link-here'),
@ -321,22 +317,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 as ClipboardManagerLike).app;
clipboardProto.handleDrop = (evt: DragEvent, ...args: unknown[]): void => {
const { dragManager } = clipboardManager.app;
const draggable = dragManager?.draggable;
if (draggable?.file instanceof TFolder) {
const folderNote = getFolderNote(folderNotePlugin, 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) {

@ -1 +1 @@
Subproject commit f047859cc9a7f1c04ad35ecdc440a31a300bc6c2
Subproject commit 2bc9795b1feede51554dafacfe56123af5e2f2a6

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'),

View file

@ -1,4 +1,4 @@
/* eslint-disable @typescript-eslint/unbound-method */
/* eslint-disable max-len */
import { Setting, Platform } from 'obsidian';
import type { SettingsTab } from './SettingsTab';
@ -11,7 +11,7 @@ import { refreshAllFolderStyles } from '../functions/styleFunctions';
import BackupWarningModal from './modals/BackupWarning';
import RenameFolderNotesModal from './modals/RenameFns';
let debounceTimer: number | undefined;
let debounceTimer: ReturnType<typeof setTimeout>;
// eslint-disable-next-line complexity
export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
@ -54,7 +54,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
'Rename all existing folder notes',
'When you click on "Confirm" all existing folder notes will be renamed to the new folder note name.',
settingsTab.renameFolderNotes,
[settingsTab.plugin.settings.oldFolderNoteName ?? '{{folder_name}}'])
[])
.open();
}),
);
@ -128,7 +128,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
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.',
);
@ -482,7 +482,7 @@ export async function renderGeneral(settingsTab: SettingsTab): Promise<void> {
settingsTab.settingsPage.createEl('h3', { text: 'Integration & compatibility' });
const desc1 = document.createDocumentFragment();
const desc1 = activeDocument.createDocumentFragment();
const link = activeDocument.createElement('a');
link.href = 'https://github.com/snezhig/obsidian-front-matter-title';

View file

@ -298,7 +298,7 @@ export class SettingsTab extends PluginSettingTab {
tabEl.addClass('fn-settings-tab-active');
}
tabEl.addEventListener('click', () => {
for (const child of Array.from(tabBar.children)) {
for (const child of tabBar.children) {
(child as HTMLElement).classList.remove('fn-settings-tab-active');
if (!plugin) { return; }
plugin.settings.settingsTab = tabId.toLocaleLowerCase();

View file

@ -5,15 +5,15 @@ export default class BackupWarningModal extends Modal {
plugin: FolderNotesPlugin;
title: string;
desc: string;
callback: (oldMethod: string) => void;
args: [string];
callback: (...args: unknown[]) => void;
args: unknown[];
constructor(
plugin: FolderNotesPlugin,
title: string,
description: string,
callback: (oldMethod: string) => void,
args: [string],
callback: (...args: unknown[]) => void,
args: unknown[] = [],
) {
super(plugin.app);
this.plugin = plugin;

View file

@ -7,8 +7,8 @@ export default class RenameFolderNotesModal extends BackupWarningModal {
plugin: FolderNotesPlugin,
title: string,
description: string,
callback: (oldMethod: string) => void,
args: [string],
callback: (...args: unknown[]) => void,
args: unknown[] = [],
) {
super(plugin, title, description, callback, args);
}

View file

@ -1,6 +1,5 @@
{
"compilerOptions": {
"ignoreDeprecations": "6.0",
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,