├── .eslintignore
├── versions.json
├── src
├── emotions
│ ├── locale
│ │ ├── Locales.ts
│ │ ├── sv
│ │ │ └── DefaultEmotions.ts
│ │ └── es
│ │ │ └── DefaultEmotions.ts
│ ├── EmotionSection.ts
│ ├── index.ts
│ └── DefaultEmotions.ts
├── settings
│ ├── PluginSettings.ts
│ └── SettingsTab.ts
├── SmileIcon.ts
└── Modal.ts
├── emotion-picker.png
├── .editorconfig
├── .gitignore
├── manifest.json
├── tsconfig.json
├── README.md
├── .eslintrc
├── package.json
├── esbuild.config.mjs
├── .github
└── workflows
│ └── release.yml
├── LICENSE
├── styles.css
└── main.ts
/.eslintignore:
--------------------------------------------------------------------------------
1 | npm node_modules
2 | build
--------------------------------------------------------------------------------
/versions.json:
--------------------------------------------------------------------------------
1 | {
2 | "1.0.1": "0.9.12",
3 | "1.0.0": "0.9.7"
4 | }
5 |
--------------------------------------------------------------------------------
/src/emotions/locale/Locales.ts:
--------------------------------------------------------------------------------
1 | const locales = ["en", "sv", "es"];
2 |
3 | export { locales };
4 |
--------------------------------------------------------------------------------
/emotion-picker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dartungar/obsidian-emotion-picker/HEAD/emotion-picker.png
--------------------------------------------------------------------------------
/src/emotions/EmotionSection.ts:
--------------------------------------------------------------------------------
1 | export class EmotionSection {
2 | id: number;
3 | name: string;
4 | color: string;
5 | emotions: string[];
6 | }
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | insert_final_newline = true
7 | indent_style = tab
8 | indent_size = 4
9 | tab_width = 4
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # vscode
2 | .vscode
3 |
4 | # Intellij
5 | *.iml
6 | .idea
7 |
8 | # npm
9 | node_modules
10 | package-lock.json
11 |
12 | # Don't include the compiled main.js file in the repo.
13 | # They should be uploaded to GitHub releases instead.
14 | main.js
15 |
16 | # Exclude sourcemaps
17 | *.map
18 |
19 | # obsidian
20 | data.json
21 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "obsidian-emotion-picker",
3 | "name": "Emotion Picker",
4 | "version": "1.2.1",
5 | "minAppVersion": "0.12.0",
6 | "description": "A plugin for Obsidian.md that lets you choose an emotion from a list to insert into a note.",
7 | "author": "dartungar",
8 | "authorUrl": "https://dartungar.com",
9 | "fundingUrl": "https://www.paypal.com/paypalme/dartungar",
10 | "isDesktopOnly": false
11 | }
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "inlineSourceMap": true,
5 | "inlineSources": true,
6 | "module": "ESNext",
7 | "target": "ES6",
8 | "allowJs": true,
9 | "noImplicitAny": true,
10 | "moduleResolution": "node",
11 | "importHelpers": true,
12 | "lib": [
13 | "DOM",
14 | "ES5",
15 | "ES6",
16 | "ES7"
17 | ]
18 | },
19 | "include": [
20 | "**/*.ts"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Obsidian Emotion Picker
2 |
3 | Paste an emotion / feeling into note, using customizable list of emotions (or sensible default one).
4 |
5 | Click on an emotion to insert it as text at the current cursor position.
6 |
7 | Available options (default values can be set in Settings tab):
8 |
9 | - add comma after (convenient if entering more than one emotion)
10 | - insert as [[link]]
11 | - insert as #tag
12 | - capitalize the word before inserting
13 |
14 | 
15 |
16 | The primary purpose is to provide an easier way to add entries to daily journal, emotions / mood tracker, etc.
17 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "plugins": [
5 | "@typescript-eslint"
6 | ],
7 | "extends": [
8 | "eslint:recommended",
9 | "plugin:@typescript-eslint/eslint-recommended",
10 | "plugin:@typescript-eslint/recommended"
11 | ],
12 | "parserOptions": {
13 | "sourceType": "module"
14 | },
15 | "rules": {
16 | "no-unused-vars": "off",
17 | "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
18 | "@typescript-eslint/ban-ts-comment": "off",
19 | "no-prototype-builtins": "off",
20 | "@typescript-eslint/no-empty-function": "off"
21 | }
22 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "obsidian-emotion-picker",
3 | "version": "0.12.0",
4 | "description": "A plugin for Obsidian.md that lets you choose an emotion from a list to insert into a note.",
5 | "main": "main.js",
6 | "scripts": {
7 | "dev": "node esbuild.config.mjs",
8 | "build": "node esbuild.config.mjs production"
9 | },
10 | "keywords": [],
11 | "author": "",
12 | "license": "MIT",
13 | "devDependencies": {
14 | "@types/node": "^18.13.0",
15 | "@typescript-eslint/eslint-plugin": "^5.51.0",
16 | "@typescript-eslint/parser": "^5.51.0",
17 | "builtin-modules": "^3.3.0",
18 | "esbuild": "0.17.7",
19 | "obsidian": "^1.1.1",
20 | "tslib": "2.5.0",
21 | "typescript": "4.9.5"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/emotions/index.ts:
--------------------------------------------------------------------------------
1 | import { EmotionSection } from "./EmotionSection";
2 | import { DEFAULT_EMOTIONS } from "./DefaultEmotions";
3 | import { DEFAULT_SWEDISH_EMOTIONS } from "./locale/sv/DefaultEmotions";
4 | import { DEFAULT_SPANISH_EMOTIONS } from "./locale/es/DefaultEmotions";
5 |
6 |
7 |
8 | const getDefaultEmotions = (locale: string): EmotionSection[] => {
9 | switch (locale) {
10 | case "sv":
11 | return DEFAULT_SWEDISH_EMOTIONS;
12 | case "es":
13 | return DEFAULT_SPANISH_EMOTIONS;
14 | default:
15 | return DEFAULT_EMOTIONS;
16 | }
17 | };
18 |
19 | export {
20 | DEFAULT_EMOTIONS,
21 | DEFAULT_SWEDISH_EMOTIONS,
22 | DEFAULT_SPANISH_EMOTIONS,
23 | EmotionSection,
24 | getDefaultEmotions,
25 | };
26 |
--------------------------------------------------------------------------------
/esbuild.config.mjs:
--------------------------------------------------------------------------------
1 | import esbuild from "esbuild";
2 | import process from "process";
3 | import builtins from "builtin-modules";
4 |
5 | const banner = `/*
6 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
7 | if you want to view the source, please visit the github repository of this plugin
8 | */
9 | `;
10 |
11 | const prod = process.argv[2] === "production";
12 |
13 | esbuild
14 | .build({
15 | banner: {
16 | js: banner,
17 | },
18 | entryPoints: ["main.ts"],
19 | bundle: true,
20 | external: ["obsidian", "electron", ...builtins],
21 | format: "cjs",
22 | target: "es2016",
23 | logLevel: "info",
24 | sourcemap: prod ? false : "inline",
25 | treeShaking: true,
26 | outfile: "main.js",
27 | })
28 | .catch(() => process.exit(1));
29 |
--------------------------------------------------------------------------------
/src/settings/PluginSettings.ts:
--------------------------------------------------------------------------------
1 | import { DEFAULT_EMOTIONS } from "../emotions/index";
2 | import { EmotionSection } from "../emotions/EmotionSection";
3 |
4 | export type EmotionPickerLocaleOverride = "en" | string;
5 |
6 | export interface EmotionPickerSettings {
7 | modalHeaderText: string;
8 | useCommaInSeparator: boolean;
9 | addAsLink: boolean;
10 | addAsTag: boolean;
11 | capitalize: boolean;
12 | emotions: EmotionSection[];
13 | locale: EmotionPickerLocaleOverride;
14 | }
15 |
16 | export class DefaultSettings implements EmotionPickerSettings {
17 | modalHeaderText = "What do you feel?";
18 | useCommaInSeparator = false;
19 | addAsLink = false;
20 | addAsTag = false;
21 | capitalize = false;
22 | emotions = DEFAULT_EMOTIONS;
23 | locale: "en";
24 | }
25 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Create release
2 |
3 | on:
4 | push:
5 | tags:
6 | - "*"
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v3
14 |
15 | - name: Use Node.js
16 | uses: actions/setup-node@v3
17 | with:
18 | node-version: "18.x"
19 |
20 | - name: Build plugin
21 | run: |
22 | npm install
23 | npm run build
24 | - name: Create release
25 | env:
26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27 | run: |
28 | tag="${{ github.ref_name }}"
29 | gh release create "$tag" \
30 | --title="$tag" \
31 | --draft \
32 | main.js manifest.json styles.css
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [year] [fullname]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/styles.css:
--------------------------------------------------------------------------------
1 |
2 | /* settings */
3 | .emotion-section-setting {
4 | display: block;
5 | }
6 |
7 | .emotion-section-setting > .setting-item-control {
8 | display: flex;
9 | flex-direction: row;
10 | align-items: flex-start;
11 | justify-content: flex-start;
12 | }
13 |
14 | .emotion-section-setting > .setting-item-control > span {
15 | margin-right: 5px;
16 | }
17 |
18 | .emotion-section-setting > .setting-item-control > input, .emotion-section-setting > .setting-item-control > textarea {
19 | margin-right: 30px;
20 | }
21 |
22 | .emotion-section-setting textarea {
23 | max-height: 200px;
24 | }
25 |
26 | /* modal */
27 | /* override default Modal styles */
28 | .modal {
29 | min-width: 230px !important;
30 | }
31 |
32 | .modal-body > div {
33 | display: flex;
34 | flex-direction: row;
35 | flex-wrap: wrap;
36 | justify-content: space-between;
37 | }
38 | .toggles-section {
39 | justify-content: flex-start !important;
40 | }
41 |
42 | .emotion-toggle-label {
43 | position: relative;
44 | padding-left: 5px;
45 | padding-right: 30px;
46 | bottom: 5px;
47 | }
48 |
49 | .emotion-section {
50 | padding: 0.5rem;
51 | width: 130px;
52 | }
53 |
54 | .emotion-element {
55 | text-decoration: underline solid 1px;
56 | cursor: pointer;
57 | transition: linear 0.1s;
58 | }
59 |
60 | .emotion-element:hover {
61 | /* text-decoration: underline solid 1px; */
62 | font-size: 103%;
63 | text-decoration: underline solid 2px;
64 | }
65 |
--------------------------------------------------------------------------------
/main.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from "obsidian";
2 | import { DefaultSettings, EmotionPickerSettings } from "src/settings/PluginSettings";
3 | import { addSmileIcon } from "src/SmileIcon";
4 | import { EmotionPickerModal } from "./src/Modal";
5 | import { EmotionPickerSettingsTab } from "src/settings/SettingsTab";
6 | import { EmotionSection, getDefaultEmotions } from "src/emotions";
7 |
8 | export default class EmotionPickerPlugin extends Plugin {
9 | settings: EmotionPickerSettings;
10 |
11 | async onload() {
12 | // add new icon for ribbon item
13 | addSmileIcon();
14 |
15 | this.addRibbonIcon("smile", "Emotions Picker", (evt: MouseEvent) => {
16 | new EmotionPickerModal(this.app, this).open();
17 | });
18 |
19 | this.addCommand({
20 | id: "Open",
21 | name: "Open",
22 | callback: () => {
23 | new EmotionPickerModal(this.app, this).open();
24 | },
25 | });
26 |
27 | await this.loadSettings();
28 |
29 | this.addSettingTab(new EmotionPickerSettingsTab(this.app, this));
30 | }
31 |
32 | onunload() {}
33 |
34 | async loadSettings() {
35 | const loadedSettings = await this.loadData();
36 | this.settings = Object.assign(
37 | {},
38 | new DefaultSettings(),
39 | loadedSettings
40 | );
41 | }
42 |
43 | async saveSettings() {
44 | await this.saveData(this.settings);
45 | }
46 |
47 | getEmotionsOrDefault(): EmotionSection[] {
48 | if (this.settings.emotions && this.settings.emotions.length > 0)
49 | return this.settings.emotions;
50 | return getDefaultEmotions(this.settings.locale);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/SmileIcon.ts:
--------------------------------------------------------------------------------
1 | import { addIcon } from "obsidian";
2 |
3 | export function addSmileIcon(): void {
4 | addIcon(
5 | "smile",
6 | `
10 |
14 |
17 | `
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/src/emotions/locale/sv/DefaultEmotions.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_SWEDISH_EMOTIONS = [
2 | {
3 | id: 1,
4 | name: "Glädje",
5 | color: "#c1e08d",
6 | emotions: [
7 | "glad",
8 | "nöjd",
9 | "belåten",
10 | "tillfredsställd",
11 | "lycklig",
12 | "underhållen",
13 | "förtjust",
14 | "munter",
15 | "jovial",
16 | "lycklig",
17 | "stolt",
18 | "segervisst",
19 | "optimistisk",
20 | "ivrig",
21 | "hoppfull",
22 | "entusiastisk",
23 | "upphetsad",
24 | "euforisk",
25 | "besvärjad",
26 | ],
27 | },
28 | {
29 | id: 2,
30 | name: "Kärleksfull",
31 | color: "#e8c3da",
32 | emotions: [
33 | "kärleksfull",
34 | "romantisk",
35 | "tillgiven",
36 | "passionerad",
37 | "attraherad",
38 | "sentimental",
39 | "medlidsam",
40 | "tillfredsställd",
41 | "fredfull",
42 | "lättad",
43 | ],
44 | },
45 | {
46 | id: 3,
47 | name: "Överraskad",
48 | color: "#94d4d3",
49 | emotions: [
50 | "förvånad",
51 | "chockerad",
52 | "bedrövad",
53 | "förvirrad",
54 | "besviken",
55 | "förvirrad",
56 | "häpna",
57 | "häpnadsväckande",
58 | "rörde",
59 | "rörde",
60 | ],
61 | },
62 | {
63 | id: 4,
64 | name: "Illa",
65 | color: "#b84444",
66 | emotions: [
67 | "arg",
68 | "rasande",
69 | "hatfull",
70 | "upprörd",
71 | "frustrerad",
72 | "irriterad",
73 | "frustrerad",
74 | "avundsjuk",
75 | "svartsjuk",
76 | "äcklad",
77 | "föraktfull",
78 | ],
79 | },
80 | {
81 | id: 5,
82 | name: "Sorg",
83 | color: "#4e72a3",
84 | emotions: [
85 | "ledsen",
86 | "skadad",
87 | "plågsam",
88 | "deprimerad",
89 | "bedrövad",
90 | "besviken",
91 | "missnöjd",
92 | "skamsen",
93 | "ångerfull",
94 | "skyldig",
95 | "negligerad",
96 | "isolerad",
97 | "ensam",
98 | "sörjande",
99 | "maktlös",
100 | ],
101 | },
102 | {
103 | id: 6,
104 | name: "Rädsla",
105 | color: "#dbab4b",
106 | emotions: [
107 | "rädd",
108 | "maktlös",
109 | "panik",
110 | "hystetisk",
111 | "osäker",
112 | "underlägsen",
113 | "inkompetent",
114 | "nervös",
115 | "ångestfull",
116 | "bekymrad",
117 | "hemsk",
118 | "förskräcklig",
119 | ],
120 | },
121 | ];
122 |
--------------------------------------------------------------------------------
/src/emotions/DefaultEmotions.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_EMOTIONS = [
2 | {
3 | id: 1,
4 | name: "Joy",
5 | color: "#c1e08d",
6 | emotions: [
7 | "joyful",
8 | "content",
9 | "pleased",
10 | "satisfied",
11 | "happy",
12 | "amused",
13 | "delighted",
14 | "cheerful",
15 | "jovial",
16 | "blissful",
17 | "proud",
18 | "triumphant",
19 | "optimistic",
20 | "eager",
21 | "hopeful",
22 | "enthusiastic",
23 | "excited",
24 | "euphoric",
25 | "enchanted",
26 | ],
27 | },
28 | {
29 | id: 2,
30 | name: "Love",
31 | color: "#e8c3da",
32 | emotions: [
33 | "loving",
34 | "romantic",
35 | "affectionate",
36 | "passionate",
37 | "attracted",
38 | "sentimental",
39 | "compassionate",
40 | "satisfied",
41 | "peaceful",
42 | "relieved",
43 | ],
44 | },
45 | {
46 | id: 3,
47 | name: "Surprise",
48 | color: "#94d4d3",
49 | emotions: [
50 | "surprised",
51 | "shocked",
52 | "dismayed",
53 | "confused",
54 | "disillusioned",
55 | "perplexed",
56 | "amazed",
57 | "astonished",
58 | "moved",
59 | "touched",
60 | ],
61 | },
62 | {
63 | id: 4,
64 | name: "Anger",
65 | color: "#b84444",
66 | emotions: [
67 | "angry",
68 | "enraged",
69 | "hateful",
70 | "hostile",
71 | "agitated",
72 | "frustrated",
73 | "irritated",
74 | "annoyed",
75 | "aggravated",
76 | "envious",
77 | "jealous",
78 | "disgusted",
79 | "contemptful",
80 | ],
81 | },
82 | {
83 | id: 5,
84 | name: "Sadness",
85 | color: "#4e72a3",
86 | emotions: [
87 | "sad",
88 | "hurt",
89 | "agonizing",
90 | "depressed",
91 | "sorrowful",
92 | "disappointed",
93 | "dismayed",
94 | "displeased",
95 | "shameful",
96 | "regretful",
97 | "guilty",
98 | "neglected",
99 | "isolated",
100 | "lonely",
101 | "despaired",
102 | "grieving",
103 | "powerless",
104 | ],
105 | },
106 | {
107 | id: 6,
108 | name: "Fear",
109 | color: "#dbab4b",
110 | emotions: [
111 | "fearful",
112 | "scared",
113 | "helpless",
114 | "frightened",
115 | "panicking",
116 | "hystetical",
117 | "insecure",
118 | "inferior",
119 | "inadequate",
120 | "nervous",
121 | "anxious",
122 | "worried",
123 | "dreadful",
124 | "mortified",
125 | ],
126 | },
127 | ];
128 |
--------------------------------------------------------------------------------
/src/emotions/locale/es/DefaultEmotions.ts:
--------------------------------------------------------------------------------
1 | export const DEFAULT_SPANISH_EMOTIONS = [
2 | {
3 | id: 1,
4 | name: "Alegría",
5 | color: "#c1e08d",
6 | emotions: [
7 | "alegre",
8 | "contento",
9 | "gozoso",
10 | "satisfecho",
11 | "feliz",
12 | "divertido",
13 | "encantado",
14 | "jovial",
15 | "dichoso",
16 | "orgulloso",
17 | "triunfante",
18 | "optimista",
19 | "ansioso",
20 | "esperanzado",
21 | "entusiasmado",
22 | "excitado",
23 | "eufórico",
24 | ],
25 | },
26 | {
27 | id: 2,
28 | name: "Amor",
29 | color: "#e8c3da",
30 | emotions: [
31 | "amoroso",
32 | "romántico",
33 | "afectuoso",
34 | "apasionado",
35 | "atraído",
36 | "sentimental",
37 | "compasivo",
38 | "satisfecho",
39 | "pacífico",
40 | "aliviado",
41 | ],
42 | },
43 | {
44 | id: 3,
45 | name: "Sorpresa",
46 | color: "#94d4d3",
47 | emotions: [
48 | "sorprendido",
49 | "conmocionado",
50 | "consternado",
51 | "confundido",
52 | "desilusionado",
53 | "perplejo",
54 | "asombrado",
55 | "atónito",
56 | "conmovido",
57 | "tocado",
58 | ],
59 | },
60 | {
61 | id: 4,
62 | name: "Rabia",
63 | color: "#b84444",
64 | emotions: [
65 | "enojado",
66 | "enfurecido",
67 | "odioso",
68 | "hostil",
69 | "agitado",
70 | "frustrado",
71 | "irritado",
72 | "agravado",
73 | "envidioso",
74 | "celoso",
75 | "disgustado",
76 | "despreciativo",
77 | ],
78 | },
79 | {
80 | id: 5,
81 | name: "Tristeza",
82 | color: "#4e72a3",
83 | emotions: [
84 | "triste",
85 | "herido",
86 | "agonizante",
87 | "deprimido",
88 | "doloroso",
89 | "decepcionado",
90 | "consternado",
91 | "disgustado",
92 | "vergonzoso",
93 | "arrepentido",
94 | "culpable",
95 | "abandonado",
96 | "aislado",
97 | "solitario",
98 | "desesperado",
99 | "afligido",
100 | "impotente",
101 | ],
102 | },
103 | {
104 | id: 6,
105 | name: "Miedo",
106 | color: "#dbab4b",
107 | emotions: [
108 | "temeroso",
109 | "asustado",
110 | "impotente",
111 | "atemorizado",
112 | "en pánico",
113 | "histérico",
114 | "inseguro",
115 | "inferior",
116 | "inadecuado",
117 | "nervioso",
118 | "ansioso",
119 | "preocupado",
120 | "terrible",
121 | "mortificado",
122 | ],
123 | },
124 | ];
125 |
--------------------------------------------------------------------------------
/src/Modal.ts:
--------------------------------------------------------------------------------
1 | import EmotionPickerPlugin from "main";
2 | import {
3 | App,
4 | Modal,
5 | MarkdownView,
6 | Notice,
7 | EditorPosition,
8 | Editor,
9 | } from "obsidian";
10 | import { EmotionSection } from "./emotions/EmotionSection";
11 | import { EmotionPickerSettings } from "./settings/PluginSettings";
12 |
13 | export class EmotionPickerModal extends Modal {
14 | app: App;
15 | plugin: EmotionPickerPlugin;
16 | content: HTMLElement;
17 | emotions: EmotionSection[];
18 | editor: Editor;
19 | initialCursorPosition: EditorPosition;
20 | locale: string;
21 | // current settings
22 | state: EmotionPickerSettings;
23 |
24 | constructor(app: App, plugin: EmotionPickerPlugin) {
25 | super(app);
26 | this.app = app;
27 | this.plugin = plugin;
28 | this.locale = this.plugin.settings.locale;
29 | this.emotions = plugin.getEmotionsOrDefault();
30 | }
31 |
32 | onOpen() {
33 | const { contentEl } = this;
34 | this.state = this.plugin.settings;
35 | contentEl.classList.add("modal-body");
36 |
37 | this.generateHeading();
38 | this.generateToggles();
39 | this.generateContentFromEmotions();
40 |
41 | const view = this.app.workspace.getActiveViewOfType(MarkdownView);
42 | if (view) {
43 | this.editor = view.editor;
44 | this.initialCursorPosition = this.editor.getCursor();
45 | } else {
46 | new Notice(`Error getting cursor position.`);
47 | this.close();
48 | }
49 | }
50 |
51 | onClose() {
52 | const { contentEl } = this;
53 | contentEl.empty();
54 | this.initialCursorPosition = undefined;
55 | }
56 |
57 | generateHeading(): void {
58 | const headingEl = this.contentEl.createEl("h3");
59 | headingEl.innerText = this.plugin.settings.modalHeaderText;
60 | }
61 |
62 | generateToggles(): void {
63 | const togglesEl = this.contentEl.createDiv();
64 | togglesEl.addClass("toggles-section");
65 |
66 | const useCommaToggleEl = this.generateToggleElement(
67 | togglesEl,
68 | "add comma after",
69 | this.state.useCommaInSeparator
70 | );
71 | useCommaToggleEl.onClickEvent(() => {
72 | this.state.useCommaInSeparator = !this.state.useCommaInSeparator;
73 | });
74 |
75 | const addAsLinkToggleEl = this.generateToggleElement(
76 | togglesEl,
77 | "add as [[link]]",
78 | this.state.addAsLink
79 | );
80 | addAsLinkToggleEl.onClickEvent(() => {
81 | this.state.addAsLink = !this.state.addAsLink;
82 | });
83 |
84 | const addAsTagToggleEl = this.generateToggleElement(
85 | togglesEl,
86 | "add as #tag",
87 | this.state.addAsTag
88 | );
89 | addAsTagToggleEl.onClickEvent(() => {
90 | this.state.addAsTag = !this.state.addAsTag;
91 | });
92 | }
93 |
94 | generateContentFromEmotions(): void {
95 | const contentEl = this.contentEl.createDiv();
96 |
97 | this.emotions.forEach((section) => {
98 | this.generateElementFromEmotionSection(section, contentEl);
99 | });
100 | }
101 |
102 | generateElementFromEmotionSection(
103 | section: EmotionSection,
104 | baseEl: HTMLElement
105 | ): void {
106 | const sectionEl = baseEl.createDiv();
107 | sectionEl.classList.add("emotion-section");
108 |
109 | const heading = sectionEl.createEl("h4");
110 | heading.innerText = section.name;
111 | heading.style.color = section.color;
112 | heading.style.fontWeight = "bold";
113 |
114 | section.emotions.forEach((emotionString) => {
115 | const emotionEl = sectionEl.createDiv();
116 | emotionEl.innerHTML = emotionString;
117 | emotionEl.style.textDecorationColor = section.color;
118 | emotionEl.classList.add("emotion-element");
119 | emotionEl.onClickEvent(() => {
120 | this.insertText(this.getFinalText(emotionString));
121 | });
122 | });
123 | }
124 |
125 | insertText(text: string): void {
126 | if (this.editor) {
127 | this.editor.replaceRange(text, this.initialCursorPosition);
128 | this.initialCursorPosition.ch += text.length;
129 | }
130 |
131 | new Notice(`Inserted '${text}'.`);
132 | }
133 |
134 | generateToggleElement(
135 | baseEl: HTMLElement,
136 | text: string,
137 | initialState = false
138 | ): HTMLDivElement {
139 | const toggleContainerEl = baseEl.createDiv();
140 |
141 | const toggleEl = toggleContainerEl.createDiv();
142 | toggleEl.classList.add("checkbox-container");
143 | toggleEl.onClickEvent(() => toggleEl.classList.toggle("is-enabled"));
144 |
145 | if (initialState == true) {
146 | toggleEl.classList.add("is-enabled");
147 | }
148 |
149 | const labelEl = toggleContainerEl.createEl("span");
150 | labelEl.classList.add("emotion-toggle-label");
151 | labelEl.textContent = text;
152 |
153 | return toggleContainerEl;
154 | }
155 |
156 | getFinalText(text: string): string {
157 | if (this.state.capitalize) text = this.capitalize(text);
158 | if (this.state.addAsTag) text = `#${text}`;
159 | if (this.state.addAsLink) text = `[[${text}]]`;
160 | if (this.state.useCommaInSeparator) text = text + ", ";
161 | text = " " + text;
162 | return text;
163 | }
164 |
165 | capitalize(text: string): string {
166 | return text.charAt(0).toUpperCase() + text.slice(1);
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/settings/SettingsTab.ts:
--------------------------------------------------------------------------------
1 | import { App, PluginSettingTab, Setting } from "obsidian";
2 | import { locales } from "../emotions/locale/Locales";
3 | import EmotionPickerPlugin from "../../main";
4 | import { EmotionPickerLocaleOverride } from "./PluginSettings";
5 | import { getDefaultEmotions } from "src/emotions";
6 | export class EmotionPickerSettingsTab extends PluginSettingTab {
7 | plugin: EmotionPickerPlugin;
8 |
9 | constructor(app: App, plugin: EmotionPickerPlugin) {
10 | super(app, plugin);
11 | this.plugin = plugin;
12 | this.plugin.settings.emotions = plugin.getEmotionsOrDefault();
13 | }
14 |
15 | refresh(): void {
16 | this.display();
17 | }
18 |
19 | display(): void {
20 | const { containerEl } = this;
21 |
22 | containerEl.empty();
23 |
24 | containerEl.createEl("h2", { text: "Emotion Picker Settings" });
25 |
26 | // TODO: customize modal header
27 |
28 | new Setting(containerEl)
29 | .setName("Modal header text")
30 | .setDesc("Customize modal header text")
31 | .addText((textField) => {
32 | textField.setValue(this.plugin.settings.modalHeaderText);
33 | textField.onChange((newValue) => {
34 | this.plugin.settings.modalHeaderText = newValue;
35 | this.plugin.saveSettings();
36 | });
37 | });
38 |
39 | new Setting(containerEl)
40 | .setName("Add comma after")
41 | .setDesc("Add a comma after pasted emotion")
42 | .addToggle((toggle) =>
43 | toggle
44 | .setValue(this.plugin.settings.useCommaInSeparator)
45 | .onChange(async (value) => {
46 | this.plugin.settings.useCommaInSeparator = value;
47 | await this.plugin.saveSettings();
48 | })
49 | );
50 |
51 | new Setting(containerEl)
52 | .setName("Insert as link")
53 | .setDesc("Insert emotion as a [[link]]")
54 | .addToggle((toggle) =>
55 | toggle
56 | .setValue(this.plugin.settings.addAsLink)
57 | .onChange(async (value) => {
58 | this.plugin.settings.addAsLink = value;
59 | await this.plugin.saveSettings();
60 | })
61 | );
62 |
63 | new Setting(containerEl)
64 | .setName("Insert as tag")
65 | .setDesc("Insert emotion as a #tag")
66 | .addToggle((toggle) =>
67 | toggle
68 | .setValue(this.plugin.settings.addAsTag)
69 | .onChange(async (value) => {
70 | this.plugin.settings.addAsTag = value;
71 | await this.plugin.saveSettings();
72 | })
73 | );
74 |
75 | new Setting(containerEl)
76 | .setName("Capitalize")
77 | .setDesc("Capitalize (useful if inserting emotion as link or tag)")
78 | .addToggle((toggle) =>
79 | toggle
80 | .setValue(this.plugin.settings.capitalize)
81 | .onChange(async (value) => {
82 | this.plugin.settings.capitalize = value;
83 | await this.plugin.saveSettings();
84 | })
85 | );
86 |
87 | new Setting(this.containerEl)
88 | .setName("Override locale:")
89 | .setDesc(
90 | "Set this if you want to use a locale different from the default. \nWARNING: changing locale will erase your custom emotions for current locale!"
91 | )
92 | .addDropdown((dropdown) => {
93 | locales.forEach((locale) => {
94 | dropdown.addOption(locale, locale);
95 | });
96 | dropdown.setValue(this.plugin.settings.locale);
97 | dropdown.onChange(async (value) => {
98 | const isLocaleChanged = value !== this.plugin.settings.locale;
99 | if (isLocaleChanged) {
100 | this.plugin.settings.locale = value as EmotionPickerLocaleOverride;
101 | this.plugin.settings.emotions = getDefaultEmotions(value);
102 | await this.plugin.saveSettings();
103 | this.refresh();
104 | }
105 | });
106 | });
107 |
108 | containerEl.createEl("h3", { text: "Emotion groups" });
109 |
110 | // TODO: refresh settings page after saving
111 |
112 | this.plugin.settings.emotions.forEach((es) => {
113 | const setting = new Setting(containerEl);
114 | setting.setClass("emotion-section-setting");
115 |
116 | const groupNameLabel = createEl("span", { text: "Name: " });
117 | setting.controlEl.appendChild(groupNameLabel);
118 |
119 | setting.addText((textField) => {
120 | textField
121 | .setValue(es.name)
122 | .setPlaceholder("group name")
123 | .onChange((value) => {
124 | es.name = value;
125 | this.plugin.saveSettings();
126 | });
127 | });
128 |
129 | const colorPickerLabel = createEl("span", { text: "Color: " });
130 | setting.controlEl.appendChild(colorPickerLabel);
131 |
132 | const colorPicker = createEl(
133 | "input",
134 | {
135 | type: "color",
136 | cls: "settings-color-picker",
137 | },
138 | (el) => {
139 | el.value = es.color;
140 | el.onchange = () => {
141 | es.color = el.value;
142 | this.plugin.saveSettings();
143 | };
144 | }
145 | );
146 |
147 | setting.controlEl.appendChild(colorPicker);
148 |
149 | const emotionsListLabel = createEl("span", {
150 | text: "Emotions:",
151 | });
152 | setting.controlEl.appendChild(emotionsListLabel);
153 |
154 | setting.addTextArea((textArea) => {
155 | textArea.inputEl.setAttribute(
156 | "rows",
157 | es.emotions.length.toString()
158 | );
159 |
160 | textArea
161 | .setPlaceholder("emotions, separated by newline or comma.")
162 | .setValue(es.emotions.join("\n"))
163 | .onChange(async (value) => {
164 | es.emotions = splitByCommaAndNewline(value);
165 | this.plugin.saveSettings();
166 | });
167 | });
168 |
169 | setting.addButton((btn) => {
170 | btn.setButtonText("delete group");
171 | btn.setClass("settings-delete-btn");
172 | btn.onClick(() => {
173 | this.plugin.settings.emotions =
174 | this.plugin.settings.emotions.filter(
175 | (e) => e.id !== es.id
176 | );
177 | this.plugin.saveSettings();
178 | this.refresh();
179 | });
180 | });
181 | });
182 |
183 | new Setting(containerEl).addButton((btn) => {
184 | btn.setButtonText("Add group");
185 | btn.onClick(() => {
186 | this.plugin.settings.emotions.push({
187 | id:
188 | Math.max(
189 | ...this.plugin.settings.emotions.map((e) => e.id)
190 | ) + 1, // id "generation"
191 | name: "New section",
192 | emotions: [],
193 | color: "#000000",
194 | });
195 | this.plugin.saveSettings();
196 | this.refresh();
197 | });
198 | });
199 | }
200 | }
201 |
202 | function splitByCommaAndNewline(rawEmotions: string): string[] {
203 | const splitted: string[] = [];
204 |
205 | rawEmotions
206 | .split("\n")
207 | .forEach((s) => s.split(",").forEach((sp) => splitted.push(sp.trim())));
208 |
209 | return splitted;
210 | }
211 |
212 |
--------------------------------------------------------------------------------