diff --git a/README.md b/README.md index a815286..1d2b65b 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,16 @@ -## Obsidian Sample Plugin +## Obsidian Emotion Picker -This is a sample plugin for Obsidian (https://obsidian.md). +Paste an emotion / feeling into note, using convenient list of emotions, divided into five categories: -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. +- Joy +- Love +- Surprise +- Anger +- Sadness +- Fear -**Note:** The Obsidian API is still in early alpha and is subject to change at any time! +Click on an emotion to insert it as text at the current cursor position. -This sample plugin demonstrates some of the basic functionality the plugin API can do. -- Changes the default font color to red using `styles.css`. -- Adds a ribbon icon, which shows a Notice when clicked. -- Adds a command "Open Sample Modal" which opens a Modal. -- Adds a plugin setting tab to the settings page. -- Registers a global click event and output 'click' to the console. -- Registers a global interval which logs 'setInterval' to the console. +![Plugin overview](./emotion-picker.png "Plugin overview") -### First time developing plugins? - -Quick starting guide for new plugin devs: - -- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it). -- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder. -- Install NodeJS, then run `npm i` in the command line under your repo folder. -- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`. -- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`. -- Reload Obsidian to load the new version of your plugin. -- Enable plugin in settings window. -- For updates to the Obsidian API run `npm update` in the command line under your repo folder. - -### Releasing new releases - -- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release. -- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible. -- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases -- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments. Note: The manifest.json file must be in two places, first the root path of your repository and also in the release. -- Publish the release. - -### Adding your plugin to the community plugin list - -- 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. - -### How to use - -- Clone this repo. -- `npm i` or `yarn` to install dependencies -- `npm run dev` to start compilation in watch mode. - -### Manually installing the plugin - -- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`. - -### Improve code quality with eslint (optional) -- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code. -- To use eslint with this project, make sure to install eslint from terminal: - - `npm install -g eslint` -- To use eslint to analyze this project use this command: - - `eslint main.ts` - - eslint will then create a report with suggestions for code improvement by file and line number. -- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: - - `eslint .\src\` - - -### API Documentation - -See https://github.com/obsidianmd/obsidian-api +The primary purpose is to provide an easier way to add entries to daily journal, emotions / mood tracker, etc. diff --git a/emotion-picker.png b/emotion-picker.png new file mode 100644 index 0000000..de6b947 Binary files /dev/null and b/emotion-picker.png differ diff --git a/main.ts b/main.ts index 9b24b2f..5ff5b70 100644 --- a/main.ts +++ b/main.ts @@ -1,137 +1,24 @@ -import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; - -// Remember to rename these classes and interfaces! - -interface MyPluginSettings { - mySetting: string; -} - -const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: 'default' -} +import { Plugin } from "obsidian"; +import { addSmileIcon } from "src/SmileIcon"; +import { EmotionPickerModal } from "./src/Modal"; export default class MyPlugin extends Plugin { - settings: MyPluginSettings; - async onload() { - await this.loadSettings(); + // add new icon for ribbon item + addSmileIcon(); - // 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!'); + this.addRibbonIcon("smile", "Emotions Picker", (evt: MouseEvent) => { + new EmotionPickerModal(this.app).open(); }); - // 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)', + id: "Open", + name: "Open", callback: () => { - new SampleModal(this.app).open(); - } + new EmotionPickerModal(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(); - - 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(); - })); - } + onunload() {} } diff --git a/manifest.json b/manifest.json index 30ec656..566f5bf 100644 --- a/manifest.json +++ b/manifest.json @@ -1,10 +1,10 @@ { - "id": "obsidian-sample-plugin", - "name": "Sample Plugin", - "version": "1.0.1", + "id": "obsidian-emotion-picker", + "name": "Emotion Picker", + "version": "0.1.0", "minAppVersion": "0.12.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", + "description": "A plugin for Obsidian.md that lets you choose an emotion from a list to insert into a note.", + "author": "dartungar", + "authorUrl": "https://dartungar.com", "isDesktopOnly": false } diff --git a/package.json b/package.json index 11071f9..639e59b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "obsidian-sample-plugin", + "name": "obsidian-emotion-picker", "version": "0.12.0", - "description": "This is a sample plugin for Obsidian (https://obsidian.md)", + "description": "A plugin for Obsidian.md that lets you choose an emotion from a list to insert into a note.", "main": "main.js", "scripts": { "dev": "node esbuild.config.mjs", diff --git a/src/Modal.ts b/src/Modal.ts new file mode 100644 index 0000000..d22be24 --- /dev/null +++ b/src/Modal.ts @@ -0,0 +1,103 @@ +import { App, Modal, MarkdownView, Notice } from "obsidian"; +import { Emotions } from "./emotions/Emotions"; +import { EmotionSection } from "./emotions/EmotionSection"; + +export class EmotionPickerModal extends Modal { + app: App; + content: HTMLElement; + emotions: Emotions; + useCommaInSeparator = false; + + constructor(app: App) { + super(app); + this.app = app; + this.emotions = new Emotions(); + } + + onOpen() { + const { contentEl } = this; + contentEl.classList.add("modal-body"); + + this.generateHeading(); + this.generateToggles(); + this.generateContentFromEmotions(); + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + } + + generateHeading(): void { + const headingEl = this.contentEl.createEl("h3"); + headingEl.innerText = "How do you feel?"; + } + + generateToggles(): void { + const togglesEl = this.contentEl.createDiv(); + + const useCommaToggleEl = this.generateToggleElement( + togglesEl, + "add comma after" + ); + useCommaToggleEl.onClickEvent(() => { + this.useCommaInSeparator = !this.useCommaInSeparator; + }); + } + + generateContentFromEmotions(): void { + const contentEl = this.contentEl.createDiv(); + + this.emotions.emotionSections.forEach((section) => { + this.generateElementFromEmotionSection(section, contentEl); + }); + } + + generateElementFromEmotionSection( + section: EmotionSection, + baseEl: HTMLElement + ): void { + const sectionEl = baseEl.createDiv(); + sectionEl.classList.add("emotion-section"); + + const heading = sectionEl.createEl("h4"); + heading.innerText = section.name; + heading.style.color = section.color; + heading.style.fontWeight = "bold"; + + section.emotions.forEach((emotionString) => { + const emotionEl = sectionEl.createDiv(); + emotionEl.innerHTML = emotionString; + emotionEl.style.textDecorationColor = section.color; + emotionEl.classList.add("emotion-element"); + emotionEl.onClickEvent(() => { + this.insertText( + " " + emotionString + (this.useCommaInSeparator ? "," : "") + ); + }); + }); + } + + insertText(text: string): void { + const view = this.app.workspace.getActiveViewOfType(MarkdownView); + if (view) { + const editor = view.editor; + editor.replaceRange(text, editor.getCursor()); + } + + new Notice(`Inserted '${text}'.`); + } + + generateToggleElement(baseEl: HTMLElement, text: string): HTMLDivElement { + const toggleContainerEl = baseEl.createDiv(); + const toggleEl = toggleContainerEl.createDiv(); + toggleEl.classList.add("checkbox-container"); + toggleEl.onClickEvent(() => toggleEl.classList.toggle("is-enabled")); + + const labelEl = toggleContainerEl.createEl("span"); + labelEl.classList.add("emotion-toggle-label"); + labelEl.textContent = text; + + return toggleContainerEl; + } +} diff --git a/src/SmileIcon.ts b/src/SmileIcon.ts new file mode 100644 index 0000000..98217d9 --- /dev/null +++ b/src/SmileIcon.ts @@ -0,0 +1,22 @@ +import { addIcon } from "obsidian"; + +export function addSmileIcon(): void { + addIcon( + "smile", + ` + + + ` + ); +} diff --git a/src/emotions/EmotionSection.ts b/src/emotions/EmotionSection.ts new file mode 100644 index 0000000..36a8640 --- /dev/null +++ b/src/emotions/EmotionSection.ts @@ -0,0 +1,5 @@ +export class EmotionSection { + name: string; + color: string; + emotions: string[]; +} diff --git a/src/emotions/Emotions.ts b/src/emotions/Emotions.ts new file mode 100644 index 0000000..3956eab --- /dev/null +++ b/src/emotions/Emotions.ts @@ -0,0 +1,20 @@ +import { EmotionSection } from "./EmotionSection"; +import { sectionsData } from "./emotionsData"; + +export class Emotions { + emotionSections: EmotionSection[] = []; + + constructor() { + this.populateSections(); + } + + populateSections(): void { + sectionsData.forEach((s) => { + const section = new EmotionSection(); + section.name = s.name; + section.color = s.color; + section.emotions = s.emotions; + this.emotionSections.push(section); + }); + } +} diff --git a/src/emotions/emotionsData.ts b/src/emotions/emotionsData.ts new file mode 100644 index 0000000..9692869 --- /dev/null +++ b/src/emotions/emotionsData.ts @@ -0,0 +1,121 @@ +export const sectionsData = [ + { + name: "Joy", + color: "#c1e08d", + emotions: [ + "joyful", + "content", + "pleased", + "satisfied", + "happy", + "amused", + "delighted", + "cheerful", + "jovial", + "blissful", + "proud", + "triumphant", + "optimistic", + "eager", + "hopeful", + "enthusiastic", + "excited", + "euphoric", + "enchanted", + ], + }, + { + name: "Love", + color: "#e8c3da", + emotions: [ + "loving", + "romantic", + "affectionate", + "passionate", + "attracted", + "sentimental", + "compassionate", + "satisfied", + "peaceful", + "relieved", + ], + }, + { + name: "Surprise", + color: "#94d4d3", + emotions: [ + "surprised", + "shocked", + "dismayed", + "confused", + "disillusioned", + "perplexed", + "amazed", + "astonished", + "moved", + "touched", + ], + }, + { + name: "Anger", + color: "#b84444", + emotions: [ + "angry", + "enraged", + "hateful", + "hostile", + "agitated", + "frustrated", + "irritated", + "annoyed", + "aggravated", + "envious", + "jealous", + "disgusted", + "contemptful ", + ], + }, + { + name: "Sadness", + color: "#4e72a3", + emotions: [ + "sad", + "hurt", + "agonizing", + "depressed", + "sorrowful", + "disappointed", + "dismayed", + "displeased", + "shameful", + "regretful", + "guilty", + "neglected", + "isolated", + "lonely", + "despaired", + "grieving", + "powerless", + ], + }, + { + name: "Fear", + color: "#dbab4b", + emotions: [ + "fearful", + "scared", + "helpless", + "frightened", + "panicking", + "hystetical", + "insecure", + "inferior", + "inadequate", + "nervous", + "anxious", + "worried", + "dreadful", + "mortified", + ], + }, +]; diff --git a/styles.css b/styles.css index cfd0fd7..22eae2d 100644 --- a/styles.css +++ b/styles.css @@ -1,4 +1,34 @@ -/* Sets all the text color to red! */ -body { - color: red; +/* override default Modal styles */ +.modal { + min-width: 230px !important; +} + +.modal-body > div { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; +} + +.emotion-toggle-label { + position: relative; + padding-left: 5px; + bottom: 5px; +} + +.emotion-section { + padding: 0.5rem; + width: 130px; +} + +.emotion-element { + text-decoration: underline solid 1px; + cursor: pointer; + transition: linear 0.1s; +} + +.emotion-element:hover { + /* text-decoration: underline solid 1px; */ + font-size: 103%; + text-decoration: underline solid 2px; }