mirror of
https://github.com/lostpaul/obsidian-folder-notes.git
synced 2026-07-22 07:40:24 +00:00
Compare commits
39 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
494d159107 | ||
|
|
0532186cb2 | ||
|
|
f6a28dab67 | ||
|
|
85cd981d8d | ||
|
|
f172b8508f | ||
|
|
4a71e3d795 | ||
|
|
101758a52b | ||
|
|
a493120095 | ||
|
|
8248de14d9 | ||
|
|
961914c4af | ||
|
|
71e3c79078 | ||
|
|
432ecc6072 | ||
|
|
71c2a309b7 | ||
|
|
3bdc358643 | ||
|
|
960fa91b52 | ||
|
|
644c5ce099 | ||
|
|
937e5939bc | ||
|
|
4215b22559 | ||
|
|
c6c334bd16 | ||
|
|
32ce9f55ed | ||
|
|
b41cc7641d | ||
|
|
ec73ad869f | ||
|
|
681320e2f2 | ||
|
|
a4951dbd86 | ||
|
|
3a744e06b8 | ||
|
|
aae916aafa | ||
|
|
1e99850592 | ||
|
|
b296a3d7e6 | ||
|
|
9c1b4aeba2 | ||
|
|
3fce80d6fb | ||
|
|
2683ee1a1a | ||
|
|
86bd49d873 | ||
|
|
89f5db7bf0 | ||
|
|
5ed694654c | ||
|
|
1ace668c8f | ||
|
|
3daea7efd3 | ||
|
|
347c409619 | ||
|
|
8fbf999df4 | ||
|
|
8344490906 |
51 changed files with 10420 additions and 1831 deletions
15
.github/workflows/release.yaml
vendored
15
.github/workflows/release.yaml
vendored
|
|
@ -6,6 +6,11 @@ on:
|
|||
tags:
|
||||
- "[0-9]+.[0-9]+.[0-9]+"
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
attestations: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -57,6 +62,16 @@ jobs:
|
|||
npm install
|
||||
npm run fn-build --if-present
|
||||
|
||||
- name: Generate artifact attestation for main.js
|
||||
uses: actions/attest@v4
|
||||
with:
|
||||
subject-path: 'main.js'
|
||||
|
||||
- name: Generate artifact attestation for styles.css
|
||||
uses: actions/attest@v4
|
||||
with:
|
||||
subject-path: 'styles.css'
|
||||
|
||||
- name: Create Plugin release
|
||||
uses: ncipollo/release-action@v1.12.0
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import obsidianmd from "eslint-plugin-obsidianmd";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default [
|
||||
|
|
@ -14,9 +15,22 @@ export default [
|
|||
"**/dist/**",
|
||||
],
|
||||
plugins: {
|
||||
"@typescript-eslint": tseslint.plugin
|
||||
"@typescript-eslint": tseslint.plugin,
|
||||
obsidianmd
|
||||
},
|
||||
rules: {
|
||||
"obsidianmd/no-tfile-tfolder-cast": "error",
|
||||
"obsidianmd/rule-custom-message": [
|
||||
"error",
|
||||
{
|
||||
"no-console": {
|
||||
messages: {
|
||||
"Unexpected console statement. Only these console methods are allowed: warn, error, debug.": "Avoid unnecessary logging to console. See https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines#Avoid+unnecessary+logging+to+console",
|
||||
},
|
||||
options: [{ allow: ["warn", "error", "debug"] }],
|
||||
}
|
||||
}
|
||||
],
|
||||
"no-unused-vars": "off",
|
||||
"quotes": [
|
||||
"error",
|
||||
|
|
@ -143,7 +157,7 @@ export default [
|
|||
],
|
||||
'max-len': [
|
||||
'warn', {
|
||||
code: 100,
|
||||
code: 110,
|
||||
ignoreComments: true,
|
||||
}
|
||||
],
|
||||
|
|
@ -160,4 +174,4 @@ export default [
|
|||
],
|
||||
}
|
||||
}
|
||||
];
|
||||
];
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "folder-notes",
|
||||
"name": "Folder notes beta",
|
||||
"version": "1.8.17-5-beta",
|
||||
"version": "1.8.20-1-beta",
|
||||
"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",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"id": "folder-notes",
|
||||
"name": "Folder notes",
|
||||
"version": "1.8.18",
|
||||
"minAppVersion": "0.15.0",
|
||||
"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",
|
||||
|
|
|
|||
11051
package-lock.json
generated
11051
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -18,18 +18,19 @@
|
|||
"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",
|
||||
"jiti": "^2.5.1",
|
||||
"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": {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
@ -126,7 +128,7 @@ export class Commands {
|
|||
id: `create-${type}-folder-note-for-active-file-explorer-folder`,
|
||||
name: `Create ${type} folder note for current active folder in file explorer`,
|
||||
checkCallback: (checking: boolean) => {
|
||||
const folder = getFileExplorerActiveFolder();
|
||||
const folder = getFileExplorerActiveFolder(this.plugin);
|
||||
if (!folder) return false;
|
||||
// Is there already a folder note for the active folder?
|
||||
const folderNote = getFolderNote(this.plugin, folder.path);
|
||||
|
|
@ -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);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -160,7 +162,7 @@ export class Commands {
|
|||
id: 'delete-folder-note-of-active-file-explorer-folder',
|
||||
name: 'Delete folder note of current active folder in file explorer',
|
||||
checkCallback: (checking: boolean) => {
|
||||
const folder = getFileExplorerActiveFolder();
|
||||
const folder = getFileExplorerActiveFolder(this.plugin);
|
||||
if (!folder) return false;
|
||||
// Is there any folder note for the active folder?
|
||||
const folderNote = getFolderNote(this.plugin, folder.path);
|
||||
|
|
@ -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,14 +184,14 @@ 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({
|
||||
id: 'open-folder-note-of-active-file-explorer-folder',
|
||||
name: 'Open folder note of current active folder in file explorer',
|
||||
checkCallback: (checking: boolean) => {
|
||||
const folder = getFileExplorerActiveFolder();
|
||||
const folder = getFileExplorerActiveFolder(this.plugin);
|
||||
if (!folder) return false;
|
||||
// Is there any folder note for the active folder?
|
||||
const folderNote = getFolderNote(this.plugin, folder.path);
|
||||
|
|
@ -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}]]`);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export class ExcludePattern {
|
|||
enableCollapsing: boolean;
|
||||
excludeFromFolderOverview: boolean;
|
||||
hideInSettings: boolean;
|
||||
detached: boolean;
|
||||
detached: boolean = false;
|
||||
detachedFilePath?: string;
|
||||
showFolderNote: boolean;
|
||||
constructor(
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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 || '');
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Keymap, Platform } from 'obsidian';
|
||||
import type FolderNotesPlugin from 'src/main';
|
||||
import { getFolderNote } from 'src/functions/folderNoteFunctions';
|
||||
import { getFolderNote, getFolderNoteFolder } from 'src/functions/folderNoteFunctions';
|
||||
import { handleViewHeaderClick } from './handleClick';
|
||||
import { getExcludedFolder } from 'src/ExcludeFolders/functions/folderFunctions';
|
||||
import { updateCSSClassesForFolder } from 'src/functions/styleFunctions';
|
||||
|
|
@ -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) {
|
||||
|
|
@ -158,27 +158,60 @@ async function setupFolderTitle(
|
|||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
async function updateFolderNamesInPath(
|
||||
plugin: FolderNotesPlugin,
|
||||
titleContainer: HTMLElement,
|
||||
): Promise<void> {
|
||||
const headers = titleContainer.querySelectorAll('span.view-header-breadcrumb');
|
||||
const titleParent = titleContainer.querySelector('.view-header-title-parent');
|
||||
let path = '';
|
||||
const TRAILING_SLASH_LENGTH = 1;
|
||||
headers.forEach(async (breadcrumb: HTMLElement) => {
|
||||
path += breadcrumb.getAttribute('old-name') ?? (breadcrumb as HTMLElement).innerText.trim();
|
||||
|
||||
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;
|
||||
if (breadcrumb.classList.contains('view-header-breadcrumb-separator')) {
|
||||
if (breadcrumb.nextSibling === null) {
|
||||
breadcrumb.classList.add('is-last-separator');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
path += breadcrumb.getAttribute('old-name') ?? (breadcrumb).innerText.trim();
|
||||
path += '/';
|
||||
const folderPath = path.slice(0, -TRAILING_SLASH_LENGTH);
|
||||
|
||||
const excludedFolder = getExcludedFolder(plugin, folderPath, true);
|
||||
if (excludedFolder?.disableFolderNote) return;
|
||||
const folderNote = getFolderNote(plugin, folderPath);
|
||||
if (!folderNote) return;
|
||||
if (folderNote) breadcrumb.classList.add('has-folder-note');
|
||||
const viewHeaderTitle = titleContainer.querySelector('.view-header-title');
|
||||
|
||||
if (viewHeaderTitle && folderNote) {
|
||||
const filePath = path + (viewHeaderTitle as HTMLElement).innerText.trim() + '.md';
|
||||
const file = plugin.app.vault.getAbstractFileByPath(
|
||||
filePath);
|
||||
const folder = getFolderNoteFolder(plugin, folderNote, file?.name ?? '');
|
||||
if (folder && file && file.path === folderNote?.path && file.parent?.path !== '/') {
|
||||
viewHeaderTitle.parentElement?.classList.add('hide-folder-note-title-in-path');
|
||||
viewHeaderTitle.classList.add('path-is-folder-note');
|
||||
} else {
|
||||
viewHeaderTitle.parentElement?.classList.remove('hide-folder-note-title-in-path');
|
||||
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');
|
||||
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 });
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +221,7 @@ async function updateFolderNamesInPath(
|
|||
true,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Schedules a callback to run when the browser is idle, or after a timeout as a fallback.
|
||||
|
|
@ -202,6 +235,6 @@ function scheduleIdle(callback: () => void, options?: { timeout: number }): void
|
|||
};
|
||||
windowWithIdle.requestIdleCallback(callback, options);
|
||||
} else {
|
||||
setTimeout(callback, options?.timeout || DEFAULT_IDLE_TIMEOUT);
|
||||
globalThis.setTimeout(callback, options?.timeout || DEFAULT_IDLE_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
removeCSSClassFromFileExplorerEL,
|
||||
addCSSClassToFileExplorerEl,
|
||||
} from 'src/functions/styleFunctions';
|
||||
import { isFileInAttachmentFolder } from '@app/functions/utils';
|
||||
|
||||
export async function handleCreate(file: TAbstractFile, plugin: FolderNotesPlugin): Promise<void> {
|
||||
if (!plugin.app.workspace.layoutReady) return;
|
||||
|
|
@ -50,7 +51,8 @@ async function handleFileCreation(file: TFile, plugin: FolderNotesPlugin): Promi
|
|||
if (folderNote && folderNote.path === file.path) {
|
||||
addCSSClassToFileExplorerEl(folder.path, 'has-folder-note', false, plugin);
|
||||
addCSSClassToFileExplorerEl(file.path, 'is-folder-note', false, plugin);
|
||||
} else if (plugin.settings.autoCreateForFiles) {
|
||||
} else if (plugin.settings.autoCreateForFiles && !isFileInAttachmentFolder(plugin, file)) {
|
||||
if (!plugin.settings.supportedFileTypes.includes(file.extension)) { return; }
|
||||
if (!file.parent) { return; }
|
||||
const newFolder = await plugin.app.fileManager.createNewFolder(file.parent);
|
||||
turnIntoFolderNote(plugin, file, newFolder);
|
||||
|
|
@ -78,6 +80,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ function shouldCreateFolderNote(
|
|||
detachedExcludedFolder: ExcludedFolder | undefined,
|
||||
): boolean {
|
||||
return !excludedFolder?.disableFolderNote
|
||||
&& folderName === (newFolder as TFolder)?.name
|
||||
&& folderName === (newFolder)?.name
|
||||
&& !detachedExcludedFolder;
|
||||
}
|
||||
|
||||
|
|
@ -384,7 +384,7 @@ function shouldRemoveFolderNoteClasses(
|
|||
folderName: string,
|
||||
newFolder: TAbstractFile | null,
|
||||
): boolean {
|
||||
return excludedFolder?.disableFolderNote || (folderName !== (newFolder as TFolder)?.name);
|
||||
return excludedFolder?.disableFolderNote || (folderName !== (newFolder)?.name);
|
||||
}
|
||||
|
||||
function handleFolderNoteCreation(
|
||||
|
|
@ -424,8 +424,8 @@ function shouldRenameFolderOnFileRename(
|
|||
): boolean {
|
||||
if (!oldFolder) return false;
|
||||
|
||||
const oldFolderAsFolder = oldFolder as TFolder;
|
||||
const newFolderAsFolder = newFolder as TFolder;
|
||||
const oldFolderAsFolder = oldFolder;
|
||||
const newFolderAsFolder = newFolder;
|
||||
|
||||
return (oldFolderName === oldFolderAsFolder.name
|
||||
&& newFolderAsFolder?.path === oldFolderAsFolder.path)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,19 +1,29 @@
|
|||
import type { WorkspaceLeaf, App } from 'obsidian';
|
||||
import type { WorkspaceLeaf, App, TFile } from 'obsidian';
|
||||
|
||||
interface ExcalidrawPlugin {
|
||||
setExcalidrawView(leaf: WorkspaceLeaf): void;
|
||||
openDrawing(file: TFile): void;
|
||||
getBlankDrawing(): Promise<string>;
|
||||
}
|
||||
|
||||
export async function openExcalidrawView(
|
||||
app: App,
|
||||
leaf: WorkspaceLeaf,
|
||||
file: TFile,
|
||||
): Promise<void> {
|
||||
const { excalidraw, excalidrawEnabled } = await getExcalidrawPlugin(app);
|
||||
if (excalidrawEnabled && excalidraw) {
|
||||
excalidraw.setExcalidrawView(leaf);
|
||||
excalidraw.openDrawing(file);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getDefaultTemplate(app: App): Promise<string> {
|
||||
const { excalidraw, excalidrawEnabled } = await getExcalidrawPlugin(app);
|
||||
if (excalidrawEnabled && excalidraw) {
|
||||
return excalidraw.getBlankDrawing();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
export async function getExcalidrawPlugin(
|
||||
app: App,
|
||||
): Promise<{ excalidraw: ExcalidrawPlugin | null; excalidrawEnabled: boolean }> {
|
||||
|
|
|
|||
|
|
@ -20,13 +20,14 @@ import {
|
|||
updateExcludedFolder,
|
||||
} from '../ExcludeFolders/functions/folderFunctions';
|
||||
import { ExcludedFolder } from '../ExcludeFolders/ExcludeFolder';
|
||||
import { openExcalidrawView } from './excalidraw';
|
||||
import { getDefaultTemplate, openExcalidrawView } from './excalidraw';
|
||||
import { AskForExtensionModal } from 'src/modals/AskForExtension';
|
||||
import {
|
||||
addCSSClassToFileExplorerEl,
|
||||
removeCSSClassFromFileExplorerEL,
|
||||
removeActiveFolder,
|
||||
setActiveFolder,
|
||||
updateCSSClassesForFolderNote,
|
||||
} from 'src/functions/styleFunctions';
|
||||
import {
|
||||
getFolderNameFromPathString,
|
||||
|
|
@ -35,21 +36,6 @@ import {
|
|||
} from 'src/functions/utils';
|
||||
|
||||
|
||||
const defaultExcalidrawTemplate = `---
|
||||
|
||||
excalidraw-plugin: parsed
|
||||
tags: [excalidraw]
|
||||
|
||||
---
|
||||
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
|
||||
|
||||
|
||||
%%
|
||||
# Drawing
|
||||
\`\`\`json
|
||||
{'type":"excalidraw","version":2,"source":"https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.9.20","elements":[],"appState":{"gridSize":null,"viewBackgroundColor":"#ffffff'}}
|
||||
\`\`\`
|
||||
%%`;
|
||||
|
||||
export async function createFolderNote(
|
||||
plugin: FolderNotesPlugin,
|
||||
|
|
@ -121,7 +107,7 @@ export async function createFolderNote(
|
|||
}
|
||||
await leaf.openFile(folderNote);
|
||||
if (plugin.settings.folderNoteType === '.excalidraw' || extension === '.excalidraw') {
|
||||
openExcalidrawView(plugin.app, leaf);
|
||||
openExcalidrawView(plugin.app, folderNote);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,10 +175,10 @@ 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 = defaultExcalidrawTemplate;
|
||||
content = await getDefaultTemplate(plugin.app);
|
||||
}
|
||||
} else {
|
||||
plugin.app.vault.readBinary(templateFile).then(async (data) => {
|
||||
|
|
@ -208,7 +194,7 @@ async function handleCreateFolderNote(plugin: FolderNotesPlugin, folderNoteType:
|
|||
plugin.settings.folderNoteType === '.excalidraw' ||
|
||||
extension === '.excalidraw'
|
||||
) {
|
||||
content = defaultExcalidrawTemplate;
|
||||
content = await getDefaultTemplate(plugin.app);
|
||||
} else if (plugin.settings.folderNoteType === '.canvas') {
|
||||
content = '{}';
|
||||
}
|
||||
|
|
@ -227,7 +213,8 @@ async function handleTurnNoteIntoFolderNote(
|
|||
): Promise<void> {
|
||||
deleteExcludedFolder(plugin, detachedFolder);
|
||||
removeCSSClassFromFileExplorerEL(folderNote?.path, 'is-folder-note', false, plugin);
|
||||
const folder = plugin.app.vault.getAbstractFileByPath(folderPath) as TFolder;
|
||||
const folder = plugin.app.vault.getAbstractFileByPath(folderPath);
|
||||
if (!(folder instanceof TFolder)) return;
|
||||
if (!folderNote || folderNote.basename !== fileName) return;
|
||||
let count = 1;
|
||||
const baseName = removeExtension(folderNote.path);
|
||||
|
|
@ -316,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);
|
||||
|
|
@ -506,6 +493,7 @@ export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile): void {
|
|||
undefined,
|
||||
plugin,
|
||||
);
|
||||
excludedFolder.showFolderNote = true;
|
||||
excludedFolder.hideInSettings = true;
|
||||
excludedFolder.disableFolderNote = true;
|
||||
excludedFolder.disableSync = true;
|
||||
|
|
@ -514,6 +502,7 @@ export function detachFolderNote(plugin: FolderNotesPlugin, file: TFile): void {
|
|||
excludedFolder.detached = true;
|
||||
excludedFolder.detachedFilePath = file.path;
|
||||
addExcludedFolder(plugin, excludedFolder);
|
||||
void updateCSSClassesForFolderNote(file.path, plugin);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -567,6 +556,13 @@ export function getFolderNoteFolder(
|
|||
filePath = folderNote.path;
|
||||
}
|
||||
const folderName = extractFolderName(plugin.settings.folderNoteName, fileName);
|
||||
if (!plugin.settings.folderNoteName.includes('{{folder_name}}') && plugin.settings.storageLocation === 'insideFolder') {
|
||||
if (folderNote instanceof TFile) {
|
||||
return folderNote.parent;
|
||||
}
|
||||
const file = plugin.app.vault.getAbstractFileByPath(filePath);
|
||||
return file instanceof TFile ? file.parent : null;
|
||||
}
|
||||
if (!folderName) return null;
|
||||
let folderPath = getFolderPathFromString(filePath);
|
||||
if (plugin.settings.storageLocation === 'parentFolder') {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export function refreshAllFolderStyles(forceReload = false, plugin: FolderNotesP
|
|||
/**
|
||||
* @description Updates the CSS classes for a specific folder in the file explorer.
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
export async function updateCSSClassesForFolder(
|
||||
folderPath: string,
|
||||
plugin: FolderNotesPlugin,
|
||||
|
|
@ -45,6 +46,7 @@ export async function updateCSSClassesForFolder(
|
|||
if (!folderNote || detachedFolderNote) {
|
||||
removeCSSClassFromFileExplorerEL(folder?.path, 'has-folder-note', false, plugin);
|
||||
removeCSSClassFromFileExplorerEL(folder?.path, 'only-has-folder-note', true, plugin);
|
||||
removeCSSClassFromFileExplorerEL(folderNote?.path, 'is-folder-note', false, plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -149,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;
|
||||
}
|
||||
|
|
@ -162,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);
|
||||
});
|
||||
}
|
||||
|
|
@ -181,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; }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
import { TFolder, TFile, View } 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);
|
||||
}
|
||||
|
|
@ -37,57 +42,70 @@ export function getFolderPathFromString(path: string): string {
|
|||
}
|
||||
|
||||
export function getParentFolderPath(path: string): string {
|
||||
return this.getFolderPathFromString(this.getFolderPathFromString(path));
|
||||
return getFolderPathFromString(getFolderPathFromString(path));
|
||||
}
|
||||
|
||||
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') {
|
||||
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;
|
||||
}
|
||||
|
||||
export function getFileExplorerActiveFolder(): TFolder | null {
|
||||
export function getFileExplorerActiveFolder(plugin: FolderNotesPlugin): TFolder | null {
|
||||
// Check if the active view is a file explorer.
|
||||
const view = this.app.workspace.getActiveViewOfType(View);
|
||||
const view = plugin.app.workspace.getActiveViewOfType(View);
|
||||
if (view?.getViewType() !== 'file-explorer') return null;
|
||||
// Check if there is a focused or active item in the file explorer.
|
||||
const fe = view as FileExplorerView;
|
||||
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 {
|
||||
const folder = getFileExplorerActiveFolder();
|
||||
export function getFileExplorerActiveFolderNote(plugin: FolderNotesPlugin): TFile | null {
|
||||
const folder = getFileExplorerActiveFolder(plugin);
|
||||
if (!folder) return null;
|
||||
// Is there any folder note for the active folder?
|
||||
const folderNote = getFolderNote(this.plugin, folder.path);
|
||||
const folderNote = getFolderNote(plugin, folder.path);
|
||||
if (!(folderNote instanceof TFile)) return null;
|
||||
return folderNote;
|
||||
}
|
||||
|
||||
|
||||
export function getAttachmentFolderPath(plugin: FolderNotesPlugin): string {
|
||||
const attachmentFolderPath = plugin.app.vault.getConfig('attachmentFolderPath') as string;
|
||||
const cleanAttachmentFolderPath = attachmentFolderPath?.replace('./', '') || '';
|
||||
return cleanAttachmentFolderPath;
|
||||
}
|
||||
|
||||
export function isFileInAttachmentFolder(plugin: FolderNotesPlugin, file: TAbstractFile): boolean {
|
||||
const attachmentFolderPath = getAttachmentFolderPath(plugin);
|
||||
if (!attachmentFolderPath) return false;
|
||||
return file.path.startsWith(attachmentFolderPath + '/')
|
||||
|| file.path.includes('/' + attachmentFolderPath + '/');
|
||||
}
|
||||
|
|
|
|||
269
src/main.ts
269
src/main.ts
|
|
@ -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';
|
||||
|
|
@ -30,71 +30,80 @@ import {
|
|||
refreshAllFolderStyles, setActiveFolder, removeActiveFolder,
|
||||
} from './functions/styleFunctions';
|
||||
import { getExcludedFolder } from './ExcludeFolders/functions/folderFunctions';
|
||||
import { getFileExplorer } from './functions/utils';
|
||||
import { FOLDER_OVERVIEW_VIEW, FolderOverviewView } from './obsidian-folder-overview/src/view';
|
||||
import { registerOverviewCommands } from './obsidian-folder-overview/src/Commands';
|
||||
import { updateOverviewView, updateViewDropdown } from './obsidian-folder-overview/src/main';
|
||||
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> {
|
||||
console.log('loading folder notes plugin');
|
||||
console.debug('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
|
||||
document.body.classList.add('folder-notes-plugin');
|
||||
if (this.settings.hideFolderNote) { document.body.classList.add('hide-folder-note'); }
|
||||
if (this.settings.hideCollapsingIconForEmptyFolders) {
|
||||
document.body.classList.add('fn-hide-empty-collapse-icon');
|
||||
}
|
||||
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');
|
||||
}
|
||||
if (this.settings.underlineFolderInPath) {
|
||||
document.body.classList.add('folder-note-underline-path');
|
||||
}
|
||||
if (this.settings.stopWhitespaceCollapsing) {
|
||||
document.body.classList.add('fn-whitespace-stop-collapsing');
|
||||
}
|
||||
if (this.settings.hideCollapsingIcon) {
|
||||
document.body.classList.add('fn-hide-collapse-icon');
|
||||
}
|
||||
if (this.settings.ignoreAttachmentFolder) {
|
||||
document.body.classList.add('fn-ignore-attachment-folder');
|
||||
}
|
||||
if (!this.settings.highlightFolder) {
|
||||
document.body.classList.add('disable-folder-highlight');
|
||||
}
|
||||
|
||||
if (requireApiVersion('1.7.2')) {
|
||||
document.body.classList.add('version-1-7-2');
|
||||
}
|
||||
this.addSettingCssClasses();
|
||||
|
||||
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';
|
||||
|
|
@ -123,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; }
|
||||
|
|
@ -139,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();
|
||||
}));
|
||||
|
||||
|
|
@ -161,12 +172,64 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
);
|
||||
}
|
||||
|
||||
addSettingCssClasses(): void {
|
||||
activeDocument.body.classList.add('folder-notes-plugin');
|
||||
if (this.settings.hideFolderNote) { activeDocument.body.classList.add('hide-folder-note'); }
|
||||
if (this.settings.hideCollapsingIconForEmptyFolders) {
|
||||
activeDocument.body.classList.add('fn-hide-empty-collapse-icon');
|
||||
}
|
||||
if (this.settings.hideFolderNoteNameInPath) {
|
||||
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.cursiveNameInPath) {
|
||||
activeDocument.body.classList.add('folder-note-cursive-path');
|
||||
}
|
||||
if (this.settings.underlineFolderInPath) {
|
||||
activeDocument.body.classList.add('folder-note-underline-path');
|
||||
}
|
||||
if (this.settings.stopWhitespaceCollapsing) {
|
||||
activeDocument.body.classList.add('fn-whitespace-stop-collapsing');
|
||||
}
|
||||
if (this.settings.hideCollapsingIcon) {
|
||||
activeDocument.body.classList.add('fn-hide-collapse-icon');
|
||||
}
|
||||
if (this.settings.ignoreAttachmentFolder) {
|
||||
activeDocument.body.classList.add('fn-ignore-attachment-folder');
|
||||
}
|
||||
if (!this.settings.highlightFolder) {
|
||||
activeDocument.body.classList.add('disable-folder-highlight');
|
||||
}
|
||||
|
||||
if (requireApiVersion('1.7.2')) {
|
||||
activeDocument.body.classList.add('version-1-7-2');
|
||||
}
|
||||
}
|
||||
|
||||
onLayoutReady(): void {
|
||||
if (!this._loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
this.registerView(FOLDER_OVERVIEW_VIEW, (leaf: WorkspaceLeaf) => {
|
||||
return new FolderOverviewView(leaf, this);
|
||||
});
|
||||
|
|
@ -181,37 +244,46 @@ 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);
|
||||
}, true);
|
||||
|
||||
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'])
|
||||
.bind(fileExplorer);
|
||||
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;
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -220,25 +292,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 originalHandleDragOver = clipboardProto.handleDragOver;
|
||||
const originalHandleDrop = clipboardProto.handleDrop;
|
||||
const { clipboardManager } = editMode;
|
||||
const clipboardProto = Object.getPrototypeOf(clipboardManager) as ClipboardManagerLike;
|
||||
|
||||
const originalHandleDragOver =
|
||||
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.app;
|
||||
const { dragManager } = (this as ClipboardManagerLike).app;
|
||||
const draggable = dragManager?.draggable;
|
||||
|
||||
if (draggable?.file instanceof TFolder) {
|
||||
const folderNote = getFolderNote(plugin, draggable.file.path);
|
||||
const folderNote = getFolderNote(folderNotePlugin, draggable.file.path);
|
||||
if (folderNote) {
|
||||
dragManager.setAction(
|
||||
window.i18next.t('interface.drag-and-drop.insert-link-here'),
|
||||
|
|
@ -251,11 +325,11 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
};
|
||||
|
||||
clipboardProto.handleDrop = function (evt: DragEvent, ...args: unknown[]): void {
|
||||
const { dragManager } = this.app;
|
||||
const { dragManager } = (this as ClipboardManagerLike).app;
|
||||
const draggable = dragManager?.draggable;
|
||||
|
||||
if (draggable?.file instanceof TFolder) {
|
||||
const folderNote = getFolderNote(plugin, draggable.file.path);
|
||||
const folderNote = getFolderNote(folderNotePlugin, draggable.file.path);
|
||||
if (folderNote) {
|
||||
draggable.file = folderNote;
|
||||
draggable.type = 'file';
|
||||
|
|
@ -274,7 +348,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)();
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +379,7 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
evt.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
openFolderNote(this, folderNote, evt);
|
||||
void openFolderNote(this, folderNote, evt);
|
||||
}
|
||||
|
||||
private isMobileClickDisabled(): boolean {
|
||||
|
|
@ -316,9 +390,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(
|
||||
|
|
@ -349,9 +426,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 {
|
||||
|
|
@ -371,7 +448,7 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
new FolderOverviewSettings(
|
||||
this.app,
|
||||
this,
|
||||
parseYaml(source),
|
||||
parseYaml(source) as defaultOverviewSettings,
|
||||
ctx,
|
||||
el,
|
||||
this.settings.defaultOverview,
|
||||
|
|
@ -395,7 +472,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(
|
||||
|
|
@ -405,7 +482,7 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
el,
|
||||
this.settings.defaultOverview,
|
||||
);
|
||||
folderOverview.create(this, el, ctx);
|
||||
void folderOverview.create(this, el, ctx);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -429,7 +506,7 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
}
|
||||
|
||||
if (!leaf) return;
|
||||
workspace.revealLeaf(leaf);
|
||||
void workspace.revealLeaf(leaf);
|
||||
}
|
||||
|
||||
updateOverviewView: typeof updateOverviewView = updateOverviewView;
|
||||
|
|
@ -442,12 +519,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) {
|
||||
|
|
@ -460,11 +537,15 @@ 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,
|
||||
);
|
||||
void addCSSClassToFileExplorerEl(
|
||||
folder.path, 'fn-has-attachment-folder',
|
||||
true, this,
|
||||
);
|
||||
}
|
||||
return folder.children.length <= threshold + 1;
|
||||
}
|
||||
|
|
@ -486,7 +567,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;
|
||||
}
|
||||
|
|
@ -496,11 +577,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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -521,7 +602,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; }
|
||||
|
|
@ -541,10 +622,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();
|
||||
|
|
@ -552,7 +633,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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
})();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit c5986efabab87155c1ab652920deebbc75b883af
|
||||
Subproject commit f047859cc9a7f1c04ad35ecdc440a31a300bc6c2
|
||||
|
|
@ -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';
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
/* 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: number | undefined;
|
||||
|
||||
// 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();
|
||||
|
|
@ -53,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();
|
||||
}),
|
||||
);
|
||||
|
|
@ -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,10 +119,10 @@ 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();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -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 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')
|
||||
|
|
|
|||
|
|
@ -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,11 +106,24 @@ 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();
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Hide folder note name in the path')
|
||||
.setDesc('Only show the folder name in the path and hide the folder note name.')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(settingsTab.plugin.settings.hideFolderNoteNameInPath)
|
||||
.onChange(async (value) => {
|
||||
activeDocument.body.classList.toggle('folder-note-hide-name-path', value);
|
||||
settingsTab.plugin.settings.hideFolderNoteNameInPath = value;
|
||||
await settingsTab.plugin.saveSettings();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ export interface FolderNotesSettings {
|
|||
fvGlobalSettings: {
|
||||
autoUpdateLinks: boolean;
|
||||
}
|
||||
hideFolderNoteNameInPath: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: FolderNotesSettings = {
|
||||
|
|
@ -206,14 +207,15 @@ export const DEFAULT_SETTINGS: FolderNotesSettings = {
|
|||
fvGlobalSettings: {
|
||||
autoUpdateLinks: false,
|
||||
},
|
||||
hideFolderNoteNameInPath: false,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -244,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;
|
||||
}
|
||||
}
|
||||
|
|
@ -296,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');
|
||||
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();
|
||||
plugin.saveSettings();
|
||||
void plugin.saveSettings();
|
||||
}
|
||||
tabEl.addClass('fn-settings-tab-active');
|
||||
if (!settingsTab) { return; }
|
||||
|
|
@ -342,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');
|
||||
}
|
||||
|
||||
|
|
@ -371,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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,12 +1,21 @@
|
|||
import { TFile, TFolder, Vault, AbstractInputSuggest, type TAbstractFile } from 'obsidian';
|
||||
import type FolderNotesPlugin from '../main';
|
||||
import { getTemplatePlugins } from 'src/template';
|
||||
|
||||
interface TemplateSuggestion {
|
||||
path: string;
|
||||
name: string;
|
||||
parent?: {
|
||||
path: string;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export enum FileSuggestMode {
|
||||
TemplateFiles,
|
||||
ScriptFiles,
|
||||
}
|
||||
|
||||
export class TemplateSuggest extends AbstractInputSuggest<TFile> {
|
||||
export class TemplateSuggest extends AbstractInputSuggest<TemplateSuggestion> {
|
||||
constructor(
|
||||
public inputEl: HTMLInputElement,
|
||||
public plugin: FolderNotesPlugin,
|
||||
|
|
@ -24,10 +33,10 @@ export class TemplateSuggest extends AbstractInputSuggest<TFile> {
|
|||
}
|
||||
}
|
||||
|
||||
getSuggestions(input_str: string): TFile[] {
|
||||
getSuggestions(input_str: string): TemplateSuggestion[] {
|
||||
const { templateFolder, templaterPlugin } = getTemplatePlugins(this.app);
|
||||
|
||||
let files: TFile[] = [];
|
||||
let files: TemplateSuggestion[] = [];
|
||||
const lower_input_str = input_str.toLowerCase();
|
||||
|
||||
if ((!templateFolder || templateFolder.trim() === '') && !templaterPlugin) {
|
||||
|
|
@ -47,13 +56,12 @@ 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.',
|
||||
} 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)) {
|
||||
|
|
@ -71,7 +79,7 @@ export class TemplateSuggest extends AbstractInputSuggest<TFile> {
|
|||
}
|
||||
|
||||
|
||||
renderSuggestion(file: TFile, el: HTMLElement): void {
|
||||
renderSuggestion(file: TemplateSuggestion, el: HTMLElement): void {
|
||||
const { templateFolder, templaterPlugin } = getTemplatePlugins(this.app);
|
||||
|
||||
if ((!templateFolder || templateFolder.trim() === '') && !templaterPlugin) {
|
||||
|
|
@ -82,11 +90,11 @@ export class TemplateSuggest extends AbstractInputSuggest<TFile> {
|
|||
}
|
||||
|
||||
|
||||
selectSuggestion(file: TFile): void {
|
||||
selectSuggestion(file: TemplateSuggestion): void {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
14
styles.css
14
styles.css
|
|
@ -23,10 +23,15 @@
|
|||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.hide-folder-note :not(.show-folder-note-in-explorer).only-has-folder-note .nav-folder-children {
|
||||
.hide-folder-note :not(.show-folder-note-in-explorer):not(.fn-has-attachment-folder).only-has-folder-note .nav-folder-children {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
.fn-warning-text {
|
||||
color: #fb464c
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Tree Items
|
||||
========================================================================== */
|
||||
|
|
@ -222,6 +227,10 @@ li:has(.fv-link-list-item),
|
|||
.folder-note-cursive-path .has-folder-note.view-header-breadcrumb {
|
||||
font-style: italic;
|
||||
}
|
||||
.folder-note-hide-name-path .hide-folder-note-title-in-path .is-last-separator,
|
||||
.folder-note-hide-name-path .hide-folder-note-title-in-path .path-is-folder-note {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* Collapse Icon Handling */
|
||||
|
|
@ -232,7 +241,7 @@ li:has(.fv-link-list-item),
|
|||
|
||||
.fn-has-no-files .collapse-icon,
|
||||
.fn-hide-collapse-icon .has-folder-note.only-has-folder-note .tree-item-icon,
|
||||
body.fn-ignore-attachment-folder.fn-hide-collapse-icon .only-has-folder-note .fn-empty-folder.fn-has-attachment-folder .tree-item-icon,
|
||||
body.fn-hide-collapse-icon.fn-ignore-attachment-folder .only-has-folder-note .fn-empty-folder.fn-has-attachment-folder .tree-item-icon,
|
||||
body.fn-hide-collapse-icon .only-has-folder-note .fn-empty-folder:not(.fn-has-attachment-folder) .tree-item-icon,
|
||||
body.fn-hide-empty-collapse-icon :not(.only-has-folder-note) > .fn-empty-folder:not(.fn-has-attachment-folder) .tree-item-icon,
|
||||
body.fn-hide-collapse-icon.only-has-folder-note:not(.is-collapsed):not(.show-folder-note-in-explorer)>.nav-folder-children {
|
||||
|
|
@ -240,6 +249,7 @@ body.fn-hide-collapse-icon.only-has-folder-note:not(.is-collapsed):not(.show-fol
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
Settings Tabs
|
||||
========================================================================== */
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"ignoreDeprecations": "6.0",
|
||||
"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 +18,10 @@
|
|||
"ES6",
|
||||
"ES7",
|
||||
"ES2021"
|
||||
]
|
||||
],
|
||||
"paths": {
|
||||
"@app/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable no-undef */
|
||||
import { readFileSync, writeFileSync } from "fs";
|
||||
|
||||
const targetVersion = process.env.npm_package_version;
|
||||
|
|
|
|||
Loading…
Reference in a new issue