diff --git a/CHANGELOG.md b/CHANGELOG.md index aa368eb..57755e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.3] - 2024-12-20 + +### Fixed + +- Adds "template on rename" setting back into settings, disabled by default. +- Consolidates template prepend logic to ensure both note creation and rename + events atomically read the content of an existing note before prepending a template. + ## [1.1.2] - 2024-12-19 ### Fixed diff --git a/README.md b/README.md index b77293b..ad6d2ad 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Buy Me A Coffee This is a simple [Obsidian](https://obsidian.md/) plugin to automatically template notes -based on their name when created. Users can template notes that match +based on their name when created or renamed. Users can template notes that match their desired naming conventions with any template that exists in their specified templates folder (including sub-folders). @@ -18,6 +18,8 @@ Examples: - **Quicker templating**: Automatically template notes based on their name at creation time - **Customizable**: Users can create rules to template notes based on their personal naming conventions - **Multiple match options**: Choose from prefix, suffix, or contains to template notes +- **Optionally template on rename**: Choose whether or not to template notes when they are renamed + If a note is renamed to a matching template, the template content will be prepended to the existing note. - **Case sensitivity options**: Choose whether or not to match note names against rules in a case-sensitive manner ## Installation diff --git a/docs/assets/search.js b/docs/assets/search.js index 00af0ae..1b9e928 100644 --- a/docs/assets/search.js +++ b/docs/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE62YTW/jNhCG/8vkSiQeWv7SsVsE6GG3RRP0IhgLVaITofowJNrbheH/XlAWzaHJ2KycmyHP+86QfDgidYC2+dFBnBzgn6LOIeazOYM6rQTE8DWV2btogcGuLSGGopai3aSZ6J6Gvx7fZVUCg6xMu050EAMcmbbCCY/OXpUSvMi2qN9u+T30sZ2OJd4Mtmkraklq+yCdFNW2TKX4I5XvN/Pp4O0peFTCvuavQr43edj4Kh0bmo6uzOtQ8S8/vzVSfEsr8SKkLOq3zpf84+jw9dNz9NyUuR+JG1nOs7zRDv6BXxnaB6XlfVltlcoxZeV9SYP6s0qSRXVHSUr96SVlaSdeRN0VstiLMVUpg44YfFZh1Yn0MeQ+EO095fClgUls0l1plm2wfRqeh2+Y7nJD+pweSJR/ALoe0gcmkSn3R1tI8do8F6W4nqgPlM3mFDgq16ao86EtPTft7ZQqfligTdPel7kUp8zdn7vbaUtxStu1u/E534TU8Hxpaqk0V9O+Cak7XHaOH5VZ7NNyl0qh01/Pq6OliR6VtRXbMs3Er6kUN5gdIvMh8p5sr0UVmE0OkaOy6bn5vf7SipszqqObOtPRo7I2ddmk+fVc55hRGZTWefF786jIe3tNl+7dY4a/q6V7cW+2pt4Fzd8ucAbXDIo6F/9CfIC9aLuiqSEG/jh9XAGDTSHKXJ2DT+kZZE1VnfZw3mS7/ud6CPtLZLJpVfAp+mkCLJkwvnzEabRes0SL+z/6B9rDPOmFCCxBnxAdIVpCDizhPiF3hNwSToElU59w6ginljAClkQ+YeQII0s4A5bMfMKZI5xZwjmwZO4Tzh3h3BIugCULn3DhCBeWcAksWfqES0e4tIQrYMnKJ1w5wpUNgOIBveygCw9e0NPj4+fHA5BNECou0MsQuhChTREqNtDLEbogoU0SKj7QyxK6MKFNEypG0MsTukChTRQqTtDLFLpQoU0VKlbQyxW6YKFNFipe0MsWunChTRcqZtDLF7qAoU0YV8xwL2HcJYzbhHHFDPcSxl3C+EWP6puUv0t52pRNGFfMcC9h3CVseNS3+L1opch/O7X6JIGLa8sBvg+vgfOr5gBLiA/Ho2n68eFI+r76r09HborGZm5s5mE2+oZhPHBiTHAS5OIe+4gdqQnDinIOzcQtIm5RoJt78ieGU2I4DTL0Ha6J4YwYzoIM7fOQseJorDgGWVX6K5lxIcsZtprmHmtMVsZkFW6iPysZHzLbYZNtfX4js0wmOchHn23J7JKJ4WEzYw54xIYMiYeNyb6zkEEtyKgW/8dquJAQK9JQMKyj2OdkMkBOBsjDrDw2SJcsbM30Lvv7Z91IocQ+Y9IRwhrC5bc/Y0W2btjOdW9nZMBkz2DYprG//ZIlICsQZkQ+2hkbglcYXdZ3GjIyUg8GFLRmsC22oixqAXGyPh7/A+0J6OPpFwAA"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE62ZTW/jNhCG/8vkSjge+kvWsVsE6GG3xWbRi2AUqkQnQvVhSLTbheH/XlAWw6HJ2IycWyDP+86QfDgUlSO0zb8dxMkR/inqHGK+WDKo00pADF9Tmb2KFhjs2xJiKGop2m2aie5x+GnyKqsSGGRl2nWigxjgxLQVTvn8zatSgmfZFvXLLb+HPrbTscSbwS5tRS1Jbe+kk6LalakUf6Ty9WY+Hbw7B49K2Nf8VcjXJg8bX6VjQ9PRlfkxVPzLz2+NFN/SSjwLKYv6pfMlfz86fP30HD01Ze5H4kaWt1neagf/wK8M7Z3S8r6stkrlmLLyvqRB/VklyaK6oySl/vySBsXv9XfRP7pjCZu61R6fVV6WduJZ1F0hi8Oo2pRBRww+q7DqvBHHbKwHor2nHB4Z1sU23ZeGqsH2cXgevp+7y37hc3ogUf4B6HpIm5rOTbnbos6HTvbUtE9FKa7nU/HDpG2bdnuOH5m5FOfM3ff97bSlOKft2v34nC9C6gX90tRSaa6mfRFS76jsLX5UZnFIy30qhU5/Pa+OliZ6VNZW7Mo0E7+mUtzgaIjMh8h7sv0oqsBscogclU3PjdqX17PpyLoZP5OmOX9pxc31M20409F3Zr04Em5kvdH8b2Rt6rJJ8+u53mJGZVBa533Im0dF3tvjuvTgvn35u2l6EPdma+p90PztA2dww6Coc/EfxEc4iLYrmhpi4JPZZA0MtoUoc3U9AL3iTVWd+1TeZPv+z80Q9qfIZNOq4HP04xRYMmU8mqyn882GJVrc/9A/0B7mSS9EYAn6hOgI0RJyYAn3Cbkj5JZwBiyZ+YQzRzizhHNgydwnnDvCuSVcAEsWPuHCES4s4RJYsvQJl45waQlXwJKVT7hyhCtLGAFLIsZnk9V6YQkjRxhZwjWwZO3LuHaEaxsAxQN62UEXHrygp8fHz48HIJsgVFyglyF0IUKbIlRsoJcjdEFCmyRUfKCXJXRhQpsmVIyglyd0gUKbKFScoJcpdKFCmypUrKCXK3TBQpssVLxg5BW7cKFNFypm0MsXuoChTRhXzHAvYdwljNuEcXxvR3AXMH7Rovoe5aWTe7qUDRhXyHB/h3MB4zZgXCHDvXRyFzBuA8YVMtxLJ3cBGx71Z8tBtFLkv53PmCSBi3vaEf4azp+1PuOOsIb4eDqZ0yY+nsiBo37r05Gbu7FZGptlmI2+UhkPRGOCGOTivlMTO1IThhXl3EiI25y4zQPd3GsVMZwRw1mQoe/mQgwXxHARZGi/iBkrzo0VD6ut0l8tjcvUmEw/4mGVgsQFP2CjP/QZJzLfHxiS/iBKKiIFBfno12oyv8SD80CTvWtDmORhK25fCcmgVmRUq49YDfc9YhURqyjIyn5FJwMka8bDNp3PBgnOGDbdep/9/VNdMJXYZ0zmP6y6y6+xxops3rCVtC/AZLCko2NYS3dvtmQJyP7jYfvPvbIaO8JGdJbzsE1k/7eA1EfKCzMin3mNDcE/gP4Ng12xE2VRC4iTzen0Px7cOmDRGQAA"; \ No newline at end of file diff --git a/docs/classes/default.html b/docs/classes/default.html index 5519007..8285d37 100644 --- a/docs/classes/default.html +++ b/docs/classes/default.html @@ -1,5 +1,5 @@ default | obsidian-template-by-note-name

TemplateByNoteNamePlugin is the main class that handles the plugin's lifecycle.

-

Hierarchy

  • Plugin
    • default

Constructors

Hierarchy

  • Plugin
    • default

Constructors

  • Parameters

    • app: App
    • manifest: PluginManifest

    Returns default

Properties

app: App
manifest: PluginManifest

Collection of user-provided settings values set in plugin settings tab

-

Methods

  • Adds a child component, loading it if this component is loaded

    +

Methods

  • Adds a child component, loading it if this component is loaded

    Type Parameters

    • T extends Component

    Parameters

    • component: T

    Returns T

  • Register a command globally. Registered commands will be available from the @{link https://help.obsidian.md/Plugins/Command+palette Command palette}. The command id and name will be automatically prefixed with this plugin's id and name.

    @@ -58,26 +59,26 @@ Not available on mobile.

  • Evaluate any {{date}} or {{time}} variables in template content

    Parameters

    • template: string

      The template content to evaluate

    Returns string

    The template content with any date or time variables replaced according to user format settings.

    -
  • Determine if the given basename matches the provided matcher rule.

    +
  • Determine if the given basename matches the provided matcher rule.

    Parameters

    Returns boolean

    Whether the basename matches the matcher rule

    -
  • Find the first Matcher instance in the user-provided settings that matches the given file.

    +
  • Find the first Matcher instance in the user-provided settings that matches the given file.

    Parameters

    • file: TFile

      The Obsidian file object representing an existing note

    Returns undefined | Matcher

    The first Matcher whose rule matches the file, or undefined if no match is found

    -
  • Get the content of the template note at the provided path.

    +
  • Get the content of the template note at the provided path.

    Parameters

    • templatePath: string

      Full path to a template note from the vault root

    Returns Promise<string>

    The content of the template file

    -
  • Load this component and its children

    +
  • Load this component and its children

    Returns void

  • Load the plugin's current user-provided settings on top of the default settings.

    -

    Returns Promise<void>

  • Called when the data.json file is modified on disk externally from Obsidian. +

    Returns Promise<void>

  • Called when the data.json file is modified on disk externally from Obsidian. This usually means that a Sync service or external program has modified the plugin settings.

    Implement this method to reload plugin settings when they have changed externally.

    Returns any

  • Load the plugin's user-provided settings and register event listeners.

    -

    Returns Promise<void>

  • Clear all matchers when the plugin is disabled.

    -

    Returns Promise<void>

  • Perform any initial setup code. The user has explicitly interacted with the plugin +

    Returns Promise<void>

  • Clear all matchers when the plugin is disabled.

    +

    Returns Promise<void>

  • Perform any initial setup code. The user has explicitly interacted with the plugin so its safe to engage with the user. If your plugin registers a custom view, you can open it here.

    Returns void

  • Registers a callback to be called when unloading

    @@ -110,18 +111,21 @@ This should not be needed unless your plugin registers commands dynamically.

    Supported formats are those provided by moment.js: https://momentjs.com/docs/#/displaying/format/

    Parameters

    • content: string

      The content to replace date variables in

    Returns string

    The content with date variables replaced

    -
  • Replace any {{time}} or {{time:format}} variables in the content with the current time. +

  • Replace any {{time}} or {{time:format}} variables in the content with the current time. Supported formats are those provided by moment.js: https://momentjs.com/docs/#/displaying/format/

    Parameters

    • content: string

      The content to replace time variables in

    Returns string

    The content with time variables replaced

    -
  • Write settings data to disk. +

  • Write settings data to disk. Data is stored in data.json in the plugin folder.

    Parameters

    • data: any

    Returns Promise<void>

  • Save the plugin's current settings to disk

    -

    Returns Promise<void>

  • Apply a template to a new note if the note name matches a user-provided rule.

    -

    Parameters

    • file: TFile

      The Obsidian file object representing the newly created note

      -

    Returns Promise<void>

  • Unload this component and its children

    -

    Returns void

  • Write the given content to the provided file.

    +

    Returns Promise<void>

  • Prepend the content of a template note to a note.

    Parameters

    • file: TFile

      The Obsidian file object representing an existing note

      -
    • content: string

      The content to write to the note, overwriting the existing content

      -

    Returns Promise<void>

+
  • templatePath: string

    Full path to a template note from the vault root

    +
  • Returns Promise<void>

    diff --git a/docs/index.html b/docs/index.html index fa79b5e..5b3a611 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,6 +1,6 @@ obsidian-template-by-note-name

    obsidian-template-by-note-name

    Template by Note Name

    Buy Me A Coffee

    This is a simple Obsidian plugin to automatically template notes -based on their name when created. Users can template notes that match +based on their name when created or renamed. Users can template notes that match their desired naming conventions with any template that exists in their specified templates folder (including sub-folders).

    Examples:

    @@ -13,6 +13,8 @@ specified templates folder (including sub-folders).

  • Quicker templating: Automatically template notes based on their name at creation time
  • Customizable: Users can create rules to template notes based on their personal naming conventions
  • Multiple match options: Choose from prefix, suffix, or contains to template notes
  • +
  • Optionally template on rename: Choose whether or not to template notes when they are renamed +If a note is renamed to a matching template, the template content will be prepended to the existing note.
  • Case sensitivity options: Choose whether or not to match note names against rules in a case-sensitive manner
  • You can install the plugin via the Community Plugins tab within Obsidian or by direct link here.

    diff --git a/docs/interfaces/Matcher.html b/docs/interfaces/Matcher.html index 42ad62c..91f46aa 100644 --- a/docs/interfaces/Matcher.html +++ b/docs/interfaces/Matcher.html @@ -1,5 +1,5 @@ Matcher | obsidian-template-by-note-name

    A Matcher represents a single user-provided templating rule as specified in the settings tab.

    -
    interface Matcher {
        matchMethod: string;
        matchString: string;
        templatePath: string;
    }

    Properties

    interface Matcher {
        matchMethod: string;
        matchString: string;
        templatePath: string;
    }

    Properties

    matchMethod: string
    matchString: string
    templatePath: string
    +

    Properties

    matchMethod: string
    matchString: string
    templatePath: string
    diff --git a/docs/interfaces/TemplateByNoteNameSettings.html b/docs/interfaces/TemplateByNoteNameSettings.html index aa9fc55..73d20e4 100644 --- a/docs/interfaces/TemplateByNoteNameSettings.html +++ b/docs/interfaces/TemplateByNoteNameSettings.html @@ -1,12 +1,14 @@ TemplateByNoteNameSettings | obsidian-template-by-note-name

    TemplateByNoteNameSettings represents the user-provided settings for the plugin.

    -
    interface TemplateByNoteNameSettings {
        caseSensitive: boolean;
        dateFormat: string;
        matchers: Matcher[];
        templateFolder: string;
        timeFormat: string;
    }

    Properties

    interface TemplateByNoteNameSettings {
        caseSensitive: boolean;
        dateFormat: string;
        matchers: Matcher[];
        templateFolder: string;
        templateOnRename: boolean;
        timeFormat: string;
    }

    Properties

    caseSensitive: boolean

    Whether to match note names against user-provided rule match strings in a case-sensitive manner

    -
    dateFormat: string

    Date format used in templates

    -
    matchers: Matcher[]

    Collection of user-provided matching rules to determine which template to apply to a new or renamed note

    -
    templateFolder: string

    Path to folder from vault root where user stores their templates

    -
    timeFormat: string

    Whether to apply a template to a note when it is renamed to match a rule

    -
    +
    dateFormat: string

    Date format used in templates

    +
    matchers: Matcher[]

    Collection of user-provided matching rules to determine which template to apply to a new or renamed note

    +
    templateFolder: string

    Path to folder from vault root where user stores their templates

    +
    templateOnRename: boolean

    Whether to apply a template to a note when it is renamed to match a rule

    +
    timeFormat: string

    Whether to apply a template to a note when it is renamed to match a rule

    +
    diff --git a/manifest.json b/manifest.json index ec350f9..15afaa0 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "template-by-note-name", "name": "Template by Note Name", - "version": "1.1.2", + "version": "1.1.3", "minAppVersion": "0.15.0", "description": "Automatically template notes based on their title.", "author": "Jacob Learned", diff --git a/package.json b/package.json index 9fbd2c0..2a8e58d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-template-by-note-name", - "version": "1.1.2", + "version": "1.1.3", "description": "Obsidian plugin for automatically templating notes based on their name", "main": "main.js", "type": "module", diff --git a/src/main.ts b/src/main.ts index 0adbaaf..78b4595 100644 --- a/src/main.ts +++ b/src/main.ts @@ -32,6 +32,9 @@ export interface TemplateByNoteNameSettings { /** Whether to apply a template to a note when it is renamed to match a rule */ timeFormat: string; + /** Whether to apply a template to a note when it is renamed to match a rule */ + templateOnRename: boolean; + /** Whether to match note names against user-provided rule match strings in a case-sensitive manner */ caseSensitive: boolean; @@ -43,6 +46,7 @@ const DEFAULT_SETTINGS: TemplateByNoteNameSettings = { templateFolder: "Templates", dateFormat: "YYYY-MM-DD", timeFormat: "HH:mm", + templateOnRename: false, caseSensitive: true, matchers: [], }; @@ -55,15 +59,6 @@ export default class TemplateByNoteNamePlugin extends Plugin { /** Collection of user-provided settings values set in plugin settings tab */ settings: TemplateByNoteNameSettings; - /** - * Write the given content to the provided file. - * @param file The Obsidian file object representing an existing note - * @param content The content to write to the note, overwriting the existing content - */ - async writeToFile(file: TFile, content: string) { - await this.app.vault.modify(file, content); - } - /** * Find the first Matcher instance in the user-provided settings that matches the given file. * @param file The Obsidian file object representing an existing note @@ -180,15 +175,56 @@ export default class TemplateByNoteNamePlugin extends Plugin { } /** - * Apply a template to a new note if the note name matches a user-provided rule. + * Prepend the content of a template note to a note. + * @param file The Obsidian file object representing an existing note + * @param templatePath Full path to a template note from the vault root + */ + async templateNote(file: TFile, templatePath: string) { + const templateContent = await this.getTemplateContent(templatePath); + const fileContent = await this.app.vault.read(file); + await this.app.vault.modify( + file, + `${templateContent}\n\n${fileContent}`, + ); + } + + /** + * Apply a template to a newly created note if the note name matches a user-provided rule. * @param file The Obsidian file object representing the newly created note */ async templateOnCreate(file: TFile) { const templatePath = this.findMatcherForFile(file)?.templatePath; if (templatePath) { - const templateContent = await this.getTemplateContent(templatePath); - await this.writeToFile(file, templateContent); + await this.templateNote(file, templatePath); + } + } + + /** + * Apply a template to a renamed note if the new name matches a user-provided rule. + * @param file The Obsidian file object representing the renamed note + * @param oldName The full path to the note from the vault root before it was renamed + */ + async templateOnRename(file: TFile, oldName: string) { + const matcher = this.findMatcherForFile(file); + + if (matcher) { + if (!this.settings.templateOnRename) { + return; + } + + /* We only want to prepend the template content if the note was renamed to match a rule + and the oldName does not match the rule the note now matches. + This prevents the template content from being prepended multiple times on subsequent renames. + + oldName is the full path to the note from the vault root, e.g. "Path/To/Note.md" + */ + const oldBaseName = oldName.split("/").pop()?.slice(0, -3) ?? ""; + if (this.fileMatchesRule(oldBaseName, matcher)) { + return; + } + + await this.templateNote(file, matcher.templatePath); } } @@ -206,6 +242,14 @@ export default class TemplateByNoteNamePlugin extends Plugin { } }), ); + + this.registerEvent( + this.app.vault.on("rename", async (file, oldName) => { + if (file instanceof TFile) { + await this.templateOnRename(file, oldName); + } + }), + ); }); // Adds tab for Template by Note Name under @@ -444,6 +488,22 @@ class TemplateByNoteNameSettingTab extends PluginSettingTab { new Setting(containerEl).setName("Advanced").setHeading(); + new Setting(containerEl) + .setName("Template on rename") + .setDesc( + `When an existing note's name is changed to one that matches a rule, the plugin will prepend the rule's template to the note's content. + This is useful to enable if you frequently rename default "Untitled" notes to match a rule. Be cautious when performing any bulk rename operations + to ensure that the plugin does not prepend the template to notes that you do not intend to.`, + ) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.templateOnRename) + .onChange(async (value) => { + this.plugin.settings.templateOnRename = value; + await this.plugin.saveSettings(); + }), + ); + new Setting(containerEl) .setName("Case-Sensitive matching") .setDesc( diff --git a/versions.json b/versions.json index 2f0c1a3..1b27793 100644 --- a/versions.json +++ b/versions.json @@ -2,5 +2,6 @@ "1.0.0": "0.15.0", "1.1.0": "0.15.0", "1.1.1": "0.15.0", - "1.1.2": "0.15.0" + "1.1.2": "0.15.0", + "1.1.3": "0.15.0" }