mirror of
https://github.com/lostpaul/obsidian-folder-notes.git
synced 2026-07-22 07:40:24 +00:00
Settings progress
This commit is contained in:
parent
c1b5e229a8
commit
d7b8af7989
7 changed files with 2222 additions and 19 deletions
1889
package-lock.json
generated
1889
package-lock.json
generated
File diff suppressed because it is too large
Load diff
73
src/main.ts
73
src/main.ts
|
|
@ -1,6 +1,7 @@
|
|||
import { Plugin, TFile, TFolder, TAbstractFile } from 'obsidian'
|
||||
import { DEFAULT_SETTINGS, FolderNotesSettings, SettingsTab } from './settings'
|
||||
import FolderNameModal from './modals/folderName';
|
||||
import { applyTemplate } from './template';
|
||||
export default class FolderNotesPlugin extends Plugin {
|
||||
observer: MutationObserver
|
||||
folders: TFolder[] = []
|
||||
|
|
@ -21,7 +22,12 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
if (!this.app.workspace.layoutReady) return;
|
||||
if (!this.settings.autoCreate) return;
|
||||
if (!(folder instanceof TFolder)) return;
|
||||
const excludedFolder = this.settings.excludeFolders.find((excludedFolder) => excludedFolder.path === folder.path);
|
||||
|
||||
const excludedFolder = this.settings.excludeFolders.find(
|
||||
(excludedFolder) => (excludedFolder.path ===
|
||||
folder.path?.slice(0, folder?.path.lastIndexOf("/") >= 0 ? folder.path?.lastIndexOf("/") : folder.path.length)) ||
|
||||
(excludedFolder.path === folder?.path.slice(0, folder.path?.lastIndexOf("/")).slice(0, folder.path?.lastIndexOf("/"))
|
||||
&& excludedFolder.subFolders));
|
||||
if (excludedFolder?.disableAutoCreate) return;
|
||||
|
||||
const path = folder.path + '/' + folder.name + '.md';
|
||||
|
|
@ -82,7 +88,23 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
const folder = event.target.parentElement?.getAttribute('data-path');
|
||||
const excludedFolder = this.settings.excludeFolders.find(
|
||||
(excludedFolder) => (excludedFolder.path === folder?.slice(0, folder?.lastIndexOf("/") >= 0 ? folder?.lastIndexOf("/") : folder.length)) ||
|
||||
(excludedFolder.path === folder?.slice(0, folder?.lastIndexOf("/")).slice(0, folder?.lastIndexOf("/"))
|
||||
&& excludedFolder.subFolders));
|
||||
if (excludedFolder?.enableCollappsing && excludedFolder?.disableFolderNote) {
|
||||
event.target.onclick = null;
|
||||
event.target.click();
|
||||
return;
|
||||
} else if (excludedFolder?.enableCollappsing) {
|
||||
event.target.onclick = null;
|
||||
event.target.click();
|
||||
event.target.onclick = (event: MouseEvent) => this.handFolderClickWithCollapse(event);
|
||||
event.target.click();
|
||||
return;
|
||||
}
|
||||
const path = folder + '/' + event.target.innerText + '.md';
|
||||
|
||||
if (this.app.vault.getAbstractFileByPath(path)) {
|
||||
|
|
@ -115,10 +137,59 @@ export default class FolderNotesPlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
handFolderClickWithCollapse(event: MouseEvent) {
|
||||
console.log(event)
|
||||
if (!(event.target instanceof HTMLElement)) return;
|
||||
if (!document.body.classList.contains('folder-notes-plugin')) {
|
||||
event.target.onclick = null;
|
||||
event.target.click();
|
||||
return;
|
||||
}
|
||||
console.log('handFolderClickWithCollapse')
|
||||
|
||||
const folder = event.target.parentElement?.getAttribute('data-path');
|
||||
const path = folder + '/' + event.target.innerText + '.md';
|
||||
console.log(path)
|
||||
|
||||
|
||||
if (this.app.vault.getAbstractFileByPath(path)) {
|
||||
console.log('openFolderNote')
|
||||
this.openFolderNote(path);
|
||||
if (!this.settings.hideFolderNote) return;
|
||||
event.target.parentElement?.parentElement?.getElementsByClassName('nav-folder-children').item(0)?.querySelectorAll('div.nav-file')
|
||||
.forEach((element: HTMLElement) => {
|
||||
if (element.innerText === (event.target as HTMLElement)?.innerText && !element.classList.contains('is-folder-note')) {
|
||||
element.classList.add('is-folder-note');
|
||||
}
|
||||
});
|
||||
|
||||
} else if (event.altKey || event.ctrlKey) {
|
||||
console.log(event.altKey, event.ctrlKey);
|
||||
if ((this.settings.altKey && event.altKey) || (this.settings.ctrlKey && event.ctrlKey)) {
|
||||
this.createFolderNote(path);
|
||||
if (!this.settings.hideFolderNote) return;
|
||||
event.target.parentElement?.parentElement?.getElementsByClassName('nav-folder-children').item(0)?.querySelectorAll('div.nav-file')
|
||||
.forEach((element: HTMLElement) => {
|
||||
if (element.innerText === (event.target as HTMLElement)?.innerText && !element.classList.contains('is-folder-note')) {
|
||||
element.classList.add('is-folder-note');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
event.target.onclick = null;
|
||||
event.target.click();
|
||||
}
|
||||
} else {
|
||||
console.log('click')
|
||||
event.target.onclick = null;
|
||||
event.target.click();
|
||||
}
|
||||
}
|
||||
|
||||
async createFolderNote(path: string, useModal?: boolean) {
|
||||
const leaf = this.app.workspace.getLeaf(false);
|
||||
const file = await this.app.vault.create(path, '');
|
||||
await leaf.openFile(file);
|
||||
applyTemplate(this, this.settings.templatePath);
|
||||
if (!this.settings.autoCreate) return;
|
||||
if (!useModal) return;
|
||||
const folder = this.app.vault.getAbstractFileByPath(path.substring(0, path.lastIndexOf('/' || '\\')));
|
||||
|
|
|
|||
|
|
@ -12,11 +12,15 @@ export default class ExcludedFolderSettings extends Modal {
|
|||
this.excludedFolder = excludedFolder;
|
||||
}
|
||||
onOpen() {
|
||||
this.display();
|
||||
}
|
||||
display() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
contentEl.createEl('h2', { text: 'Exluded folder settings' });
|
||||
new Setting(contentEl)
|
||||
.setName('Include subfolders')
|
||||
.setDesc('Choose if subfolders of the folder should also be excluded')
|
||||
.setDesc('Choose if the subfolders of the folder should also be excluded')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.excludedFolder.subFolders)
|
||||
|
|
@ -27,7 +31,56 @@ export default class ExcludedFolderSettings extends Modal {
|
|||
);
|
||||
|
||||
new Setting(contentEl)
|
||||
|
||||
.setName('Disable folder name sync')
|
||||
.setDesc('Choose if the folder note should be renamed when the folder name is changed')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.excludedFolder.disableSync)
|
||||
.onChange(async (value) => {
|
||||
this.excludedFolder.disableSync = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('Disable auto creation of folder notes in this folder')
|
||||
.setDesc('Choose if a folder note should be created when a new folder is created')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.excludedFolder.disableAutoCreate)
|
||||
.onChange(async (value) => {
|
||||
this.excludedFolder.disableAutoCreate = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
new Setting(contentEl)
|
||||
.setName('Disable open folder note')
|
||||
.setDesc('Choose if the folder note should be opened when the folder is opened')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.excludedFolder.disableFolderNote)
|
||||
.onChange(async (value) => {
|
||||
this.excludedFolder.disableFolderNote = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
})
|
||||
);
|
||||
|
||||
if (!this.excludedFolder.disableFolderNote) {
|
||||
new Setting(contentEl)
|
||||
.setName('Collapse folder when opening folder note')
|
||||
.setDesc('Choose if the folder should be collapsed when the folder note is opened')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.excludedFolder.enableCollappsing)
|
||||
.onChange(async (value) => {
|
||||
this.excludedFolder.enableCollappsing = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
onClose() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { App, PluginSettingTab, Setting } from "obsidian";
|
||||
import FolderNotesPlugin from "./main";
|
||||
import { FolderSuggest } from "./suggesters/FolderSuggester";
|
||||
import { FolderSuggest } from "./suggesters/folderSuggester";
|
||||
import ExcludedFolderSettings from "./modals/exludeFolderSettings";
|
||||
import { TemplateSuggest } from "./suggesters/templateSuggester";
|
||||
export interface FolderNotesSettings {
|
||||
syncFolderName: boolean;
|
||||
ctrlKey: boolean;
|
||||
|
|
@ -92,6 +93,23 @@ export class SettingsTab extends PluginSettingTab {
|
|||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Template path')
|
||||
.setDesc('The path to the template file')
|
||||
.addSearch((cb) => {
|
||||
new TemplateSuggest(cb.inputEl, this.plugin);
|
||||
cb.setPlaceholder('Template path');
|
||||
cb.setValue(this.plugin.app.vault.getAbstractFileByPath(this.plugin.settings.templatePath)?.name.replace('.md', '') || '');
|
||||
cb.onChange(async (value) => {
|
||||
if (value.trim() === '') {
|
||||
this.plugin.settings.templatePath = '';
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setHeading()
|
||||
.setName('Manage excluded folders')
|
||||
|
|
@ -102,7 +120,7 @@ export class SettingsTab extends PluginSettingTab {
|
|||
cb.setClass('add-exclude-folder');
|
||||
cb.setTooltip('Add excluded folder');
|
||||
cb.onClick(() => {
|
||||
const excludedFolder = new ExcludedFolder('', true, true, true, false, this.plugin.settings.excludeFolders.length);
|
||||
const excludedFolder = new ExcludedFolder('', this.plugin.settings.excludeFolders.length);
|
||||
this.addExcludeFolderListItem(containerEl, excludedFolder);
|
||||
this.addExcludedFolder(excludedFolder);
|
||||
this.display();
|
||||
|
|
@ -120,6 +138,8 @@ export class SettingsTab extends PluginSettingTab {
|
|||
cb.inputEl,
|
||||
this.plugin
|
||||
);
|
||||
// @ts-ignore
|
||||
cb.containerEl.addClass('fn-exclude-folder-path');
|
||||
cb.setPlaceholder('Folder path');
|
||||
cb.setValue(excludedFolder.path);
|
||||
cb.onChange((value) => {
|
||||
|
|
@ -198,14 +218,15 @@ export class ExcludedFolder {
|
|||
disableSync: boolean;
|
||||
disableAutoCreate: boolean;
|
||||
disableFolderNote: boolean;
|
||||
enableCollapsing: boolean;
|
||||
enableCollappsing: boolean;
|
||||
position: number;
|
||||
constructor(path: string, subFolders: boolean, disableSync: boolean, disableAutoCreate: boolean, disableFolderNote: boolean, position: number) {
|
||||
constructor(path: string, position: number) {
|
||||
this.path = path;
|
||||
this.subFolders = subFolders;
|
||||
this.disableSync = disableSync;
|
||||
this.disableAutoCreate = disableAutoCreate;
|
||||
this.disableFolderNote = disableFolderNote;
|
||||
this.subFolders = true;
|
||||
this.disableSync = true;
|
||||
this.disableAutoCreate = true;
|
||||
this.disableFolderNote = false;
|
||||
this.enableCollappsing = false;
|
||||
this.position = position;
|
||||
}
|
||||
}
|
||||
70
src/suggesters/templateSuggester.ts
Normal file
70
src/suggesters/templateSuggester.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes and https://github.com/SilentVoid13/Templater
|
||||
|
||||
import { TAbstractFile, TFile, TFolder, Vault } from "obsidian";
|
||||
import { TextInputSuggest } from "./suggest";
|
||||
import FolderNotesPlugin from "../main";
|
||||
import { getTemplatePlugins } from "src/template";
|
||||
export enum FileSuggestMode {
|
||||
TemplateFiles,
|
||||
ScriptFiles,
|
||||
}
|
||||
|
||||
export class TemplateSuggest extends TextInputSuggest<TFile> {
|
||||
constructor(
|
||||
public inputEl: HTMLInputElement,
|
||||
private plugin: FolderNotesPlugin
|
||||
) {
|
||||
super(inputEl);
|
||||
}
|
||||
|
||||
|
||||
get_error_msg(mode: FileSuggestMode): string {
|
||||
switch (mode) {
|
||||
case FileSuggestMode.TemplateFiles:
|
||||
return `Templates folder doesn't exist`;
|
||||
case FileSuggestMode.ScriptFiles:
|
||||
return `User Scripts folder doesn't exist`;
|
||||
}
|
||||
}
|
||||
|
||||
getSuggestions(input_str: string): TFile[] {
|
||||
const { templateFolder, templaterPlugin } = getTemplatePlugins(this.plugin.app);
|
||||
if ((!templateFolder || templateFolder?.trim() === "") && !templaterPlugin) {
|
||||
this.plugin.settings.templatePath = "";
|
||||
this.plugin.saveSettings();
|
||||
return [];
|
||||
}
|
||||
let folder: TFolder;
|
||||
if (templaterPlugin) {
|
||||
folder = this.plugin.app.vault.getAbstractFileByPath(templaterPlugin.plugin?.settings?.templates_folder as string) as TFolder;
|
||||
|
||||
} else {
|
||||
folder = this.plugin.app.vault.getAbstractFileByPath(templateFolder) as TFolder;
|
||||
}
|
||||
|
||||
const files: TFile[] = [];
|
||||
const lower_input_str = input_str.toLowerCase();
|
||||
|
||||
Vault.recurseChildren(folder, (file: TAbstractFile) => {
|
||||
if (file instanceof TFile &&
|
||||
file.path.toLowerCase().contains(lower_input_str)
|
||||
) {
|
||||
files.push(file);
|
||||
}
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
renderSuggestion(file: TFile, el: HTMLElement): void {
|
||||
el.setText(file.name.replace(".md", ""));
|
||||
}
|
||||
|
||||
selectSuggestion(file: TFile): void {
|
||||
this.inputEl.value = file.name.replace(".md", "");
|
||||
this.inputEl.trigger("input");
|
||||
this.plugin.settings.templatePath = file.path;
|
||||
this.plugin.saveSettings();
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
92
src/template.ts
Normal file
92
src/template.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// Thanks to @mgmeyers for the creating the obsidian kanban plugin https://github.com/mgmeyers/obsidian-kanban
|
||||
// from where I got the template code for this plugin
|
||||
// https://github.com/mgmeyers/obsidian-kanban/blob/48e6c278ce9140b7e034b181432321f697d6e45e/src/components/helpers.ts
|
||||
|
||||
import { TFile, MarkdownView, App } from 'obsidian';
|
||||
import FolderNotesPlugin from './main';
|
||||
export async function applyTemplate(
|
||||
plugin: FolderNotesPlugin,
|
||||
templatePath?: string
|
||||
) {
|
||||
const templateFile = templatePath
|
||||
? plugin.app.vault.getAbstractFileByPath(templatePath)
|
||||
: null;
|
||||
|
||||
if (templateFile && templateFile instanceof TFile) {
|
||||
const activeView = app.workspace.getActiveViewOfType(MarkdownView);
|
||||
|
||||
try {
|
||||
// Force the view to source mode, if needed
|
||||
if (activeView?.getMode() !== 'source') {
|
||||
await activeView?.setState(
|
||||
{
|
||||
...activeView.getState(),
|
||||
mode: 'source',
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
templatesEnabled,
|
||||
templaterEnabled,
|
||||
templatesPlugin,
|
||||
templaterPlugin,
|
||||
} = getTemplatePlugins(plugin.app);
|
||||
|
||||
const templateContent = await plugin.app.vault.read(templateFile);
|
||||
|
||||
// If both plugins are enabled, attempt to detect templater first
|
||||
if (templatesEnabled && templaterEnabled) {
|
||||
if (/<%/.test(templateContent)) {
|
||||
return await templaterPlugin.append_template_to_active_file(
|
||||
templateFile
|
||||
);
|
||||
}
|
||||
|
||||
return await templatesPlugin.instance.insertTemplate(templateFile);
|
||||
}
|
||||
|
||||
if (templatesEnabled) {
|
||||
return await templatesPlugin.instance.insertTemplate(templateFile);
|
||||
}
|
||||
|
||||
if (templaterEnabled) {
|
||||
return await templaterPlugin.append_template_to_active_file(
|
||||
templateFile
|
||||
);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getTemplatePlugins(app: App) {
|
||||
const templatesPlugin = (app as any).internalPlugins.plugins.templates;
|
||||
const templatesEnabled = templatesPlugin.enabled;
|
||||
const templaterPlugin = (app as any).plugins.plugins['templater-obsidian'];
|
||||
const templaterEnabled = (app as any).plugins.enabledPlugins.has(
|
||||
'templater-obsidian'
|
||||
);
|
||||
const templaterEmptyFileTemplate =
|
||||
templaterPlugin &&
|
||||
(this.app as any).plugins.plugins['templater-obsidian'].settings
|
||||
?.empty_file_template;
|
||||
|
||||
const templateFolder = templatesEnabled
|
||||
? templatesPlugin.instance.options.folder
|
||||
: templaterPlugin
|
||||
? templaterPlugin.settings.template_folder
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
templatesPlugin,
|
||||
templatesEnabled,
|
||||
templaterPlugin: templaterPlugin?.templater,
|
||||
templaterEnabled,
|
||||
templaterEmptyFileTemplate,
|
||||
templateFolder,
|
||||
};
|
||||
}
|
||||
23
styles.css
23
styles.css
|
|
@ -17,17 +17,24 @@
|
|||
}
|
||||
|
||||
.fn-exclude-folder-list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 0;
|
||||
border-top: none;
|
||||
float: left;
|
||||
display: block;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.fn-exclude-folder-list-item input {
|
||||
width: 34.5rem;
|
||||
.fn-exclude-folder-path {
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1250px) {
|
||||
.fn-exclude-folder-list-item input {
|
||||
width: 100%;
|
||||
}
|
||||
.fn-exclude-folder-list-item .setting-item-control {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.fn-exclude-folder-list-item > *.last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
Loading…
Reference in a new issue