feat: add note templating functionality

This commit is contained in:
HaloGamer33 2024-08-16 02:28:29 -06:00
parent fca4592745
commit 4159abc212
8 changed files with 771 additions and 357 deletions

126
package-lock.json generated
View file

@ -19,16 +19,6 @@
"typescript": "4.7.4"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
"integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/@codemirror/state": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
@ -37,9 +27,9 @@
"peer": true
},
"node_modules/@codemirror/view": {
"version": "6.26.1",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.1.tgz",
"integrity": "sha512-wLw0t3R9AwOSQThdZ5Onw8QQtem5asE7+bPlnzc57eubPqiuJKIzwjMZ+C42vQett+iva+J8VgFV4RYWDBh5FA==",
"version": "6.30.0",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.30.0.tgz",
"integrity": "sha512-96Nmn8OeLh6aONQprIeYk8hGVnEuYpWuxKSkdsODOx9hWPxyuyZGvmvxV/JmLsp+CubMO1PsLaN5TNNgrl0UrQ==",
"dev": true,
"peer": true,
"dependencies": {
@ -417,9 +407,9 @@
}
},
"node_modules/@eslint-community/regexpp": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
"integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz",
"integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==",
"dev": true,
"peer": true,
"engines": {
@ -464,6 +454,7 @@
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
"deprecated": "Use @eslint/config-array instead",
"dev": true,
"peer": true,
"dependencies": {
@ -493,6 +484,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"deprecated": "Use @eslint/object-schema instead",
"dev": true,
"peer": true
},
@ -553,9 +545,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "16.18.94",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.94.tgz",
"integrity": "sha512-X8q3DoKq8t/QhA0Rk/9wJUajxtXRDiCK+cVaONKLxpsjPhu+xX6uZuEj4UKGLQ4p0obTdFxa0cP/BMvf9mOYZA==",
"version": "16.18.104",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.104.tgz",
"integrity": "sha512-OF3keVCbfPlkzxnnDBUZJn1RiCJzKeadjiW0xTEb0G1SUJ5gDVb3qnzZr2T4uIFvsbKJbXy1v2DN7e2zaEY7jQ==",
"dev": true
},
"node_modules/@types/tern": {
@ -759,9 +751,9 @@
"peer": true
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"dev": true,
"peer": true,
"bin": {
@ -859,12 +851,12 @@
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@ -952,9 +944,9 @@
}
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
@ -1204,9 +1196,9 @@
}
},
"node_modules/esquery": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
"integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
"integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
"dev": true,
"peer": true,
"dependencies": {
@ -1338,9 +1330,9 @@
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
@ -1405,6 +1397,7 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"peer": true,
"dependencies": {
@ -1528,6 +1521,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
"peer": true,
"dependencies": {
@ -1670,18 +1664,6 @@
"dev": true,
"peer": true
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -1692,12 +1674,12 @@
}
},
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
"integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
"dev": true,
"dependencies": {
"braces": "^3.0.2",
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
@ -1740,9 +1722,9 @@
"peer": true
},
"node_modules/obsidian": {
"version": "1.5.7-1",
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.5.7-1.tgz",
"integrity": "sha512-T5ZRuQ1FnfXqEoakTTHVDYvzUEEoT8zSPnQCW31PVgYwG4D4tZCQfKHN2hTz1ifnCe8upvwa6mBTAP2WUA5Vng==",
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.6.6.tgz",
"integrity": "sha512-GZHzeOiwmw/wBjB5JwrsxAZBLqxGQmqtEKSvJJvT0LtTcqeOFnV8jv0ZK5kO7hBb44WxJc+LdS7mZgLXbb+qXQ==",
"dev": true,
"dependencies": {
"@types/codemirror": "5.60.8",
@ -1764,18 +1746,18 @@
}
},
"node_modules/optionator": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
"integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dev": true,
"peer": true,
"dependencies": {
"@aashutoshrathi/word-wrap": "^1.2.3",
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
"levn": "^0.4.1",
"prelude-ls": "^1.2.1",
"type-check": "^0.4.0"
"type-check": "^0.4.0",
"word-wrap": "^1.2.5"
},
"engines": {
"node": ">= 0.8.0"
@ -1953,6 +1935,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"peer": true,
"dependencies": {
@ -1989,13 +1972,10 @@
}
},
"node_modules/semver": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
@ -2199,6 +2179,16 @@
"node": ">= 8"
}
},
"node_modules/word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@ -2206,12 +2196,6 @@
"dev": true,
"peer": true
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

View file

@ -1,4 +1,4 @@
import { FolderSelectionModal } from './main';
import { FolderSelectionModal } from './folder-selection-modal';
export function AddCommands() {
this.addCommand({

View file

@ -0,0 +1,71 @@
import { App, FuzzySuggestModal, normalizePath, Instruction, TFolder } from 'obsidian';
import { FuzzyNoteCreatorSettings } from './settingsTab';
import { NoteCreationModal } from './note-creation-modal';
// Modals are elements in the UI that require interaction from the user
export class FolderSelectionModal extends FuzzySuggestModal<string> {
settings: FuzzyNoteCreatorSettings;
leafMode: string;
constructor(app: App, leafMode: string, settings: FuzzyNoteCreatorSettings) {
super(app)
this.settings = settings;
this.leafMode = leafMode;
this.setPlaceholder('Path to the folder...');
if (settings.showInstructions) {
const instructions: Instruction[] = [
{command: '↵', purpose: 'to select folder'},
{command: 'esc', purpose: 'to dismiss'},
{command: '↑↓', purpose: 'to navigate'},
{command: '⭾ tab', purpose: 'to autocomplete folder'},
];
this.setInstructions(instructions);
}
// Autocomplete selected folder with tab.
this.inputEl.addEventListener('keydown', async (event: KeyboardEvent) => {
if (event.key === 'Tab') {
event.preventDefault();
const suggestions = this.resultContainerEl.children;
for (let i = 0; i < suggestions.length; i++) {
if (!suggestions[i].classList.contains('is-selected')) {
continue;
}
this.inputEl.value = suggestions[i].getText();
break;
}
}
})
}
// ── End of constructor ──────────────────────────────────────────────
// Get the folders in the vault
getItems(): string[] {
let dirs: string[] = [];
const abstractFiles = this.app.vault.getAllLoadedFiles();
for (let i = 0; i < abstractFiles.length; i++) {
if (abstractFiles[i] instanceof TFolder) {
dirs.push(abstractFiles[i].path);
}
}
return dirs;
}
getItemText(path: string): string {
return path;
}
onChooseItem(path: string) {
// normalizePath is an obsidian function (I still do some work to normalize the path down the line)
const normalizedPath = normalizePath(path);
new NoteCreationModal(this.app, normalizedPath, this.leafMode, this.settings).open();
}
}

View file

@ -1,7 +1,6 @@
import { Plugin, Notice, App, FuzzySuggestModal, SuggestModal, normalizePath, moment, Instruction, Platform, TFolder } from 'obsidian';
import { Plugin } from 'obsidian';
import { FuzzyNoteCreatorSettingTab, FuzzyNoteCreatorSettings, DEFAULT_SETTINGS } from './settingsTab';
import { AddCommands } from './commands';
import { OpenNote } from './open-note';
export default class FuzzyNoteCreatorPlugin extends Plugin {
settings: FuzzyNoteCreatorSettings;
@ -13,263 +12,10 @@ export default class FuzzyNoteCreatorPlugin extends Plugin {
async onload() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
// Arguments: (app, plugin)
this.addSettingTab(new FuzzyNoteCreatorSettingTab(this.app, this));
// Bind `this` context and then call the function
AddCommands.bind(this)();
}
}
export class FolderSelectionModal extends FuzzySuggestModal<string> {
settings: FuzzyNoteCreatorSettings;
leafMode: string;
constructor(app: App, leafMode: string, settings: FuzzyNoteCreatorSettings) {
super(app)
this.settings = settings;
this.leafMode = leafMode;
this.setPlaceholder('Path to the folder...');
if (settings.showInstructions) {
const instructions: Instruction[] = [
{command: '↵', purpose: 'to select folder'},
{command: 'esc', purpose: 'to dismiss'},
{command: '↑↓', purpose: 'to navigate'},
{command: '⭾ tab', purpose: 'to autocomplete folder'},
];
this.setInstructions(instructions);
}
this.inputEl.addEventListener('keydown', async (event: KeyboardEvent) => {
if (event.key === 'Tab') {
event.preventDefault();
const suggestions = this.resultContainerEl.children;
for (let i = 0; i < suggestions.length; i++) {
if (!suggestions[i].classList.contains('is-selected')) {
continue;
}
this.inputEl.value = suggestions[i].getText();
break;
}
}
})
}
getItems(): string[] {
let dirs: string[] = [];
const abstractFiles = this.app.vault.getAllLoadedFiles();
for (let i = 0; i < abstractFiles.length; i++) {
if (abstractFiles[i] instanceof TFolder) {
dirs.push(abstractFiles[i].path);
}
}
return dirs;
}
getItemText(path: string): string {
return path;
}
onChooseItem(path: string) {
const normalizedPath = normalizePath(path);
new NoteTitleModal(this.app, normalizedPath, this.leafMode, this.settings).open();
}
}
export class NoteTitleModal extends SuggestModal<string> {
settings: FuzzyNoteCreatorSettings;
path: string;
leafMode: string;
constructor(app: App, path: string, leafMode: string, settings: FuzzyNoteCreatorSettings) {
super(app)
this.settings = settings
this.path = path;
this.leafMode = leafMode;
if (settings.showInstructions) {
const instructions: Instruction[] = [
{command: '↵', purpose: 'to create note'},
{command: 'esc', purpose: 'to dismiss'},
// {command: '/', purpose: 'to create parent folder of the note'},
];
this.setInstructions(instructions);
}
this.emptyStateText = 'No templates found.';
this.setPlaceholder('Title of the note...');
if (!settings.useNoteTitleTemplates) {
let previousModalJustClosed = new BooleanWrapper(true);
this.inputEl.addEventListener('keyup', event => {
this.handleEnterKey(event, previousModalJustClosed)
});
return;
}
if (settings.useNoteTitleTemplates) {
let previousModalJustClosed = new BooleanWrapper(true);
this.inputEl.addEventListener('keyup', event => {
this.noTemplatesNoteCreation(event, previousModalJustClosed)
});
return;
}
}
async noTemplatesNoteCreation(event: KeyboardEvent | MouseEvent, previousModalJustClosed: BooleanWrapper) {
const isKeyboardEvent = (event: KeyboardEvent | MouseEvent): event is KeyboardEvent => "key" in event;
if(isKeyboardEvent(event)) {
const {key} = event;
if (key !== 'Enter') {
return
}
}
if (previousModalJustClosed.value) {
previousModalJustClosed.value = false;
return;
}
if (this.getSuggestions(this.inputEl.value).length === 0) {
this.handleEnterKey(event, previousModalJustClosed)
}
}
getSuggestions(query: string): string[] {
if (!this.settings.useNoteTitleTemplates) {
this.resultContainerEl.hide();
return [];
}
if (this.settings.noteTitleTemplates === '' || this.settings.noteTitleTemplates === undefined) {
return [];
}
const templates = this.settings.noteTitleTemplates.split('\n');
const trimmedTemplates = templates.map(template => template.trim())
return trimmedTemplates.filter(template => {
return template.toLowerCase().includes(query.toLowerCase());
});
}
renderSuggestion(template: string, el: HTMLElement) {
el.createEl('div', {text: template});
}
onChooseSuggestion(noteName: string, evt: MouseEvent | KeyboardEvent) {
const formatedNoteName = moment().format(noteName)
this.handleEnterKey(evt, new BooleanWrapper(false), formatedNoteName)
}
async handleEnterKey(event: KeyboardEvent | MouseEvent, previousModalJustClosed: BooleanWrapper, noteName?: string) {
const isKeyboardEvent = (event: KeyboardEvent | MouseEvent): event is KeyboardEvent => "key" in event;
if(isKeyboardEvent(event)) {
const {key} = event;
if (key !== 'Enter') {
return
}
}
if (previousModalJustClosed.value) {
previousModalJustClosed.value = false;
return;
}
if (noteName === undefined) {
noteName = this.inputEl.value;
}
noteName = noteName.trim();
if (noteName.length == 0 && !this.settings.allowUntitledNotes) {
new Notice('Add a title to the note' , 2000);
return;
}
if (noteName.length == 0 && this.settings.allowUntitledNotes) {
noteName = (this.settings.untitledNoteName.length == 0) ? DEFAULT_SETTINGS.untitledNoteName! : this.settings.untitledNoteName!;
}
const windowsCompatibility: boolean = (
Platform.isWin || this.settings.windowsNoteTitleCompatibility
);
if (windowsCompatibility && noteName.match(/(<|>|:|"|\||\?|\*)/) !== null) {
new Notice(`The note title must not include any of this characters: < > : " ? | *`, 2000);
return;
}
if (noteName.match(/^[\\\/\s]+$/)) {
new Notice(`The note title must not be only '\\' or '/'`);
return;
}
if (noteName.match(/(\/|\\)/) !== null) {
let noteNameWithDirs = normalizePath(noteName).split('/').map(link => link.trim()).filter(link => link.length !== 0);
let parentDirs = `${this.path}/`;
for (let i = 0; i < noteNameWithDirs.length - 1; i++) {
noteNameWithDirs[i] += '/';
parentDirs += noteNameWithDirs[i];
}
parentDirs = parentDirs.slice(0, -1);
if (this.app.vault.getFolderByPath(parentDirs) === null) {
this.app.vault.createFolder(parentDirs);
}
noteName = noteNameWithDirs.join('');
}
let noteExtension = this.settings.defaultNoteExtension;
if (noteExtension.length == 0) {
noteExtension = DEFAULT_SETTINGS.defaultNoteExtension!;
}
if (windowsCompatibility && noteExtension.match(/(<|>|:|"|\\|\||\?|\/|\*|\/)/) !== null) {
new Notice(`The note extension must not include any of this characters: < > : " \ ? | * /`, 2000);
return;
}
if (noteExtension.match(/(\/)/) !== null) {
new Notice(`The note extension must not include the character: /`, 2000);
return;
}
const notePath = (this.path.length == 1) ? `${noteName}${noteExtension}` : `${this.path}/${noteName}${noteExtension}`;
const normalizedNotePath= normalizePath(notePath);
const fileAlreadyExists = (this.app.vault.getFileByPath(normalizedNotePath) !== null) ? true : false;
if (fileAlreadyExists) {
new Notice(`That note already exists`, 2000);
return;
}
const newNote = await this.app.vault.create(`${normalizedNotePath}`, '');
if (newNote == null) {
new Notice('Error opening the file, report issue to GitHub', 2000);
return;
}
if (newNote.extension !== 'md') {
new Notice(`Created note ${noteName}${noteExtension}, opening it on the system's default application if there is one`, 4000);
}
OpenNote.bind(this)(newNote);
}
}
class BooleanWrapper {
value: boolean;
constructor(value: boolean) {
this.value = value;
}
}

View file

@ -0,0 +1,401 @@
import { Notice, App, SuggestModal, normalizePath, moment, Instruction, Platform } from 'obsidian';
import { FuzzyNoteCreatorSettings, DEFAULT_SETTINGS } from './settingsTab';
import { OpenNote } from './open-note';
class BooleanWrapper {
value: boolean;
constructor(value: boolean) {
this.value = value;
}
}
type NoteType = "TitleTemplate" | "NoteTemplate";
type Suggestion = {
text: string;
type: NoteType;
query?: string;
}
type NoteTemplate = {
path: string;
}
export class NoteCreationModal extends SuggestModal<Suggestion> {
settings: FuzzyNoteCreatorSettings;
path: string;
leafMode: string;
noteTemplate: NoteTemplate;
constructor(app: App, path: string, leafMode: string, settings: FuzzyNoteCreatorSettings, noteTemplate?: NoteTemplate) {
super(app)
this.settings = settings
this.path = path;
this.leafMode = leafMode;
this.emptyStateText = 'No templates found.';
this.setPlaceholder('Title of the note...');
if (noteTemplate !== undefined) {
this.noteTemplate = noteTemplate;
}
if (settings.showInstructions) {
const instructions: Instruction[] = [
{command: '↵', purpose: 'to create note'},
{command: 'esc', purpose: 'to dismiss'},
// {command: '/', purpose: 'to create parent folder of the note'},
];
this.setInstructions(instructions);
}
let previousModalJustClosed = new BooleanWrapper(true);
if (noteTemplate !== undefined) {
this.inputEl.addEventListener('keyup', event => {
this.enterKeyHandler(event, previousModalJustClosed)
});
return;
}
if (!settings.useNoteTitleTemplates) {
this.inputEl.addEventListener('keyup', event => {
this.enterKeyHandler(event, previousModalJustClosed)
});
return;
}
if (settings.useNoteTitleTemplates) {
this.inputEl.addEventListener('keyup', event => {
this.usingTemplatesHandler(event, previousModalJustClosed)
});
return;
}
}
async usingTemplatesHandler(event: KeyboardEvent | MouseEvent, previousModalJustClosed: BooleanWrapper) {
const isKeyboardEvent = (event: KeyboardEvent | MouseEvent): event is KeyboardEvent => "key" in event;
if(isKeyboardEvent(event)) {
const {key} = event;
if (key !== 'Enter') {
return
}
}
if (previousModalJustClosed.value) {
previousModalJustClosed.value = false;
return;
}
/* This only triggers if the user pressed 'enter' when no suggestion was displayed,
* aka: doesn't want to use any template and wants to create a blank note. Otherwise
* the program goes to the function 'OnChooseSuggestion' */
if (this.getSuggestions(this.inputEl.value).length === 0) {
this.enterKeyHandler(event, previousModalJustClosed)
}
}
getSuggestions(query: string): Suggestion[] {
let suggestions: Suggestion[] = [];
if ((!this.settings.useNoteTitleTemplates && !this.settings.useNoteTemplates) || this.noteTemplate !== undefined) {
this.resultContainerEl.hide();
return [];
}
if (this.settings.useNoteTitleTemplates) {
const noteTitleTemplates = this.settings.noteTitleTemplates.split('\n');
const trimmedTemplates = noteTitleTemplates.map(template => template.trim())
const filteredTemplates = trimmedTemplates.filter(template => {
return template.toLowerCase().includes(query.toLowerCase());
});
const areThereTemplates = filteredTemplates[0] !== "";
if (areThereTemplates) {
for (let i = 0; i < filteredTemplates.length; i++) {
const currentTemplate = filteredTemplates[i];
const suggestion: Suggestion = {
text: currentTemplate,
type: "TitleTemplate",
query: query.trim(),
}
suggestions.push(suggestion);
}
}
}
if (this.settings.useNoteTemplates) {
const allNotes = this.app.vault.getFiles();
const templatesFolder = this.settings.noteTemplatesFolder;
let noteTemplatesFilePaths: string[] = [];
for (let i = 0; i < allNotes.length; i++) {
const currentNote = allNotes[i];
const notePath = currentNote.path;
if (notePath.startsWith(templatesFolder) === false) {
continue;
}
if (query.trim() === "") {
noteTemplatesFilePaths.push(notePath);
continue;
}
type queryWrapper = {
query: string;
found: boolean;
}
const queryWrappers: queryWrapper[] = query.trim().split(" ").map((query) => {return {query: query, found: false}});
const pathElements = notePath.split(" ");
// console.log(`\n\n\n-------------------\nNEW CYCLE`);
// console.log(`Working with the note:\n\n${notePath}\n`);
for (let p = 0; p < pathElements.length; p++) {
let headIndex: number = 0;
// console.log(`NEW CYCLE`);
for (let q = 0; q < queryWrappers.length; q++) {
// console.log(`current path element:`, pathElements[p]);
// console.log(`current query element:`, queryWrappers[q].query);
if (queryWrappers[q].found === true) {
// console.log("Already found, continuing");
// console.log(`\n`);
continue;
}
const indexOfQuery = pathElements[p].toLowerCase().indexOf(queryWrappers[q].query.toLowerCase(), headIndex);
if (indexOfQuery === -1) {
// console.log(`'${queryWrappers[q].query}' not found on '${pathElements[p].slice(headIndex)}', breaking.`);
// console.log(`\n`);
break;
}
// console.log(`'${queryWrappers[q].query}' found on '${pathElements[p].slice(headIndex)}'`);
headIndex = indexOfQuery + queryWrappers[q].query.length;
queryWrappers[q].found = true;
// console.log(`current wrapper:`, queryWrappers[q]);
// console.log(`\n`);
}
let allFound = true;
for (let t = 0; t < queryWrappers.length; t++) {
if (queryWrappers[t].found === false) {allFound = false}
}
if (allFound === true) {
noteTemplatesFilePaths.push(notePath);
// console.log(`All components have been found, breaking.`);
break;
}
}
}
for (let i = 0; i < noteTemplatesFilePaths.length; i++) {
const currentTemplate = noteTemplatesFilePaths[i];
const suggestion: Suggestion = {
text: currentTemplate,
type: "NoteTemplate",
query: query.trim(),
}
suggestions.push(suggestion);
}
}
return suggestions;
}
renderSuggestion(suggestion: Suggestion, el: HTMLElement) {
if (suggestion.query === "") {
el.createEl("div", {text: suggestion.text});
}
function escapeRegExp(str: string): string {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
if (suggestion.query !== undefined && suggestion.query !== "") {
const queryElements = suggestion.query.split(" ").filter((suggestion) => {return suggestion !== "";});
let startIndex = 0;
const parentDiv = el.createEl("div");
for (let i = 0; i < queryElements.length; i++) {
const regex = new RegExp(escapeRegExp(String.raw`${queryElements[i]}`), "gi");
let match;
match = regex.exec(suggestion.text.slice(startIndex))
if (match !== null) {
parentDiv.createEl("span", {text: suggestion.text.slice(startIndex, match.index + startIndex)});
parentDiv.createEl("span", {text: suggestion.text.slice(match.index + startIndex, queryElements[i].length + startIndex + match.index), cls: "suggestion-highlight"});
startIndex += queryElements[i].length + match.index;
}
}
if (startIndex !== suggestion.text.length) {
parentDiv.createEl("span", {text: suggestion.text.slice(startIndex)});
}
}
}
onChooseSuggestion(suggestion: Suggestion, evt: MouseEvent | KeyboardEvent) {
if (suggestion.type === "TitleTemplate") {
const formatedNoteTitle = moment().format(suggestion.text)
this.enterKeyHandler(evt, new BooleanWrapper(false), formatedNoteTitle)
}
if (suggestion.type === "NoteTemplate") {
const template: NoteTemplate = {path: suggestion.text};
new NoteCreationModal(this.app, this.path, this.leafMode, this.settings, template).open();
}
}
async enterKeyHandler(event: KeyboardEvent | MouseEvent, previousModalJustClosed: BooleanWrapper, noteName?: string) {
const isKeyboardEvent = (event: KeyboardEvent | MouseEvent): event is KeyboardEvent => "key" in event;
if(isKeyboardEvent(event)) {
const {key} = event;
if (key !== 'Enter') {
return
}
}
if (previousModalJustClosed.value) {
previousModalJustClosed.value = false;
return;
}
if (noteName === undefined) {
noteName = this.inputEl.value;
}
noteName = noteName.trim();
if (noteName.length == 0 && !this.settings.allowUntitledNotes) {
new Notice('Add a title to the note' , 2000);
return;
}
if (noteName.length == 0 && this.settings.allowUntitledNotes) {
noteName = (this.settings.untitledNoteName.length == 0) ? DEFAULT_SETTINGS.untitledNoteName! : this.settings.untitledNoteName!;
}
const windowsCompatibility: boolean = (
Platform.isWin || this.settings.windowsNoteTitleCompatibility
);
if (windowsCompatibility && noteName.match(/(<|>|:|"|\||\?|\*)/) !== null) {
new Notice(`The note title must not include any of this characters: < > : " ? | *`, 2000);
return;
}
if (noteName.match(/^[\\\/\s]+$/)) {
new Notice(`The note title must not be only '\\' or '/'`);
return;
}
if (noteName.match(/(\/|\\)/) !== null) {
let noteNameWithDirs = normalizePath(noteName).split('/').map(link => link.trim()).filter(link => link.length !== 0);
let parentDirs = `${this.path}/`;
for (let i = 0; i < noteNameWithDirs.length - 1; i++) {
noteNameWithDirs[i] += '/';
parentDirs += noteNameWithDirs[i];
}
parentDirs = parentDirs.slice(0, -1);
if (this.app.vault.getFolderByPath(parentDirs) === null) {
this.app.vault.createFolder(parentDirs);
}
noteName = noteNameWithDirs.join('');
}
let noteExtension = this.settings.defaultNoteExtension;
if (noteExtension.length == 0) {
noteExtension = DEFAULT_SETTINGS.defaultNoteExtension!;
}
if (windowsCompatibility && noteExtension.match(/(<|>|:|"|\\|\||\?|\/|\*|\/)/) !== null) {
new Notice(`The note extension must not include any of this characters: < > : " \ ? | * /`, 2000);
return;
}
if (noteExtension.match(/(\/)/) !== null) {
new Notice(`The note extension must not include the character: /`, 2000);
return;
}
const notePath = (this.path.length == 1) ? `${noteName}${noteExtension}` : `${this.path}/${noteName}${noteExtension}`;
const normalizedNotePath= normalizePath(notePath);
let fileAlreadyExists = false;
const loadedFiles = this.app.vault.getFiles();
for (let i = 0; i < loadedFiles.length; i++) {
if (loadedFiles[i].path.toLowerCase() === normalizedNotePath.toLowerCase()) {
fileAlreadyExists = true;
}
}
if (fileAlreadyExists) {
new Notice(`That note already exists`, 2000);
return;
}
let noteContents = "";
if (this.noteTemplate !== undefined) {
const noteTemplatePath = this.noteTemplate.path;
const templateFile = this.app.vault.getFileByPath(noteTemplatePath);
if (templateFile !== null) {
noteContents = await this.app.vault.read(templateFile);
const timeFormat = this.settings.timeFormat;
const dateFormat = this.settings.dateFormat;
const validTimeFormat = (timeFormat === undefined || timeFormat === "") ? "HH:mm" : timeFormat;
const validDateFormat = (dateFormat === undefined || dateFormat === "") ? "YYYY-MM-DD" : dateFormat;
const formatedTime = moment().format(validTimeFormat);
const formatedDate = moment().format(validDateFormat);
noteContents = noteContents.replace(/{{time}}/g, formatedTime);
noteContents = noteContents.replace(/{{date}}/g, formatedDate);
const overrideDateRegex = new RegExp(String.raw`{{date:(.+)}}`, "g");
const overrideTimeRegex = new RegExp(String.raw`{{time:(.+)}}`, "g");
const matchesDate = noteContents.matchAll(overrideDateRegex);
const matchesTime = noteContents.matchAll(overrideTimeRegex);
if (matchesDate !== null) {
const matchesArray = [...matchesDate];
for (let i = 0; i < matchesArray.length; i++) {
const override = matchesArray[i][0];
const format = matchesArray[i][1];
noteContents = noteContents.replace(override, moment().format(format));
}
}
if (matchesTime !== null) {
const matchesArray = [...matchesTime];
for (let i = 0; i < matchesArray.length; i++) {
const override = matchesArray[i][0];
const format = matchesArray[i][1];
noteContents = noteContents.replace(override, moment().format(format));
}
}
}
}
const newNote = await this.app.vault.create(`${normalizedNotePath}`, noteContents);
if (newNote == null) {
new Notice('Error opening the file, report issue to GitHub', 2000);
return;
}
if (newNote.extension !== 'md') {
new Notice(`Created note ${noteName}${noteExtension}, opening it on the system's default application if there is one`, 4000);
}
OpenNote.bind(this)(newNote);
}
}

View file

@ -1,5 +1,5 @@
import FuzzyNoteCreatorPlugin from './main';
import { App, PluginSettingTab, Setting, Platform, sanitizeHTMLToDom } from 'obsidian';
import { App, PluginSettingTab, Setting, Platform, moment } from 'obsidian';
export interface FuzzyNoteCreatorSettings {
showInstructions: boolean;
@ -10,7 +10,9 @@ export interface FuzzyNoteCreatorSettings {
useNoteTitleTemplates: boolean;
noteTitleTemplates: string;
useNoteTemplates: boolean;
noteTemplates: string;
noteTemplatesFolder: string;
dateFormat: string;
timeFormat: string;
}
export const DEFAULT_SETTINGS: Partial<FuzzyNoteCreatorSettings> = {
@ -25,10 +27,12 @@ export const DEFAULT_SETTINGS: Partial<FuzzyNoteCreatorSettings> = {
export class FuzzyNoteCreatorSettingTab extends PluginSettingTab {
plugin: FuzzyNoteCreatorPlugin
app: App
constructor(app: App, plugin: FuzzyNoteCreatorPlugin) {
super(app, plugin);
this.plugin = plugin;
this.app = app;
}
display(): void {
@ -109,6 +113,181 @@ export class FuzzyNoteCreatorSettingTab extends PluginSettingTab {
})
});
// ╭─────────────────────────────────────────────────────────╮
// │ Note Templates │
// ╰─────────────────────────────────────────────────────────╯
containerEl.createEl('h6', {text: 'Note templates'});
new Setting(containerEl)
.setName('Use note templates')
.setDesc('Whether to use templates that define the contents of the new note.')
.addToggle((slider) => {
slider
.setValue(this.plugin.settings.useNoteTemplates)
.onChange(async (value: boolean) => {
this.plugin.settings.useNoteTemplates = value;
await this.plugin.saveSettings();
});
});
// ── Template Folder Location ────────────────────────────────────────
const folderLocationSetting = new Setting(containerEl)
.setName("Template folder location")
.setDesc("Files in this folder will be available as templates.");
const suggestionDiv = folderLocationSetting.settingEl.querySelector(".setting-item-control")!.createEl("div", { cls: "suggestion" });
const folderInput = suggestionDiv.createEl("input", { attr: { id: "textbox-folder", type: "text" }});
const templateFolderPath = this.plugin.settings.noteTemplatesFolder;
const verticalTabContent = document.getElementsByClassName("vertical-tab-content-container")[0];
const fuzzyListenerAttr = verticalTabContent.getAttr("fuzzy-creator-listener");
folderInput.placeholder = "Template Folder"
if (templateFolderPath === undefined || templateFolderPath === "") {
folderInput.value = "";
} else {
folderInput.value = templateFolderPath;
}
if (fuzzyListenerAttr == null) {
verticalTabContent.addEventListener("click", clickHandler);
verticalTabContent.setAttr("fuzzy-creator-listener", "true");
}
folderInput.addEventListener("input", () => createSuggestions(folderInput, this.plugin, this.app));
folderInput.addEventListener("focus", () => createSuggestions(folderInput, this.plugin, this.app));
// ── Functions & Callbacks ───────────────────────────────────────────
function closeAllDropdowns() {
const dropdownMenu = document.getElementById("textbox-folder-dropdown-menu");
if (dropdownMenu == null) {
return;
}
dropdownMenu.detach();
}
function createSuggestions(folderInput: HTMLInputElement, plugin: FuzzyNoteCreatorPlugin, app: App) {
let dirs: string[] = [];
const obsidianFolders = app.vault.getAllFolders();
for (let i = 0; i < obsidianFolders.length; i++) {
dirs.push(obsidianFolders[i].path);
}
closeAllDropdowns()
const value = folderInput.value;
const newDiv = createEl("div", { cls: "dropdown-menu", attr: { id: `${folderInput.id}-dropdown-menu` }});
if (folderInput.parentNode !== null) {
folderInput.parentNode.appendChild(newDiv)
}
const lengthOfSuggestions = (dirs.length < 5) ? dirs.length : 5;
for (let i = 0; i < dirs.length; i++) {
if (dirs[i].toLowerCase().contains(value.toLowerCase())) {
const listElement = createEl("div", { text: `${dirs[i]}`, cls: "dropdown-item" });
listElement.addEventListener("click", function() {
if (listElement.textContent !== null) {
folderInput.value = listElement.textContent;
plugin.settings.noteTemplatesFolder = folderInput.value;
plugin.saveSettings();
}
closeAllDropdowns();
});
newDiv.appendChild(listElement);
if (newDiv.childNodes.length === lengthOfSuggestions) {break;}
}
}
if (newDiv.childNodes.length === 0) {newDiv.hide();}
}
function clickHandler(event: UIEvent) {
const dropdown = document.getElementById("textbox-folder-dropdown-menu");
const textbox = document.getElementById("textbox-folder");
let clickedOnChild: boolean = false;
if (dropdown === null) {
return;
}
for (let i = 0; i < dropdown.childNodes.length; i++) {
if (dropdown.childNodes[i] === event.target) {
clickedOnChild = true;
}
}
if (!clickedOnChild && event.target !== textbox) {
closeAllDropdowns();
}
}
const userDate = (this.plugin.settings.dateFormat !== undefined && this.plugin.settings.dateFormat !== "") ? moment().format(this.plugin.settings.dateFormat) : moment().format('YYYY-MM-DD');
const descriptionDivDate = createEl("div", {text: `Inside your template you can put '{{date}}' which when the note is created its gonna be replaced with the value that you define on this setting. With your current settings {{date}} will be replaced with `});
descriptionDivDate.createEl("b", {text: userDate, cls: "u-pop", attr: {id: "userDate"}});
descriptionDivDate.createEl("span", {text: "."});
descriptionDivDate.createEl("br");
descriptionDivDate.createEl("br");
descriptionDivDate.createEl("span", {text: "You can also use {{date:YYYY-MM-DD}} to override the format."});
const descriptionDate = new DocumentFragment();
descriptionDate.append(descriptionDivDate)
const dateFormatSetting = new Setting(containerEl)
dateFormatSetting
.setName('Date format')
.setDesc(descriptionDate)
.addMomentFormat((momentComponent) => {
momentComponent
.setValue(this.plugin.settings.dateFormat)
.setDefaultFormat('YYYY-MM-DD')
.onChange(async (value) => {
const trimmedValue = value.trim();
this.plugin.settings.dateFormat = trimmedValue;
await this.plugin.saveSettings();
const userDate = (trimmedValue !== "") ? moment().format(trimmedValue) : moment().format("YYYY-MM-DD");
document.getElementById("userDate")!.setText(userDate);
});
});
const userTime = (this.plugin.settings.timeFormat !== undefined && this.plugin.settings.timeFormat !== "") ? moment().format(this.plugin.settings.timeFormat) : moment().format('HH:mm');
const descriptionDivTime = createEl("div", {text: `Inside your template you can put '{{time}}' which when the note is created its gonna be replaced with the value that you define on this setting. With your current settings {{time}} will be replaced with `});
descriptionDivTime.createEl("b", {text: userTime, cls: "u-pop", attr: {id: "userTime"}});
descriptionDivTime.createEl("span", {text: "."});
descriptionDivTime.createEl("br");
descriptionDivTime.createEl("br");
descriptionDivTime.createEl("span", {text: "You can also use {{time:YYYY-MM-DD}} to override the format."});
const descriptionTime = new DocumentFragment();
descriptionTime.append(descriptionDivTime)
const timeFormatSetting = new Setting(containerEl)
timeFormatSetting
.setName('Time format')
.setDesc(descriptionTime)
.addMomentFormat((momentComponent) => {
momentComponent
.setValue(this.plugin.settings.timeFormat)
.setDefaultFormat('HH:mm')
.onChange(async (value) => {
const trimmedValue = value.trim();
this.plugin.settings.timeFormat = trimmedValue;
await this.plugin.saveSettings();
const userTime = (trimmedValue !== "") ? moment().format(trimmedValue) : moment().format("HH:mm");
document.getElementById("userTime")!.setText(userTime);
});
});
// ╭─────────────────────────────────────────────────────────╮
// │ Note Title Templates │
// ╰─────────────────────────────────────────────────────────╯
containerEl.createEl('h6', {text: 'Note title templates'});
new Setting(containerEl)
@ -123,12 +302,18 @@ export class FuzzyNoteCreatorSettingTab extends PluginSettingTab {
});
});
const exampleDate = moment().format('YYYY-MM-DD');
const templatesFragment = new DocumentFragment();
const templatesDescription = containerEl.createEl('div', {text: 'If you have note title templates turned on, you will see them when you are inputing your note title, dates displayed like so: YYYY-MM-DD will be replaced for the date.'});
const templatesLink = containerEl.createEl('a', {text: 'Reference for formatting', href: 'https://momentjs.com/docs/#/displaying/format/'});
const templatesDescription = containerEl.createEl('div', {text: 'If you have note title templates turned on, you will see them when you are inputing your note title. For example, when you create a note with the template: '});
templatesDescription.createEl('b', {text: 'YYYY-MM-DD', cls: 'u-pop'});
templatesDescription.createEl('span', {text: ' it will be replaced for '});
templatesDescription.createEl('b', {text: `${exampleDate}`, cls: 'u-pop'});
templatesDescription.createEl('span', {text: ', this according to the formatting that you can see '});
templatesDescription.createEl('a', {text: 'here', href: 'https://momentjs.com/docs/#/displaying/format/'});
templatesDescription.createEl('span', {text: '.'});
templatesFragment.append(templatesDescription);
templatesFragment.append(templatesLink);
new Setting(containerEl)
.setName('Note title templates')
@ -142,29 +327,9 @@ export class FuzzyNoteCreatorSettingTab extends PluginSettingTab {
this.plugin.settings.noteTitleTemplates = trimmedValue;
await this.plugin.saveSettings();
})
});
const noteTemplatesFragment = new DocumentFragment();
const noteTemplatesDescription = containerEl.createEl('div', {text: 'Works like the templates native Obsidian plugin.'});
const noteTemplatesLink = containerEl.createEl('a', {text: 'Reference for formatting', href: 'https://momentjs.com/docs/#/displaying/format/'});
noteTemplatesFragment.append(noteTemplatesDescription);
noteTemplatesFragment.append(noteTemplatesLink);
new Setting(containerEl)
.setName('Note title noteTemplates')
.setDesc(noteTemplatesFragment)
.addTextArea((text) => {
text
.setPlaceholder('YYYY-MM-DD')
.setValue(this.plugin.settings.noteTitlenoteTemplates)
.onChange(async (value) => {
const trimmedValue = value.trim();
this.plugin.settings.noteTitlenoteTemplates = trimmedValue;
await this.plugin.saveSettings();
})
});
text.inputEl.setCssStyles({resize: "none"})
});
}
}

0
source/template.ts Normal file
View file

47
styles.css Normal file
View file

@ -0,0 +1,47 @@
.suggestion {
position: relative;
display: inline-block;
overflow-y: visible;
}
.input-search {
/* border: 1px solid #FFFFFF; */
position: relative;
/* display: none; */
}
.dropdown-menu {
position: absolute;
z-index: 1;
/*position the autocomplete items to be the same width as the container:*/
top: 100%;
left: 0;
right: 0;
background-color: var(--background-primary);
border: 1px solid var(--background-modifier-border);
border-radius: var(--radius-m);
box-shadow: var(--shadow-s);
padding: var(--size-2-3);
text-align: start;
}
.dropdown-menu div {
border-radius: var(--radius-s);
padding: var(--size-2-3) var(--size-4-3);
padding-inline-start: 12px;
}
.dropdown-menu div:hover {
background-color: var(--background-modifier-hover);
border-radius: var(--radius-s);
}
.autocomplete-active {
/*when navigating through the items using the arrow keys:*/
/* background-color: DodgerBlue !important; */
/* color: #ffffff; */
}