From 7c90449bb2e89f3ae5eab71e24cc63859a879b13 Mon Sep 17 00:00:00 2001 From: valentine195 <38669521+valentine195@users.noreply.github.com> Date: Thu, 27 May 2021 15:55:13 -0400 Subject: [PATCH] 4.4.0 ## 4.4.0 - Closes #35 - Added ability to register and unregister commands to insert admonitions into a note - Admonitions that have been created in settings can have commands registered by clicking the new "Register Commands" button - Registering commands adds two commands: `Insert ` and `Insert With Title` - Registered commands can be removed by clicking "Unregister Commands" --- @types/types.ts | 3 + README.md | 20 ++++++ manifest.json | 2 +- package.json | 2 +- src/constants.ts | 158 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.css | 37 ++++++++++- src/main.ts | 139 +++++++++++++++++++++++++++-------------- src/settings.ts | 93 ++++++++++++++++++---------- src/util.ts | 1 + versions.json | 3 +- 10 files changed, 374 insertions(+), 84 deletions(-) create mode 100644 src/constants.ts diff --git a/@types/types.ts b/@types/types.ts index 48efd98..b15dc86 100644 --- a/@types/types.ts +++ b/@types/types.ts @@ -4,6 +4,7 @@ export interface Admonition { type: string; icon: string; color: string; + command: boolean; } export interface INestedAdmonition { @@ -43,4 +44,6 @@ export declare class ObsidianAdmonitionPlugin extends Plugin_2 { el: HTMLElement, ctx: MarkdownPostProcessorContext ) => void; + unregisterCommandsFor(admonition: Admonition): void; + registerCommandsFor(admonition: Admonition): void; } diff --git a/README.md b/README.md index 7d55b94..b8974b0 100644 --- a/README.md +++ b/README.md @@ -305,6 +305,19 @@ Admonitions will be automatically rendered as opened or closed when collapsible Adds a "copy content" button to every admonition block. +### Register and Unregister Commands + +Commands may be registered for each custom admonition type to insert them into an open note by clicking the `Register Commands` button. + +Clicking this button will add two commands for that admonition type: + +1. Insert +2. Insert with title + +These commands can have hotkeys assigned to them under Settings > Hotkeys. + +Registered commands may be removed by clicking on `Unregister Commands`. + ## Todo No additional features are planned at this time. If there is a feature missing that you would like to see, please open an issue. @@ -316,6 +329,13 @@ No additional features are planned at this time. If there is a feature missing t # Version History +## 4.4.0 + +- Added ability to register and unregister commands to insert admonitions into a note + - Admonitions that have been created in settings can have commands registered by clicking the new "Register Commands" button + - Registering commands adds two commands: `Insert ` and `Insert With Title` + - Registered commands can be removed by clicking "Unregister Commands" + ## 4.3.0 - Added Sync Links to Metadata Cache setting diff --git a/manifest.json b/manifest.json index 64b0d7b..4e72a14 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-admonition", "name": "Admonition", - "version": "4.3.1", + "version": "4.4.0", "minAppVersion": "0.11.0", "description": "Admonition block-styled content for Obsidian.md", "author": "Jeremy Valentine", diff --git a/package.json b/package.json index 1712b07..9f9056c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-admonition", - "version": "4.3.1", + "version": "4.4.0", "description": "Admonition block-styled content for Obsidian.md", "main": "main.js", "scripts": { diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..76d826a --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,158 @@ +import { Admonition } from "../@types/types"; + +export const ADD_ADMONITION_COMMAND_ICON = ``; +export const ADD_COMMAND_NAME = "add-admonition-command-icon"; + +export const REMOVE_ADMONITION_COMMAND_ICON = ``; +export const REMOVE_COMMAND_NAME = "remove-admonition-command-icon"; + + +export const ADMONITION_MAP: Record = { + note: { + type: "note", + color: "68, 138, 255", + icon: "pencil-alt", + command: false + }, + seealso: { + type: "note", + color: "68, 138, 255", + icon: "pencil-alt", + command: false + }, + abstract: { + type: "abstract", + color: "0, 176, 255", + icon: "book", + command: false + }, + summary: { + type: "abstract", + color: "0, 176, 255", + icon: "book", + command: false + }, + info: { + type: "info", + color: "0, 184, 212", + icon: "info-circle", + command: false + }, + todo: { + type: "info", + color: "0, 184, 212", + icon: "info-circle", + command: false + }, + tip: { type: "tip", color: "0, 191, 165", icon: "fire", command: false }, + hint: { type: "tip", color: "0, 191, 165", icon: "fire", command: false }, + important: { + type: "tip", + color: "0, 191, 165", + icon: "fire", + command: false + }, + success: { + type: "success", + color: "0, 200, 83", + icon: "check-circle", + command: false + }, + check: { + type: "success", + color: "0, 200, 83", + icon: "check-circle", + command: false + }, + done: { + type: "success", + color: "0, 200, 83", + icon: "check-circle", + command: false + }, + question: { + type: "question", + color: "100, 221, 23", + icon: "question-circle", + command: false + }, + help: { + type: "question", + color: "100, 221, 23", + icon: "question-circle", + command: false + }, + faq: { + type: "question", + color: "100, 221, 23", + icon: "question-circle", + command: false + }, + warning: { + type: "warning", + color: "255, 145, 0", + icon: "exclamation-triangle", + command: false + }, + caution: { + type: "warning", + color: "255, 145, 0", + icon: "exclamation-triangle", + command: false + }, + attention: { + type: "warning", + color: "255, 145, 0", + icon: "exclamation-triangle", + command: false + }, + failure: { + type: "failure", + color: "255, 82, 82", + icon: "times-circle", + command: false + }, + fail: { + type: "failure", + color: "255, 82, 82", + icon: "times-circle", + command: false + }, + missing: { + type: "failure", + color: "255, 82, 82", + icon: "times-circle", + command: false + }, + danger: { + type: "danger", + color: "255, 23, 68", + icon: "bolt", + command: false + }, + error: { + type: "danger", + color: "255, 23, 68", + icon: "bolt", + command: false + }, + bug: { type: "bug", color: "245, 0, 87", icon: "bug", command: false }, + example: { + type: "example", + color: "124, 77, 255", + icon: "list-ol", + command: false + }, + quote: { + type: "quote", + color: "158, 158, 158", + icon: "quote-right", + command: false + }, + cite: { + type: "quote", + color: "158, 158, 158", + icon: "quote-right", + command: false + } +}; diff --git a/src/main.css b/src/main.css index 2c86beb..2ecfe70 100644 --- a/src/main.css +++ b/src/main.css @@ -52,9 +52,9 @@ } .admonition-content { - margin: 10px 15px; - padding-right: 25px; - position: relative; + margin: 10px 15px; + padding-right: 25px; + position: relative; } .admonition-content-copy { @@ -127,6 +127,35 @@ details.admonition[open] > summary > .collapser > .handle { } /** Settings */ +.additional-container > .setting-item:not(.setting-item-heading) { + border: 0px; +} + +.additional-container { + border-bottom: 1px solid var(--background-modifier-border); + border-top: 1px solid var(--background-modifier-border); + padding: 18px 0 0 0; +} + +.additional-container > .setting-item-heading:only-child { + padding-bottom: 18px; +} + +.additional-container > .additional { + margin: 6px 12px; +} +.additional-container > .additional > .setting-item { + border-top: 0; + padding-top: 9px; +} +.additional-container + > .additional + > .setting-item + > .setting-item-control + > *:first-child { + margin: 0 6px; +} + .use-csv-marker > svg { color: yellow; margin-right: 8px; @@ -162,6 +191,8 @@ input.is-invalid { color: #dc3545; } +/** Internal */ + .admonition li.task-list-item.is-checked p { text-decoration: line-through; } diff --git a/src/main.ts b/src/main.ts index 13b1230..b3d086f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ import { + addIcon, MarkdownPostProcessorContext, MarkdownRenderChild, MarkdownRenderer, @@ -17,9 +18,26 @@ import { getMatches, getParametersFromSource } from "./util"; +import { + ADMONITION_MAP, + ADD_ADMONITION_COMMAND_ICON, + REMOVE_ADMONITION_COMMAND_ICON, + ADD_COMMAND_NAME, + REMOVE_COMMAND_NAME +} from "./constants"; import * as CodeMirror from "./codemirror"; +//add commands to app interface +declare module "obsidian" { + interface App { + commands: { + commands: { [id: string]: Command }; + editorCommands: { [id: string]: Command }; + findCommand(id: string): Command; + }; + } +} Object.fromEntries = Object.fromEntries || /** Polyfill taken from https://github.com/tc39/proposal-object-from-entries/blob/master/polyfill.js */ @@ -63,53 +81,6 @@ const DEFAULT_APP_SETTINGS: ISettingsData = { syncLinks: true }; -const ADMONITION_MAP: { - [admonitionType: string]: Admonition; -} = { - note: { type: "note", color: "68, 138, 255", icon: "pencil-alt" }, - seealso: { type: "note", color: "68, 138, 255", icon: "pencil-alt" }, - abstract: { type: "abstract", color: "0, 176, 255", icon: "book" }, - summary: { type: "abstract", color: "0, 176, 255", icon: "book" }, - info: { type: "info", color: "0, 184, 212", icon: "info-circle" }, - todo: { type: "info", color: "0, 184, 212", icon: "info-circle" }, - tip: { type: "tip", color: "0, 191, 165", icon: "fire" }, - hint: { type: "tip", color: "0, 191, 165", icon: "fire" }, - important: { type: "tip", color: "0, 191, 165", icon: "fire" }, - success: { type: "success", color: "0, 200, 83", icon: "check-circle" }, - check: { type: "success", color: "0, 200, 83", icon: "check-circle" }, - done: { type: "success", color: "0, 200, 83", icon: "check-circle" }, - question: { - type: "question", - color: "100, 221, 23", - icon: "question-circle" - }, - help: { type: "question", color: "100, 221, 23", icon: "question-circle" }, - faq: { type: "question", color: "100, 221, 23", icon: "question-circle" }, - warning: { - type: "warning", - color: "255, 145, 0", - icon: "exclamation-triangle" - }, - caution: { - type: "warning", - color: "255, 145, 0", - icon: "exclamation-triangle" - }, - attention: { - type: "warning", - color: "255, 145, 0", - icon: "exclamation-triangle" - }, - failure: { type: "failure", color: "255, 82, 82", icon: "times-circle" }, - fail: { type: "failure", color: "255, 82, 82", icon: "times-circle" }, - missing: { type: "failure", color: "255, 82, 82", icon: "times-circle" }, - danger: { type: "danger", color: "255, 23, 68", icon: "bolt" }, - error: { type: "danger", color: "255, 23, 68", icon: "bolt" }, - bug: { type: "bug", color: "245, 0, 87", icon: "bug" }, - example: { type: "example", color: "124, 77, 255", icon: "list-ol" }, - quote: { type: "quote", color: "158, 158, 158", icon: "quote-right" }, - cite: { type: "quote", color: "158, 158, 158", icon: "quote-right" } -}; export default class ObsidianAdmonition extends Plugin implements ObsidianAdmonitionPlugin @@ -181,11 +152,17 @@ export default class ObsidianAdmonition this.addSettingTab(new AdmonitionSetting(this.app, this)); + addIcon(ADD_COMMAND_NAME, ADD_ADMONITION_COMMAND_ICON); + addIcon(REMOVE_COMMAND_NAME, REMOVE_ADMONITION_COMMAND_ICON); + Object.keys(this.admonitions).forEach((type) => { this.registerMarkdownCodeBlockProcessor( `ad-${type}`, this.postprocessor.bind(this, type) ); + if (this.admonitions[type].command) { + this.registerCommandsFor(this.admonitions[type]); + } }); if (this.data.syntaxHighlight) { this.turnOnSyntaxHighlighting(); @@ -241,6 +218,74 @@ export default class ObsidianAdmonition }) ); } + unregisterCommandsFor(admonition: Admonition) { + admonition.command = false; + + if ( + this.app.commands.findCommand( + `obsidian-admonition:insert-${admonition.type}` + ) + ) { + delete this.app.commands.editorCommands[ + `obsidian-admonition:insert-${admonition.type}` + ]; + delete this.app.commands.editorCommands[ + `obsidian-admonition:insert-${admonition.type}-with-title` + ]; + delete this.app.commands.commands[ + `obsidian-admonition:insert-${admonition.type}` + ]; + delete this.app.commands.commands[ + `obsidian-admonition:insert-${admonition.type}-with-title` + ]; + } + } + registerCommandsFor(admonition: Admonition) { + admonition.command = true; + this.addCommand({ + id: `insert-${admonition.type}`, + name: `Insert ${admonition.type}`, + editorCallback: (editor, view) => { + if (admonition.command) { + try { + editor.getDoc().replaceSelection( + `\`\`\`ad-${admonition.type} + +\`\`\`\n` + ); + const cursor = editor.getCursor(); + editor.setCursor(cursor.line - 2); + } catch (e) { + new Notice( + "There was an issue inserting the admonition." + ); + } + } + } + }); + this.addCommand({ + id: `insert-${admonition.type}-with-title`, + name: `Insert ${admonition.type} With Title`, + editorCallback: (editor, view) => { + if (admonition.command) { + try { + editor.getDoc().replaceSelection( + `\`\`\`ad-${admonition.type} +title: + +\`\`\`\n` + ); + const cursor = editor.getCursor(); + editor.setCursor(cursor.line - 3); + } catch (e) { + new Notice( + "There was an issue inserting the admonition." + ); + } + } + } + }); + } turnOnSyntaxHighlighting(types: string[] = Object.keys(this.admonitions)) { if (!this.data.syntaxHighlight) return; types.forEach((type) => { diff --git a/src/settings.ts b/src/settings.ts index 25bce7c..a57f79a 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -12,6 +12,7 @@ import { Admonition, ObsidianAdmonitionPlugin } from "../@types/types"; import { getAdmonitionElement } from "./util"; import { findIconDefinition, icon, iconNames, IconName } from "./icons"; import { IconSuggestionModal } from "./modals"; +import { ADD_COMMAND_NAME, REMOVE_COMMAND_NAME } from "./constants"; /** Taken from https://stackoverflow.com/questions/34849001/check-if-css-selector-is-valid/42149818 */ const isSelectorValid = ((dummyElement) => (selector: string) => { @@ -127,7 +128,10 @@ export default class AdmonitionSetting extends PluginSettingTab { }); }); - new Setting(containerEl) + const additionalContainer = containerEl.createDiv( + "additional-container" + ); + new Setting(additionalContainer) .setName("Add New") .setDesc("Add a new Admonition type.") .addButton((button: ButtonComponent): ButtonComponent => { @@ -142,7 +146,8 @@ export default class AdmonitionSetting extends PluginSettingTab { this.plugin.addAdmonition({ type: modal.type, color: modal.color, - icon: modal.icon + icon: modal.icon, + command: false }); this.display(); } @@ -154,10 +159,11 @@ export default class AdmonitionSetting extends PluginSettingTab { return b; }); + const additional = additionalContainer.createDiv("additional"); for (let a in this.plugin.data.userAdmonitions) { const admonition = this.plugin.data.userAdmonitions[a]; - let setting = new Setting(containerEl); + let setting = new Setting(additional); let admonitionElement = await getAdmonitionElement( admonition.type, @@ -168,35 +174,60 @@ export default class AdmonitionSetting extends PluginSettingTab { ); setting.infoEl.replaceWith(admonitionElement); - setting.addButton((b) => { - b.setIcon("pencil") - .setTooltip("Edit") - .onClick(() => { - let modal = new SettingsModal(this.app, admonition); - - modal.onClose = async () => { - if (modal.saved) { - this.plugin.removeAdmonition(admonition); - this.plugin.addAdmonition({ - type: modal.type, - color: modal.color, - icon: modal.icon - }); - this.display(); - } - }; + if (!admonition.command) { + setting.addExtraButton((b) => { + b.setIcon(ADD_COMMAND_NAME) + .setTooltip("Register Commands") + .onClick(async () => { + this.plugin.registerCommandsFor(admonition); + await this.plugin.saveSettings(); + this.display(); + }); + }); + } else { + setting.addExtraButton((b) => { + b.setIcon(REMOVE_COMMAND_NAME) + .setTooltip("Unregister Commands") + .onClick(async () => { + this.plugin.unregisterCommandsFor(admonition); + await this.plugin.saveSettings(); + this.display(); + }); + }); + } - modal.open(); - }); - }); - setting.addButton((b) => { - b.setIcon("trash") - .setTooltip("Delete") - .onClick(() => { - this.plugin.removeAdmonition(admonition); - this.display(); - }); - }); + setting + .addExtraButton((b) => { + b.setIcon("pencil") + .setTooltip("Edit") + .onClick(() => { + let modal = new SettingsModal(this.app, admonition); + + modal.onClose = async () => { + if (modal.saved) { + const hasCommand = admonition.command; + this.plugin.removeAdmonition(admonition); + this.plugin.addAdmonition({ + type: modal.type, + color: modal.color, + icon: modal.icon, + command: hasCommand + }); + this.display(); + } + }; + + modal.open(); + }); + }) + .addExtraButton((b) => { + b.setIcon("trash") + .setTooltip("Delete") + .onClick(() => { + this.plugin.removeAdmonition(admonition); + this.display(); + }); + }); } } } diff --git a/src/util.ts b/src/util.ts index 21fd13e..263455f 100644 --- a/src/util.ts +++ b/src/util.ts @@ -2,6 +2,7 @@ import { findIconDefinition, icon, IconName } from "./icons"; import { INestedAdmonition } from "../@types/types"; import { MarkdownRenderer, Notice } from "obsidian"; + export function getMatches( src: string, from: number, diff --git a/versions.json b/versions.json index d27db12..1e1447f 100644 --- a/versions.json +++ b/versions.json @@ -8,5 +8,6 @@ "4.0.1": "0.11.0", "4.1.7": "0.11.0", "4.2.1": "0.11.0", - "4.3.1": "0.12.0" + "4.3.1": "0.12.0", + "4.4.0": "0.12.2" } \ No newline at end of file