Compare commits

..

17 commits
1.8.21 ... main

Author SHA1 Message Date
Paul
494d159107
Merge pull request #333 from kyan001/patch-1
Bug Fix #330 & #332
2026-06-26 14:16:55 +02:00
Kyan
0532186cb2
Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-26 11:52:15 +08:00
Kyan
f6a28dab67
Bug Fix #330 & #332
In the breadcrumb path processing loop, the function exits too early when it encounters a level without a folder note, so later levels are never checked. Because of this early return, clicking a deeper path segment does not open its folder note unless all ancestor folders also have folder notes.
2026-06-26 11:38:34 +08:00
Paul
85cd981d8d Update main.ts 2026-06-15 11:51:21 +02:00
Paul
f172b8508f Update manifest.json 2026-06-05 10:06:51 +02:00
Paul
4a71e3d795 Fix #328 2026-06-05 10:06:39 +02:00
Paul
101758a52b Fix plugin not loading on some devices 2026-06-03 10:02:40 +02:00
Paul
a493120095 Update manifest.json 2026-06-02 16:51:48 +02:00
Paul
8248de14d9 Update obsidian-folder-overview 2026-06-02 16:51:36 +02:00
Paul
961914c4af Update manifest.json 2026-06-02 16:48:23 +02:00
Paul
71e3c79078 Merge branch 'main' of https://github.com/LostPaul/obsidian-folder-notes 2026-06-02 16:48:02 +02:00
Paul
432ecc6072 Fix build issues 2026-06-02 16:47:46 +02:00
Paul
71c2a309b7
Update manifest.json 2026-06-01 20:53:42 +02:00
github-actions[bot]
3bdc358643 Update manifest.json to version 2026-06-01 16:43:00 +00:00
Paul
960fa91b52 Update manifest.json 2026-06-01 18:42:38 +02:00
Paul
644c5ce099 Merge branch 'main' of https://github.com/LostPaul/obsidian-folder-notes 2026-06-01 18:40:51 +02:00
Paul
4215b22559
Update release.yaml 2026-06-01 18:39:31 +02:00
23 changed files with 4863 additions and 1469 deletions

View file

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

View file

@ -1,12 +1,12 @@
{
"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
}
"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
}

6154
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.38.0",
"@typescript-eslint/parser": "^8.38.0",
"@typescript-eslint/eslint-plugin": "^8.60.0",
"@typescript-eslint/parser": "^8.60.0",
"builtin-modules": "3.3.0",
"esbuild": "0.14.47",
"eslint": "^9.32.0",
"eslint": "^10.4.1",
"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": "^4.8.4",
"typescript": "^6.0.3",
"typescript-eslint": "^8.38.0"
},
"dependencies": {

View file

@ -12,7 +12,7 @@ export class ExcludedFolder {
position: number;
excludeFromFolderOverview: boolean;
hideInSettings: boolean;
detached: boolean;
detached: boolean = false;
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;
detached: boolean = false;
detachedFilePath?: string;
showFolderNote: boolean;
constructor(

View file

@ -5,14 +5,14 @@ export class WhitelistedFolder {
path: string;
string: string;
subFolders: boolean;
enableSync: boolean;
enableAutoCreate: boolean;
enableFolderNote: boolean;
disableCollapsing: boolean;
showInFolderOverview: boolean;
hideInFileExplorer: boolean;
enableSync: boolean = false;
enableAutoCreate: boolean = false;
enableFolderNote: boolean = false;
disableCollapsing: boolean = false;
showInFolderOverview: boolean = false;
hideInFileExplorer: boolean = false;
position: number;
hideInSettings: boolean;
hideInSettings: boolean = false;
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;
enableAutoCreate: boolean;
enableFolderNote: boolean;
disableCollapsing: boolean;
showInFolderOverview: boolean;
hideInFileExplorer: boolean;
hideInSettings: boolean;
enableSync: boolean = false;
enableAutoCreate: boolean = false;
enableFolderNote: boolean = false;
disableCollapsing: boolean = false;
showInFolderOverview: boolean = false;
hideInFileExplorer: boolean = false;
hideInSettings: boolean = false;
constructor(
pattern: string,
position: number,

View file

@ -235,7 +235,6 @@ 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,7 +95,6 @@ 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,7 +132,6 @@ 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,7 +97,6 @@ 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'>;
dispatcher: EventDispatcherInterface<Events>;
eventRef: ListenerRef<'manager:update'> | null = null;
dispatcher: EventDispatcherInterface<Events> | null = null;
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);
// });
this.plugin.updateAllBreadcrumbs();
plugin.updateAllBreadcrumbs();
}
})();
}
deleteEvent(): void {
if (this.eventRef) {
if (this.eventRef && this.dispatcher) {
this.dispatcher.removeListener(this.eventRef);
}
}

View file

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

View file

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

View file

@ -221,9 +221,13 @@ export default class FolderNotesPlugin extends Plugin {
registerFileExplorerObserver(this);
const fileExplorer = getFileExplorer(this);
if (fileExplorer) {
// @ts-expect-error use internal API
fileExplorer.view.tree.infinityScroll.rootMargin = 1.5;
// @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;
}
this.registerView(FOLDER_OVERVIEW_VIEW, (leaf: WorkspaceLeaf) => {
@ -302,13 +306,13 @@ export default class FolderNotesPlugin extends Plugin {
clipboardProto.handleDragOver as unknown as ClipboardManagerLike['handleDragOver'];
const originalHandleDrop =
clipboardProto.handleDrop as unknown as ClipboardManagerLike['handleDrop'];
clipboardProto.handleDragOver = (evt: DragEvent, ...args: unknown[]): void => {
const { dragManager } = clipboardManager.app;
const folderNotePlugin = this;
clipboardProto.handleDragOver = function (evt: DragEvent, ...args: unknown[]): void {
const { dragManager } = (this as ClipboardManagerLike).app;
const draggable = dragManager?.draggable;
if (draggable?.file instanceof TFolder) {
const folderNote = getFolderNote(this, draggable.file.path);
const folderNote = getFolderNote(folderNotePlugin, draggable.file.path);
if (folderNote) {
dragManager.setAction(
window.i18next.t('interface.drag-and-drop.insert-link-here'),
@ -317,22 +321,22 @@ export default class FolderNotesPlugin extends Plugin {
}
}
originalHandleDragOver(evt, ...args);
return originalHandleDragOver.call(this, evt, ...args);
};
clipboardProto.handleDrop = (evt: DragEvent, ...args: unknown[]): void => {
const { dragManager } = clipboardManager.app;
clipboardProto.handleDrop = function (evt: DragEvent, ...args: unknown[]): void {
const { dragManager } = (this as ClipboardManagerLike).app;
const draggable = dragManager?.draggable;
if (draggable?.file instanceof TFolder) {
const folderNote = getFolderNote(this, draggable.file.path);
const folderNote = getFolderNote(folderNotePlugin, draggable.file.path);
if (folderNote) {
draggable.file = folderNote;
draggable.type = 'file';
}
}
originalHandleDrop(evt, ...args);
return originalHandleDrop.call(this, evt, ...args);
};
if (this.settings.fvGlobalSettings.autoUpdateLinks) {

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

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 = activeDocument.createDocumentFragment();
const desc3 = document.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: ReturnType<typeof setTimeout>;
let debounceTimer: number | undefined;
// 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 = activeDocument.createDocumentFragment();
const desc0 = document.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 = activeDocument.createDocumentFragment();
const desc1 = document.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 tabBar.children) {
for (const child of Array.from(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: (...args: unknown[]) => void;
args: unknown[];
callback: (oldMethod: string) => void;
args: [string];
constructor(
plugin: FolderNotesPlugin,
title: string,
description: string,
callback: (...args: unknown[]) => void,
args: unknown[] = [],
callback: (oldMethod: string) => void,
args: [string],
) {
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: (...args: unknown[]) => void,
args: unknown[] = [],
callback: (oldMethod: string) => void,
args: [string],
) {
super(plugin, title, description, callback, args);
}

View file

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