diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..624f403
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+github: ['phibr0']
+custom: ['https://www.buymeacoffee.com/phibr0']
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..11158f4
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,101 @@
+name: Release Obsidian Plugin
+on:
+ push:
+ # Sequence of patterns matched against refs/tags
+ tags:
+ - '*' # Push events to matching any tag format, i.e. 1.0, 20.15.10
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0 # otherwise, you will failed to push refs to dest repo
+ - name: Use Node.js
+ uses: actions/setup-node@v1
+ with:
+ node-version: '14.x' # You might need to adjust this value to your own version
+ # Get the version number and put it in a variable
+ - name: Get Version
+ id: version
+ run: |
+ echo "::set-output name=tag::$(git describe --abbrev=0)"
+ # Build the plugin
+ - name: Build
+ id: build
+ run: |
+ npm install
+ npm run build --if-present
+ # Package the required files into a zip
+ - name: Package
+ run: |
+ mkdir ${{ github.event.repository.name }}
+ cp main.js manifest.json styles.css README.md ${{ github.event.repository.name }}
+ zip -r ${{ github.event.repository.name }}.zip ${{ github.event.repository.name }}
+ # Create the release on github
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ VERSION: ${{ github.ref }}
+ with:
+ tag_name: ${{ github.ref }}
+ release_name: ${{ github.ref }}
+ draft: false
+ prerelease: false
+ # Upload the packaged release file
+ - name: Upload zip file
+ id: upload-zip
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./${{ github.event.repository.name }}.zip
+ asset_name: ${{ github.event.repository.name }}.zip
+ asset_content_type: application/zip
+ # Upload the main.js
+ - name: Upload main.js
+ id: upload-main
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./main.js
+ asset_name: main.js
+ asset_content_type: text/javascript
+ # Upload the manifest.json
+ - name: Upload manifest.json
+ id: upload-manifest
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./manifest.json
+ asset_name: manifest.json
+ asset_content_type: application/json
+ # Upload the style.css
+ - name: Upload styles.css
+ id: upload-css
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./styles.css
+ asset_name: styles.css
+ asset_content_type: text/css
+ - name: Upload offline dictionary.json
+ id: upload-dic
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./dictionary.json
+ asset_name: dictionary.json
+ asset_content_type: application/json
+ # TODO: release notes???
diff --git a/README.md b/README.md
index 9e4fe9c..b514bcf 100644
--- a/README.md
+++ b/README.md
@@ -1,57 +1,16 @@
-## Obsidian Sample Plugin
+# Obsidian Macros [![GitHub tag (Latest by date)](https://img.shields.io/github/v/tag/phibr0/obsidian-macros)](https://github.com/phibr0/obsidian-macros/releases) ![GitHub all releases](https://img.shields.io/github/downloads/phibr0/obsidian-macros/total)
-This is a sample plugin for Obsidian (https://obsidian.md).
+This plugin allows you to group as many commands as you wish together into one command (Macro). You can also specify an icon and set a delay between the commands.
-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.
+## How to install
-**Note:** The Obsidian API is still in early alpha and is subject to change at any time!
+1. Go to **Community Plugins** in your [Obsidian](https://www.obsidian.md) settings and **disable** Safe Mode
+2. Click on **Browse** and search for „Macros“
+3. Click install
+4. Toggle the plugin **on** in the **Community Plugins** tab
-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.
+## Support me
-### First time developing plugins?
+If you find this plugin helpful, consider supporting me:
-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.
-- 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/`.
-
-### API Documentation
-
-See https://github.com/obsidianmd/obsidian-api
+
diff --git a/main.ts b/main.ts
deleted file mode 100644
index eeb3dda..0000000
--- a/main.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-import { App, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
-
-interface MyPluginSettings {
- mySetting: string;
-}
-
-const DEFAULT_SETTINGS: MyPluginSettings = {
- mySetting: 'default'
-}
-
-export default class MyPlugin extends Plugin {
- settings: MyPluginSettings;
-
- async onload() {
- console.log('loading plugin');
-
- await this.loadSettings();
-
- this.addRibbonIcon('dice', 'Sample Plugin', () => {
- new Notice('This is a notice!');
- });
-
- this.addStatusBarItem().setText('Status Bar Text');
-
- this.addCommand({
- id: 'open-sample-modal',
- name: 'Open Sample Modal',
- // callback: () => {
- // console.log('Simple Callback');
- // },
- checkCallback: (checking: boolean) => {
- let leaf = this.app.workspace.activeLeaf;
- if (leaf) {
- if (!checking) {
- new SampleModal(this.app).open();
- }
- return true;
- }
- return false;
- }
- });
-
- this.addSettingTab(new SampleSettingTab(this.app, this));
-
- this.registerCodeMirror((cm: CodeMirror.Editor) => {
- console.log('codemirror', cm);
- });
-
- this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
- console.log('click', evt);
- });
-
- this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
- }
-
- onunload() {
- console.log('unloading plugin');
- }
-
- 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() {
- let {contentEl} = this;
- contentEl.setText('Woah!');
- }
-
- onClose() {
- let {contentEl} = this;
- contentEl.empty();
- }
-}
-
-class SampleSettingTab extends PluginSettingTab {
- plugin: MyPlugin;
-
- constructor(app: App, plugin: MyPlugin) {
- super(app, plugin);
- this.plugin = plugin;
- }
-
- display(): void {
- let {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('')
- .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 4ca4889..289d17f 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,10 +1,10 @@
{
- "id": "obsidian-sample-plugin",
- "name": "Sample Plugin",
- "version": "1.0.1",
- "minAppVersion": "0.9.12",
- "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/about",
+ "id": "macro-plugin",
+ "name": "Macros",
+ "version": "0.0.1",
+ "minAppVersion": "0.12.11",
+ "description": "Group multiple Commands into one Macro.",
+ "author": "phibr0",
+ "authorUrl": "https://github.com/phibr0/",
"isDesktopOnly": false
}
diff --git a/package.json b/package.json
index 29e2406..6543fb0 100644
--- a/package.json
+++ b/package.json
@@ -14,10 +14,14 @@
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-typescript": "^8.2.1",
+ "@types/feather-icons": "^4.7.0",
"@types/node": "^14.14.37",
"obsidian": "^0.12.0",
"rollup": "^2.32.1",
"tslib": "^2.2.0",
"typescript": "^4.2.4"
+ },
+ "dependencies": {
+ "feather-icons": "^4.28.0"
}
}
diff --git a/rollup.config.js b/rollup.config.js
index dd4d041..8e28e8d 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -12,7 +12,7 @@ if you want to view the source visit the plugins github repository
`;
export default {
- input: 'main.ts',
+ input: 'src/main.ts',
output: {
dir: '.',
sourcemap: 'inline',
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..429e057
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,365 @@
+import { IconPicker } from './ui/iconPicker';
+import { App, ButtonComponent, Command, Modal, Notice, Platform, Plugin, PluginSettingTab, Setting } from 'obsidian';
+import { addFeatherIcons } from './ui/icons';
+import CommandSuggester from './ui/commandSuggester';
+
+interface Macro {
+ commandID: string;
+ mobileOnly: boolean;
+ icon: string;
+ delay: number;
+ name: string;
+ commands: string[];
+ command?: Command;
+}
+
+interface MacroSettings {
+ macros: Macro[];
+}
+
+const DEFAULT_SETTINGS: MacroSettings = {
+ macros: [],
+}
+
+export default class MacroPlugin extends Plugin {
+ settings: MacroSettings;
+ iconList: string[] = ["any-key", "audio-file", "blocks", "bold-glyph", "bracket-glyph", "broken-link", "bullet-list", "bullet-list-glyph", "calendar-with-checkmark", "check-in-circle", "check-small", "checkbox-glyph", "checkmark", "clock", "cloud", "code-glyph", "create-new", "cross", "cross-in-box", "crossed-star", "csv", "deleteColumn", "deleteRow", "dice", "document", "documents", "dot-network", "double-down-arrow-glyph", "double-up-arrow-glyph", "down-arrow-with-tail", "down-chevron-glyph", "enter", "exit-fullscreen", "expand-vertically", "filled-pin", "folder", "formula", "forward-arrow", "fullscreen", "gear", "go-to-file", "hashtag", "heading-glyph", "help", "highlight-glyph", "horizontal-split", "image-file", "image-glyph", "indent-glyph", "info", "insertColumn", "insertRow", "install", "italic-glyph", "keyboard-glyph", "languages", "left-arrow", "left-arrow-with-tail", "left-chevron-glyph", "lines-of-text", "link", "link-glyph", "logo-crystal", "magnifying-glass", "microphone", "microphone-filled", "minus-with-circle", "moveColumnLeft", "moveColumnRight", "moveRowDown", "moveRowUp", "note-glyph", "number-list-glyph", "open-vault", "pane-layout", "paper-plane", "paused", "pdf-file", "pencil", "percent-sign-glyph", "pin", "plus-with-circle", "popup-open", "presentation", "price-tag-glyph", "quote-glyph", "redo-glyph", "reset", "right-arrow", "right-arrow-with-tail", "right-chevron-glyph", "right-triangle", "run-command", "search", "sheets-in-box", "sortAsc", "sortDesc", "spreadsheet", "stacked-levels", "star", "star-list", "strikethrough-glyph", "switch", "sync", "sync-small", "tag-glyph", "three-horizontal-bars", "trash", "undo-glyph", "unindent-glyph", "up-and-down-arrows", "up-arrow-with-tail", "up-chevron-glyph", "uppercase-lowercase-a", "vault", "vertical-split", "vertical-three-dots", "wrench-screwdriver-glyph"];
+
+ async onload() {
+ console.log('loading plugin');
+
+ await this.loadSettings();
+
+ addFeatherIcons(this.iconList);
+
+ this.settings.macros.forEach(macro => {
+ const command: Command = {
+ id: macro.commandID,
+ name: macro.name,
+ callback: async () => {
+ for (let i = 0; i < macro.commands.length; i++) {
+ //@ts-ignore
+ this.app.commands.executeCommandById(macro.commands[i]);
+ await wait(macro.delay);
+ }
+ },
+ icon: macro.icon,
+ }
+ this.addCommand(command);
+ });
+
+ this.addSettingTab(new MacroSettingsTab(this));
+ }
+
+ onunload() {
+ console.log('unloading plugin');
+ }
+
+ async loadSettings() {
+ this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
+ }
+
+ async saveSettings() {
+ await this.saveData(this.settings);
+ }
+}
+
+class MacroSettingsTab extends PluginSettingTab {
+ plugin: MacroPlugin;
+
+ constructor(plugin: MacroPlugin) {
+ super(plugin.app, plugin);
+ this.plugin = plugin;
+ addEventListener("M-macroAdded", async (e: CustomEvent) => {
+ if (e.detail.wasEdited) {
+ console.log("")
+ const ids: string[] = [];
+ e.detail.commands.forEach((element: string) => {
+ ids.push(element);
+ });
+ let m = this.plugin.settings.macros.find((m) => m.name === e.detail.name.replace(/Macro Plugin: /g, ""));
+ console.log(m);
+ m = {
+ icon: e.detail.icon,
+ mobileOnly: e.detail.mobileOnly,
+ commandID: e.detail.command.id,
+ commands: ids,
+ delay: e.detail.delay,
+ name: e.detail.name,
+ };
+ await this.plugin.saveSettings();
+ this.display();
+ } else {
+ const ids: string[] = [];
+ e.detail.commands.forEach((element: string) => {
+ ids.push(element);
+ });
+ this.plugin.settings.macros.push({
+ mobileOnly: e.detail.mobileOnly,
+ icon: e.detail.icon,
+ commandID: e.detail.command.id,
+ commands: ids,
+ delay: e.detail.delay,
+ name: e.detail.name,
+ });
+ await this.plugin.saveSettings();
+ this.display();
+ }
+ });
+ }
+
+
+ display(): void {
+ let { containerEl } = this;
+
+ containerEl.empty();
+
+ containerEl.createEl('h2', { text: 'Macro Plugin Settings' });
+
+ new Setting(containerEl)
+ .setName("Add new Macro")
+ .setDesc("Create a new Group of Commands to execute one after another.")
+ .addButton(cb => {
+ cb.setButtonText("+")
+ .onClick(() => {
+ new MacroCreatorModal(this.plugin).open();
+ })
+ })
+
+ this.plugin.settings.macros.forEach(macro => {
+ let dsc = "";
+ macro.commands.forEach((c, i) => {
+ //@ts-ignore
+ dsc += this.app.commands.commands[c].name;
+ if (i != macro.commands.length - 1) {
+ dsc += ", "
+ } else {
+ dsc += " | "
+ }
+ });
+ dsc += `(Delay: ${macro.delay})`
+ new Setting(containerEl)
+ .setName(macro.name)
+ .setDesc(dsc)
+ .addButton(bt => {
+ bt.setButtonText("Edit");
+ bt.onClick(() => {
+ new MacroCreatorModal(this.plugin, macro).open();
+ });
+ })
+ .addExtraButton(bt => {
+ bt.setIcon("trash");
+ bt.onClick(async () => {
+ this.plugin.settings.macros.remove(macro);
+ this.display();
+ new Notice("You will need to restart Obsidian to fully remove the Macro.")
+ await this.plugin.saveSettings();
+ })
+ });
+ });
+
+ new Setting(containerEl)
+ .setName('Donate')
+ .setDesc('If you like this Plugin, consider donating to support continued development:')
+ .setClass("AT-extra")
+ .addButton((bt) => {
+ bt.buttonEl.outerHTML = ``;
+ });
+ }
+}
+
+class MacroCreatorModal extends Modal {
+ plugin: MacroPlugin;
+ command: any;
+ delay: number; //in ms
+ name: string;
+ coms: string[] = [];
+ mobileOnly: boolean;
+ editing: boolean;
+
+ constructor(plugin: MacroPlugin, macro?: Macro) {
+ super(plugin.app);
+ this.plugin = plugin;
+ this.command = macro?.command ?? {};
+ this.delay = macro?.delay ?? 10;
+ this.name = macro?.name;
+ this.coms = macro?.commands ?? [];
+ this.mobileOnly = macro?.mobileOnly ?? false;
+ if (macro) {
+ this.editing = true;
+ } else {
+ this.editing = false;
+ }
+ addEventListener("M-iconPicked", (e: CustomEvent) => {
+ this.command.icon = e.detail.icon;
+ this.display();
+ });
+ addEventListener("M-commandAdded", (e: CustomEvent) => {
+ this.coms.push(e.detail.command);
+ this.display();
+ });
+ }
+
+ onOpen() {
+ super.onOpen();
+ this.display();
+ }
+
+ display() {
+ const { contentEl: el } = this;
+ const command = this.command as Command;
+ el.empty();
+ this.titleEl.setText("Add a new Macro")
+
+ new Setting(el)
+ .setName("Name")
+ .setDesc("Specify the Name of your brand new Macro.")
+ .addText(cb => {
+ cb.setValue(command?.name?.replace(/Macro Plugin: /g, "") ?? "")
+ .setPlaceholder("Super duper Macro")
+ .setValue(this.name)
+ .setDisabled(this.editing)
+ .onChange(value => {
+ this.name = value.trim().replace(/Macro Plugin: /g, "");
+ command.name = value.trim().replace(/Macro Plugin: /g, "");
+ command.id = value.trim().replace(/Macro Plugin: /g, "").replace(" ", "-").toLowerCase();
+ });
+ });
+
+ new Setting(el)
+ .setName("Icon")
+ .setDesc("Pick an Icon for your Macro.")
+ .addButton(bt => {
+ bt.setDisabled(this.editing);
+ if (command.icon) {
+ bt.setIcon(command.icon);
+ } else {
+ bt.setButtonText("Pick Icon");
+ }
+ bt.onClick(() => {
+ new IconPicker(this.plugin).open();
+ });
+ });
+
+ if (Platform.isMobile) {
+ new Setting(el)
+ .setName("Mobile Only?")
+ .setDesc("Is this Macro Mobile only?")
+ .addToggle(cb => {
+ cb.setDisabled(this.editing);
+ cb.setValue(command.mobileOnly)
+ cb.onChange((value) => {
+ this.command.mobileOnly = value;
+ this.mobileOnly = value;
+ })
+ });
+ }
+
+ new Setting(el)
+ .setName("Delay")
+ .setDesc("Specify a Delay between every Command.")
+ .addSlider(cb => {
+ cb.setDisabled(this.editing);
+ cb.setDynamicTooltip()
+ .setLimits(10, 2000, 10)
+ .setValue(this.delay)
+ .onChange(value => {
+ this.delay = value;
+ });
+ });
+
+ new Setting(el)
+ .setName("Add Command")
+ .setDesc("Add a Command to your Macro.")
+ .addButton(cb => {
+ cb.setButtonText("+")
+ .onClick(() => {
+ new CommandSuggester(this.plugin).open()
+ })
+ });
+
+ const commandsEl = el.createDiv({ cls: "M-commands" })
+ this.coms.forEach(c => {
+ new Setting(commandsEl)
+ //@ts-ignore
+ .setName(this.app.commands.commands[c].name)
+ .addButton(cb => {
+ cb.setIcon("trash")
+ .onClick(() => {
+ this.coms.remove(c);
+ this.display();
+ })
+ });
+ });
+
+ const btnDiv = el.createDiv({ cls: "M-flex-center" })
+ if (this.editing) {
+ const btn = createEl("button", { text: "Finish" })
+ btnDiv.appendChild(btn);
+ btn.addEventListener("click", () => {
+ const c = this.command as Command;
+ c.callback = async () => {
+ for (let i = 0; i < this.coms.length; i++) {
+ //@ts-ignore
+ this.plugin.app.commands.executeCommandById(this.coms[i]);
+ await wait(this.delay);
+ }
+ }
+ dispatchEvent(new CustomEvent("M-macroAdded", {
+ detail: {
+ icon: this.command.icon,
+ mobileOnly: this.mobileOnly,
+ command: c,
+ delay: this.delay,
+ commands: this.coms,
+ name: this.name,
+ wasEdited: this.editing,
+ }
+ }));
+ this.close();
+ });
+ } else {
+ if (this.coms.length >= 2 && command.name && command.icon) {
+ const cbtn = createEl("button", { text: "Create Macro" })
+ btnDiv.appendChild(cbtn);
+ cbtn.addEventListener("click", () => {
+ this.addCommand();
+ });
+ }
+ const btn = createEl("button", { text: "Cancel" })
+ btnDiv.appendChild(btn);
+ btn.addEventListener("click", () => {
+ this.close();
+ });
+ }
+ }
+
+ addCommand() {
+ const c = this.command as Command;
+ c.callback = async () => {
+ for (let i = 0; i < this.coms.length; i++) {
+ //@ts-ignore
+ this.plugin.app.commands.executeCommandById(this.coms[i]);
+ await wait(this.delay);
+ }
+ }
+ this.plugin.addCommand(c);
+ dispatchEvent(new CustomEvent("M-macroAdded", {
+ detail: {
+ icon: this.command.icon,
+ mobileOnly: this.mobileOnly,
+ command: c,
+ delay: this.delay,
+ commands: this.coms,
+ name: this.name,
+ wasEdited: this.editing,
+ }
+ }));
+ this.close();
+ }
+
+}
+
+async function wait(delay: number) {
+ return new Promise(resolve => setTimeout(resolve, delay));
+}
\ No newline at end of file
diff --git a/src/ui/commandSuggester.ts b/src/ui/commandSuggester.ts
new file mode 100644
index 0000000..49c8f53
--- /dev/null
+++ b/src/ui/commandSuggester.ts
@@ -0,0 +1,26 @@
+import { FuzzySuggestModal, Command } from "obsidian";
+import CustomSidebarPlugin from "src/main";
+
+export default class CommandSuggester extends FuzzySuggestModal {
+
+ constructor(private plugin: CustomSidebarPlugin) {
+ super(plugin.app);
+ }
+
+ getItems(): Command[] {
+ //@ts-ignore
+ return this.app.commands.listCommands();
+ }
+
+ getItemText(item: Command): string {
+ return item.name;
+ }
+
+ async onChooseItem(item: Command, evt: MouseEvent | KeyboardEvent): Promise {
+ this.close();
+ setTimeout(() => {
+ dispatchEvent(new CustomEvent("M-commandAdded", {detail: {command: item.id}}));
+ }, 100);
+ }
+
+}
\ No newline at end of file
diff --git a/src/ui/iconPicker.ts b/src/ui/iconPicker.ts
new file mode 100644
index 0000000..d54fdc0
--- /dev/null
+++ b/src/ui/iconPicker.ts
@@ -0,0 +1,44 @@
+import { Command, FuzzyMatch, FuzzySuggestModal, setIcon } from "obsidian";
+import MacroPlugin from "src/main";
+
+export class IconPicker extends FuzzySuggestModal{
+ plugin: MacroPlugin;
+
+ constructor(plugin: MacroPlugin) {
+ super(plugin.app);
+ this.plugin = plugin;
+ this.setPlaceholder("Pick an Icon");
+ }
+
+ private cap(string: string): string {
+ const words = string.split(" ");
+
+ return words.map((word) => {
+ return word[0].toUpperCase() + word.substring(1);
+ }).join(" ");
+ }
+
+ getItems(): string[] {
+ return this.plugin.iconList;
+ }
+
+ getItemText(item: string): string {
+ return this.cap(item.replace("feather-", "").replace(/-/ig, " "));
+ }
+
+ renderSuggestion(item: FuzzyMatch, el: HTMLElement): void {
+ el.addClass("M-icon-container");
+ const div = createDiv({ cls: "M-icon" });
+ el.appendChild(div);
+ setIcon(div, item.item);
+ super.renderSuggestion(item, el);
+ }
+
+ async onChooseItem(item: string): Promise {
+ this.close();
+ setTimeout(() => {
+ dispatchEvent(new CustomEvent("M-iconPicked", {detail: {icon: item}}));
+ }, 100);
+ }
+
+}
diff --git a/src/ui/icons.ts b/src/ui/icons.ts
new file mode 100644
index 0000000..e424586
--- /dev/null
+++ b/src/ui/icons.ts
@@ -0,0 +1,11 @@
+import * as feather from "feather-icons";
+import { addIcon } from "obsidian";
+
+export function addFeatherIcons(iconList: string[]) {
+ Object.values(feather.icons).forEach((i) => {
+ const svg = i.toSvg({viewBox: "0 0 24 24", width: "100", height: "100"});
+ //Remove the svg tag: svg.match(/(?<=>).*(?=<\/svg>)/).first()
+ addIcon("feather-" + i.name, svg);
+ iconList.push("feather-" + i.name);
+ });
+}
\ No newline at end of file
diff --git a/styles.css b/styles.css
index cfd0fd7..4b6af6c 100644
--- a/styles.css
+++ b/styles.css
@@ -1,4 +1,28 @@
-/* Sets all the text color to red! */
-body {
- color: red;
+.M-icon {
+ transform: translateY(3px);
+ margin-right: 8px;
}
+
+.M-icon-container{
+ display: flex;
+}
+
+.M-commands {
+ background-color: var(--background-secondary);
+ margin: 1rem;
+ padding: 1rem 1rem 0px 1rem;
+ border-radius: 0.8rem;
+}
+
+.M-flex-center {
+ display: flex;
+ place-content: center;
+}
+
+a[href="https://www.buymeacoffee.com/phibr0"] > img {
+ height: 2.2em;
+}
+
+a[href="https://www.buymeacoffee.com/phibr0"]{
+ transform: translate(0, 5%);
+}
\ No newline at end of file