From 556b40a5b24e53a4922796abc30a461870c92980 Mon Sep 17 00:00:00 2001 From: EloiMusk Date: Wed, 8 Mar 2023 13:08:45 +0100 Subject: [PATCH] feat(plugin): implementing base functionality --- .gitignore | 46 +++++++------- main.ts | 149 ++++++++++++++++++++++++-------------------- manifest.json | 14 ++--- styles.css | 35 +++++++++++ utils/Presets.ts | 7 +++ utils/Priority.ts | 74 ++++++++++++++++++++++ utils/SettingTab.ts | 69 ++++++++++++++++++++ utils/Settings.ts | 8 +++ 8 files changed, 306 insertions(+), 96 deletions(-) create mode 100644 utils/Presets.ts create mode 100644 utils/Priority.ts create mode 100644 utils/SettingTab.ts create mode 100644 utils/Settings.ts diff --git a/.gitignore b/.gitignore index e09a007..6da5c30 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,24 @@ -# vscode -.vscode - -# Intellij -*.iml -.idea - -# npm -node_modules - -# Don't include the compiled main.js file in the repo. -# They should be uploaded to GitHub releases instead. -main.js - -# Exclude sourcemaps -*.map - -# obsidian -data.json - -# Exclude macOS Finder (System Explorer) View States -.DS_Store +# vscode +.vscode + +# Intellij +*.iml +.idea + +# npm +node_modules + +# Don't include the compiled main.js file in the repo. +# They should be uploaded to GitHub releases instead. +main.js + +# Exclude sourcemaps +*.map + +# obsidian +data.json + +# Exclude macOS Finder (System Explorer) View States +.DS_Store + +app.css diff --git a/main.ts b/main.ts index 50b75f3..b5d8a4d 100644 --- a/main.ts +++ b/main.ts @@ -1,17 +1,39 @@ -import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; +import {App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting} from 'obsidian'; +import {PrioPluginSettings} from "./utils/Settings"; +import {SettingTab} from "./utils/SettingTab"; +import {decreasePrio, increasePrio, removePrio, setPrio} from "./utils/Priority"; -// Remember to rename these classes and interfaces! -interface MyPluginSettings { - mySetting: string; +const DEFAULT_SETTINGS: PrioPluginSettings = { + selectedPreset: 'default', + levels: ['1', '2', '3', '4', '5', '6'], + levelAliases: { + '1': 'Major', + '2': 'Minor', + '3': 'Trivial', + '4': 'Cosmetic', + '5': 'Enhancement', + '6': 'Bug' + }, + presets: [{ + id: 'default', + name: 'Default', + settings: { + levels: ['1', '2', '3', '4', '5', '6'], + levelAliases: { + '1': 'Major', + '2': 'Minor', + '3': 'Trivial', + '4': 'Cosmetic', + '5': 'Enhancement', + '6': 'Bug' + }, + } + }] } -const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: 'default' -} - -export default class MyPlugin extends Plugin { - settings: MyPluginSettings; +export default class PrioPlugin extends Plugin { + settings: PrioPluginSettings; async onload() { await this.loadSettings(); @@ -28,45 +50,65 @@ export default class MyPlugin extends Plugin { 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', + id: 'set-prio', + name: 'Set priority', editorCallback: (editor: Editor, view: MarkdownView) => { - console.log(editor.getSelection()); - editor.replaceSelection('Sample Editor Command'); - } + setPrio(editor, view, this.settings); + }, + hotkeys: [ + { + modifiers: ['Ctrl', 'Shift', 'Alt'], + key: 'p', + } + ] + }); + + this.addCommand({ + id: 'remove-prio', + name: 'Remove priority', + editorCallback: (editor: Editor, view: MarkdownView) => { + removePrio(editor, view, this.settings); + }, + hotkeys: [ + { + modifiers: ['Ctrl', 'Shift', 'Alt'], + key: 'd', + } + ] + }) + + this.addCommand({ + id: 'increase-prio', + name: 'Increase priority', + editorCallback: (editor: Editor, view: MarkdownView) => { + increasePrio(editor, view, this.settings); + }, + hotkeys: [ + { + modifiers: ['Ctrl', 'Shift', 'Alt'], + key: 'ArrowUp', + } + ] }); // 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; + id: 'decrease-prio', + name: 'Decrease priority', + editorCallback: (editor: Editor, view: MarkdownView) => { + decreasePrio(editor, view, this.settings); + }, + hotkeys: [ + { + modifiers: ['Ctrl', 'Shift', 'Alt'], + key: 'ArrowDown', } - } + ] }); // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SampleSettingTab(this.app, this)); + this.addSettingTab(new SettingTab(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. @@ -107,31 +149,4 @@ class SampleModal extends Modal { } } -class SampleSettingTab extends PluginSettingTab { - plugin: MyPlugin; - constructor(app: App, plugin: MyPlugin) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const {containerEl} = this; - - containerEl.empty(); - - containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'}); - - 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) => { - console.log('Secret: ' + value); - this.plugin.settings.mySetting = value; - await this.plugin.saveSettings(); - })); - } -} diff --git a/manifest.json b/manifest.json index 0897aff..ae9460f 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,11 @@ { - "id": "obsidian-sample-plugin", - "name": "Sample Plugin", + "id": "obsidian-prio-plugin", + "name": "Obsidian Prio Plugin", "version": "1.0.0", "minAppVersion": "0.15.0", - "description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.", - "author": "Obsidian", - "authorUrl": "https://obsidian.md", - "fundingUrl": "https://obsidian.md/pricing", - "isDesktopOnly": false + "description": "A plugin to prioritize your tasks and notes in Obsidian", + "author": "EloiMusk", + "authorUrl": "https://github.com/EloiMusk/", + "fundingUrl": "", + "isDesktopOnly": true } diff --git a/styles.css b/styles.css index 71cc60f..50caebb 100644 --- a/styles.css +++ b/styles.css @@ -6,3 +6,38 @@ available in the app when your plugin is enabled. If your plugin does not need CSS, delete this file. */ +.preset-list { + display: flex; + flex-wrap: nowrap; + width: 100%; + border: 1px solid var(--color-base-10); + box-shadow: inset var(--shadow-stationary); + padding: 10px; +} + +.preset-list-item { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + padding: 5px; + border-top: 1px solid var(--background-modifier-border); + border-bottom: 1px solid var(--background-modifier-border); +} + +.preset-list-item:hover { + background-color: var(--background-modifier-hover); +} + +.btn { + margin: 5px; +} + +.btn-primary { + background-color: var(--interactive-accent); +} + +.btn-primary:hover { + background-color: var(--interactive-accent-hover); +} diff --git a/utils/Presets.ts b/utils/Presets.ts new file mode 100644 index 0000000..02194b8 --- /dev/null +++ b/utils/Presets.ts @@ -0,0 +1,7 @@ +import {PrioPluginSettings} from "./Settings"; + +export interface Preset { + id: string; + name: string; + settings: PrioPluginSettings; +} diff --git a/utils/Priority.ts b/utils/Priority.ts new file mode 100644 index 0000000..c26d016 --- /dev/null +++ b/utils/Priority.ts @@ -0,0 +1,74 @@ +import {Editor, MarkdownView} from "obsidian"; +import {PrioPluginSettings} from "./Settings"; + +export const setPrio = (editor: Editor, view: MarkdownView, settings: PrioPluginSettings) => { + if (getPrio(editor, view, settings)) { + return; + } + const cursor = editor.getCursor(); + const line = editor.getLine(cursor.line); + const levels = settings.levels; + const levelAliases = settings.levelAliases; + const level = levels[Math.round((levels.length - 1) / 2)]; + const levelAlias = levelAliases[level]; + const newLine = `${line} #${levelAlias}`; + editor.replaceRange(newLine, {line: cursor.line, ch: 0}, {line: cursor.line, ch: line.length}); + view.editor.focus(); +} + +export const increasePrio = (editor: Editor, view: MarkdownView, settings: PrioPluginSettings) => { + const currentPrio = getPrio(editor, view, settings); + if (!currentPrio) { + setPrio(editor, view, settings); + return; + } + const currentLevelIndex = getLevelIndex(editor, view, settings, currentPrio); + replacePrio(editor, view, settings, currentLevelIndex - 1 < 0 ? 0 : currentLevelIndex - 1); + +} + +export const decreasePrio = (editor: Editor, view: MarkdownView, settings: PrioPluginSettings) => { + const currentPrio = getPrio(editor, view, settings); + if (!currentPrio) { + setPrio(editor, view, settings); + return; + } + const currentLevelIndex = getLevelIndex(editor, view, settings, currentPrio); + const levels = settings.levels; + replacePrio(editor, view, settings, currentLevelIndex + 1 > levels.length - 1 ? levels.length - 1 : currentLevelIndex + 1); +} + +export const removePrio = (editor: Editor, view: MarkdownView, settings: PrioPluginSettings) => { + const cursor = editor.getCursor(); + const line = editor.getLine(cursor.line); + const newLine = line.replace(`#${getPrio(editor, view, settings)}`, ''); + editor.replaceRange(newLine, {line: cursor.line, ch: 0}, {line: cursor.line, ch: line.length}); + view.editor.focus(); +} + +export const getLevelIndex = (editor: Editor, view: MarkdownView, settings: PrioPluginSettings, prio: string) => { + const levels = settings.levels; + const levelAliases = settings.levelAliases; + const currentLevel = levels.find(level => levelAliases[level] === prio); + return levels.indexOf(currentLevel as string); +} + +export const replacePrio = (editor: Editor, view: MarkdownView, settings: PrioPluginSettings, levelIndex: number) => { + const cursor = editor.getCursor(); + const line = editor.getLine(cursor.line); + const levels = settings.levels; + const levelAliases = settings.levelAliases; + const level = levels[levelIndex]; + const levelAlias = levelAliases[level]; + const newLine = line.replace(`#${getPrio(editor, view, settings)}`, `#${levelAlias}`); + editor.replaceRange(newLine, {line: cursor.line, ch: 0}, {line: cursor.line, ch: line.length}); + view.editor.focus(); +} + +export const getPrio = (editor: Editor, view: MarkdownView, settings: PrioPluginSettings) => { + const cursor = editor.getCursor(); + const line = editor.getLine(cursor.line); + return line.match(/#\w+/g)?.filter(tag => + Object.values(settings.levelAliases).includes(tag.replace('#', '')) + ).first()?.replace('#', ''); +} diff --git a/utils/SettingTab.ts b/utils/SettingTab.ts new file mode 100644 index 0000000..7d63125 --- /dev/null +++ b/utils/SettingTab.ts @@ -0,0 +1,69 @@ +import {App, PluginSettingTab, Setting} from "obsidian"; +import PrioPlugin from "../main"; + +export class SettingTab extends PluginSettingTab { + plugin: PrioPlugin; + + constructor(app: App, plugin: PrioPlugin) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const {containerEl} = this; + + containerEl.empty(); + + let presets = this.plugin.settings.presets; + + containerEl.createEl('h2', {text: 'General Settings'}); + if (this.plugin.settings.presets) { + const dropDownOptions: Record = {'default': 'Default'}; + this.plugin.settings.presets.map(preset => { + return dropDownOptions[preset.id] = preset.name; + }); + new Setting(containerEl) + .setName('Preset') + .setDesc('Select a preset to use.') + .addSearch((search) => { + search + .setPlaceholder('Search Presets') + .onChange(async (value) => { + presets = this.plugin.settings.presets?.filter(preset => preset.name.toLowerCase().includes(value.toLowerCase())); + }); + }); + } + const presetList = createEl('ol', { + cls: 'preset-list', + parent: containerEl + }); + + (presets || []).map(preset => { + const el = createEl('li', { + text: preset.name, + cls: 'preset-list-item', + parent: presetList + }); + const btnGroup = el.createEl('div', { + cls: 'btn-group' + }); + + btnGroup.createEl('button', { + text: 'Select', + cls: ['preset-list-item-select', 'btn', 'btn-primary'], + attr: { + 'onclick': 'alert("Preset Selected")' + } + }) + btnGroup.createEl('button', { + text: 'Save', + cls: ['preset-list-item-save', 'btn', 'btn-primary'], + attr: { + 'onclick': 'alert("Save Preset")' + } + }) + return el; + }) + + } +} diff --git a/utils/Settings.ts b/utils/Settings.ts new file mode 100644 index 0000000..2f7626e --- /dev/null +++ b/utils/Settings.ts @@ -0,0 +1,8 @@ +import {Preset} from "./Presets"; + +export interface PrioPluginSettings { + selectedPreset?: string; + levels: string[]; + levelAliases: Record; + presets?: Preset[]; +}