feat(plugin): implementing base functionality

This commit is contained in:
EloiMusk 2023-03-08 13:08:45 +01:00
parent 3b016915fd
commit 556b40a5b2
No known key found for this signature in database
GPG key ID: A59863E13934DD4F
8 changed files with 306 additions and 96 deletions

2
.gitignore vendored
View file

@ -20,3 +20,5 @@ data.json
# Exclude macOS Finder (System Explorer) View States
.DS_Store
app.css

149
main.ts
View file

@ -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();
}));
}
}

View file

@ -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
}

View file

@ -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);
}

7
utils/Presets.ts Normal file
View file

@ -0,0 +1,7 @@
import {PrioPluginSettings} from "./Settings";
export interface Preset {
id: string;
name: string;
settings: PrioPluginSettings;
}

74
utils/Priority.ts Normal file
View file

@ -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('#', '');
}

69
utils/SettingTab.ts Normal file
View file

@ -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<string, string> = {'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;
})
}
}

8
utils/Settings.ts Normal file
View file

@ -0,0 +1,8 @@
import {Preset} from "./Presets";
export interface PrioPluginSettings {
selectedPreset?: string;
levels: string[];
levelAliases: Record<string, string>;
presets?: Preset[];
}