diff --git a/README.md b/README.md index bb0348e..c773152 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,8 @@ This is a sample plugin for Obsidian (https://obsidian.md). -This project uses Typescript to provide type checking and documentation. -The repo depends on the latest plugin API (obsidian.d.ts) in Typescript Definition format, which contains TSDoc comments describing what it does. - -**Note:** The Obsidian API is still in early alpha and is subject to change at any time! +This project uses TypeScript to provide type checking and documentation. +The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does. This sample plugin demonstrates some of the basic functionality the plugin API can do. - Adds a ribbon icon, which shows a Notice when clicked. @@ -41,7 +39,7 @@ Quick starting guide for new plugin devs: ## Adding your plugin to the community plugin list -- Check https://github.com/obsidianmd/obsidian-releases/blob/master/plugin-review.md +- Check the [plugin guidelines](https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines). - Publish an initial version. - Make sure you have a `README.md` file in the root of your repo. - Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin. diff --git a/esbuild.config.mjs b/esbuild.config.mjs index b13282b..a5de8b8 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -38,6 +38,7 @@ const context = await esbuild.context({ sourcemap: prod ? false : "inline", treeShaking: true, outfile: "main.js", + minify: prod, }); if (prod) { @@ -45,4 +46,4 @@ if (prod) { process.exit(0); } else { await context.watch(); -} \ No newline at end of file +} diff --git a/main.ts b/main.ts index 2d07212..0261989 100644 --- a/main.ts +++ b/main.ts @@ -1,134 +1,13 @@ -import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; +import { Plugin } from 'obsidian'; -// Remember to rename these classes and interfaces! +const UNSAFE_CHAR_REGEX = /[*?<>"]/g; -interface MyPluginSettings { - mySetting: string; -} - -const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: 'default' -} - -export default class MyPlugin extends Plugin { - settings: MyPluginSettings; - - async onload() { - await this.loadSettings(); - - // This creates an icon in the left ribbon. - const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => { - // Called when the user clicks the icon. - new Notice('This is a notice!'); - }); - // Perform additional things with the ribbon - ribbonIconEl.addClass('my-plugin-ribbon-class'); - - // This adds a status bar item to the bottom of the app. Does not work on mobile apps. - const statusBarItemEl = this.addStatusBarItem(); - statusBarItemEl.setText('Status Bar Text'); - - // This adds a simple command that can be triggered anywhere - this.addCommand({ - id: 'open-sample-modal-simple', - name: 'Open sample modal (simple)', - callback: () => { - new SampleModal(this.app).open(); - } - }); - // This adds an editor command that can perform some operation on the current editor instance - this.addCommand({ - id: 'sample-editor-command', - name: 'Sample editor command', - editorCallback: (editor: Editor, view: MarkdownView) => { - console.log(editor.getSelection()); - editor.replaceSelection('Sample Editor Command'); - } - }); - // This adds a complex command that can check whether the current state of the app allows execution of the command - this.addCommand({ - id: 'open-sample-modal-complex', - name: 'Open sample modal (complex)', - checkCallback: (checking: boolean) => { - // Conditions to check - const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); - if (markdownView) { - // If checking is true, we're simply "checking" if the command can be run. - // If checking is false, then we want to actually perform the operation. - if (!checking) { - new SampleModal(this.app).open(); - } - - // This command will only show up in Command Palette when the check function returns true - return true; - } - } - }); - - // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SampleSettingTab(this.app, this)); - - // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) - // Using this function will automatically remove the event listener when this plugin is disabled. - this.registerDomEvent(document, 'click', (evt: MouseEvent) => { - console.log('click', evt); - }); - - // When registering intervals, this function will automatically clear the interval when the plugin is disabled. - this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000)); - } - - onunload() { - - } - - async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); - } - - async saveSettings() { - await this.saveData(this.settings); - } -} - -class SampleModal extends Modal { - constructor(app: App) { - super(app); - } - - onOpen() { - const {contentEl} = this; - contentEl.setText('Woah!'); - } - - onClose() { - const {contentEl} = this; - contentEl.empty(); - } -} - -class SampleSettingTab extends PluginSettingTab { - plugin: MyPlugin; - - constructor(app: App, plugin: MyPlugin) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const {containerEl} = this; - - containerEl.empty(); - - new Setting(containerEl) - .setName('Setting #1') - .setDesc('It\'s a secret') - .addText(text => text - .setPlaceholder('Enter your secret') - .setValue(this.plugin.settings.mySetting) - .onChange(async (value) => { - this.plugin.settings.mySetting = value; - await this.plugin.saveSettings(); - })); - } +export default class SafeFilenames extends Plugin { + async onload() { + this.registerEvent(this.app.vault.on('rename', (file, oldPath) => { + if (UNSAFE_CHAR_REGEX.test(file.path)) { + this.app.fileManager.renameFile(file, file.path.replaceAll(UNSAFE_CHAR_REGEX, '')); + } + })); + } } diff --git a/manifest.json b/manifest.json index dfa940e..1c80431 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,10 @@ { - "id": "sample-plugin", - "name": "Sample Plugin", + "id": "safe-filenames", + "name": "Safe Filenames", "version": "1.0.0", "minAppVersion": "0.15.0", - "description": "Demonstrates some of the capabilities of the Obsidian API.", - "author": "Obsidian", - "authorUrl": "https://obsidian.md", - "fundingUrl": "https://obsidian.md/pricing", + "description": "Prevents renaming files with characters that can't be synced cross-platform.", + "author": "xenofem", + "authorUrl": "https://xeno.science", "isDesktopOnly": false } diff --git a/package.json b/package.json index 6a00766..0319f4e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "obsidian-sample-plugin", + "name": "obsidian-safe-filenames", "version": "1.0.0", - "description": "This is a sample plugin for Obsidian (https://obsidian.md)", + "description": "Obsidian plugin to prevent renaming files with unsafe characters", "main": "main.js", "scripts": { "dev": "node esbuild.config.mjs", @@ -9,7 +9,7 @@ "version": "node version-bump.mjs && git add manifest.json versions.json" }, "keywords": [], - "author": "", + "author": "xenofem", "license": "MIT", "devDependencies": { "@types/node": "^16.11.6", diff --git a/styles.css b/styles.css deleted file mode 100644 index 71cc60f..0000000 --- a/styles.css +++ /dev/null @@ -1,8 +0,0 @@ -/* - -This CSS file will be included with your plugin, and -available in the app when your plugin is enabled. - -If your plugin does not need CSS, delete this file. - -*/ diff --git a/tsconfig.json b/tsconfig.json index 2d6fbdf..c44b729 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "moduleResolution": "node", "importHelpers": true, "isolatedModules": true, - "strictNullChecks": true, + "strictNullChecks": true, "lib": [ "DOM", "ES5",