mirror of
https://github.com/tekknoman/obsidian-prio-plugin.git
synced 2026-07-22 07:30:23 +00:00
feat(plugin): implementing base functionality
This commit is contained in:
parent
3b016915fd
commit
556b40a5b2
8 changed files with 306 additions and 96 deletions
46
.gitignore
vendored
46
.gitignore
vendored
|
|
@ -1,22 +1,24 @@
|
||||||
# vscode
|
# vscode
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
# Intellij
|
# Intellij
|
||||||
*.iml
|
*.iml
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
# npm
|
# npm
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
# Don't include the compiled main.js file in the repo.
|
# Don't include the compiled main.js file in the repo.
|
||||||
# They should be uploaded to GitHub releases instead.
|
# They should be uploaded to GitHub releases instead.
|
||||||
main.js
|
main.js
|
||||||
|
|
||||||
# Exclude sourcemaps
|
# Exclude sourcemaps
|
||||||
*.map
|
*.map
|
||||||
|
|
||||||
# obsidian
|
# obsidian
|
||||||
data.json
|
data.json
|
||||||
|
|
||||||
# Exclude macOS Finder (System Explorer) View States
|
# Exclude macOS Finder (System Explorer) View States
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
app.css
|
||||||
|
|
|
||||||
149
main.ts
149
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 {
|
const DEFAULT_SETTINGS: PrioPluginSettings = {
|
||||||
mySetting: string;
|
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 = {
|
export default class PrioPlugin extends Plugin {
|
||||||
mySetting: 'default'
|
settings: PrioPluginSettings;
|
||||||
}
|
|
||||||
|
|
||||||
export default class MyPlugin extends Plugin {
|
|
||||||
settings: MyPluginSettings;
|
|
||||||
|
|
||||||
async onload() {
|
async onload() {
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
|
|
@ -28,45 +50,65 @@ export default class MyPlugin extends Plugin {
|
||||||
const statusBarItemEl = this.addStatusBarItem();
|
const statusBarItemEl = this.addStatusBarItem();
|
||||||
statusBarItemEl.setText('Status Bar Text');
|
statusBarItemEl.setText('Status Bar Text');
|
||||||
|
|
||||||
// This adds a simple command that can be triggered anywhere
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'open-sample-modal-simple',
|
id: 'set-prio',
|
||||||
name: 'Open sample modal (simple)',
|
name: 'Set priority',
|
||||||
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) => {
|
editorCallback: (editor: Editor, view: MarkdownView) => {
|
||||||
console.log(editor.getSelection());
|
setPrio(editor, view, this.settings);
|
||||||
editor.replaceSelection('Sample Editor Command');
|
},
|
||||||
}
|
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 adds a complex command that can check whether the current state of the app allows execution of the command
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'open-sample-modal-complex',
|
id: 'decrease-prio',
|
||||||
name: 'Open sample modal (complex)',
|
name: 'Decrease priority',
|
||||||
checkCallback: (checking: boolean) => {
|
editorCallback: (editor: Editor, view: MarkdownView) => {
|
||||||
// Conditions to check
|
decreasePrio(editor, view, this.settings);
|
||||||
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
},
|
||||||
if (markdownView) {
|
hotkeys: [
|
||||||
// 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.
|
modifiers: ['Ctrl', 'Shift', 'Alt'],
|
||||||
if (!checking) {
|
key: 'ArrowDown',
|
||||||
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 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)
|
// 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.
|
// 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();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"id": "obsidian-sample-plugin",
|
"id": "obsidian-prio-plugin",
|
||||||
"name": "Sample Plugin",
|
"name": "Obsidian Prio Plugin",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"minAppVersion": "0.15.0",
|
"minAppVersion": "0.15.0",
|
||||||
"description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.",
|
"description": "A plugin to prioritize your tasks and notes in Obsidian",
|
||||||
"author": "Obsidian",
|
"author": "EloiMusk",
|
||||||
"authorUrl": "https://obsidian.md",
|
"authorUrl": "https://github.com/EloiMusk/",
|
||||||
"fundingUrl": "https://obsidian.md/pricing",
|
"fundingUrl": "",
|
||||||
"isDesktopOnly": false
|
"isDesktopOnly": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
35
styles.css
35
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.
|
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
7
utils/Presets.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import {PrioPluginSettings} from "./Settings";
|
||||||
|
|
||||||
|
export interface Preset {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
settings: PrioPluginSettings;
|
||||||
|
}
|
||||||
74
utils/Priority.ts
Normal file
74
utils/Priority.ts
Normal 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
69
utils/SettingTab.ts
Normal 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
8
utils/Settings.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import {Preset} from "./Presets";
|
||||||
|
|
||||||
|
export interface PrioPluginSettings {
|
||||||
|
selectedPreset?: string;
|
||||||
|
levels: string[];
|
||||||
|
levelAliases: Record<string, string>;
|
||||||
|
presets?: Preset[];
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue