1
0
mirror of https://github.com/elisspace/ObsidianTweaks.git synced 2026-08-29 15:44:02 +00:00

Initial commit

This commit is contained in:
Jeppe Klitgaard
2021-05-31 23:08:39 +01:00
parent c59974523f
commit a447def8fb
14 changed files with 367 additions and 178 deletions

0
.gitignore vendored Normal file → Executable file
View File

24
LICENSE Executable file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

73
README.md Normal file → Executable file
View File

@@ -1,57 +1,44 @@
## Obsidian Sample Plugin
# ObsidianTweaks
This is a sample plugin for Obsidian (https://obsidian.md).
This is a small plugin that implements some features of Obsidian that I believe
should've been (perhaps in the future they will be? ❤️).
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.
## Features
**Note:** The Obsidian API is still in early alpha and is subject to change at any time!
Below is a list of the current functionality that has been implemented.
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.
Note that hotkeys cannot be 'disabled', but they can be set to <kbd>Blank</kbd>.
They are all set to blank by default.
### First time developing plugins?
### Hotkeys
| Recommended hotkey | Action |
| --- | --- |
| <kbd>Shift</kbd> + <kbd>Alt</kbd> + <kbd>↓</kbd> | Copy Current Line(s) Down |
| <kbd>Shift</kbd> + <kbd>Alt</kbd> + <kbd>↑</kbd> | Copy Current Line(s) Up |
| <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd> | Select Current Line(s) |
| <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>1</kbd> | Toggle Heading - H1 |
| <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>2</kbd> | Toggle Heading - H2 |
| <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>3</kbd> | Toggle Heading - H3 |
| <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>4</kbd> | Toggle Heading - H4 |
| <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>5</kbd> | Toggle Heading - H5 |
| <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>6</kbd> | Toggle Heading - H6 |
Quick starting guide for new plugin devs:
## Philosophy
- 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.
- By default no features are enabled. You can enable the features you want.
- Thus all features can also be disabled.
- All code is released into public domain without any requirement for attribution using the [Unlicense](https://unlicense.org/).
### Releasing new releases
## Why Unlicense?
- 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.
In case the developers of Obsidian want to just straight copy some code into Obsidian, they can do that without worrying about licensing at all.
### Adding your plugin to the community plugin list
**Note that any contributions to ObsidianTweaks will thus also be Unlicensed.**
- 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.
## Honorable Mentions
### How to use
I initally planned to contribute to the [Hotkeys++](https://github.com/argenos/hotkeysplus-obsidian) plugin, but since I don't necessarily want ObsidianTweaks to be limited to hotkeys in the future, I decided to make my own.
- Clone this repo.
- `npm i` or `yarn` to install dependencies
- `npm run dev` to start compilation in watch mode.
## Contributors
### 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
- [Jeppe Klitgaard](https://github.com/JeppeKlitgaard)

112
main.ts
View File

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

14
manifest.json Normal file → Executable file
View File

@@ -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": "obsidian-tweaks",
"name": "Obsidian Tweaks",
"version": "0.1.0",
"minAppVersion": "0.12.3",
"description": "A small plugin that implements some sorely missed features.",
"author": "Jeppe Klitgaard",
"authorUrl": "jeppe.science",
"isDesktopOnly": false
}

12
package.json Normal file → Executable file
View File

@@ -1,15 +1,15 @@
{
"name": "obsidian-sample-plugin",
"version": "0.12.0",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"name": "obsidian-tweaks",
"version": "0.1.0",
"description": "A small plugin that implements some sorely missed features.",
"main": "main.js",
"scripts": {
"dev": "rollup --config rollup.config.js -w",
"build": "rollup --config rollup.config.js --environment BUILD:production"
},
"keywords": [],
"author": "",
"license": "MIT",
"keywords": ["obsidian"],
"author": "Jeppe Klitgaard",
"license": "Unlicense",
"devDependencies": {
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",

4
rollup.config.js Normal file → Executable file
View File

@@ -4,7 +4,7 @@ import commonjs from '@rollup/plugin-commonjs';
const isProd = (process.env.BUILD === 'production');
const banner =
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
@@ -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',

65
src/DirectionalCopy.ts Executable file
View File

@@ -0,0 +1,65 @@
import { App, EditableFileView, Editor, MarkdownView } from "obsidian";
import ObsidianTweaksPlugin from "./main";
export enum Direction {
Up,
Down,
}
export class DirectionalCopy {
public app: App;
private plugin: ObsidianTweaksPlugin;
constructor(app: App, plugin: ObsidianTweaksPlugin) {
this.app = app;
this.plugin = plugin;
}
directionalCopy(direction: Direction): void {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView) {
return;
}
var anchor = activeView.editor.getCursor("from");
var head = activeView.editor.getCursor("to");
var headLength = activeView.editor.getLine(head.line).length;
var textToCopy = activeView.editor.getRange(
{line: anchor.line, ch: 0},
{line: head.line, ch: headLength}
)
// Copy up
if (direction === Direction.Up) {
activeView.editor.replaceRange(
textToCopy + '\n',
{line: anchor.line, ch: 0}
);
activeView.editor.setSelection(
{line: anchor.line, ch: anchor.ch},
{line: head.line, ch: head.ch},
);
}
// Copy down
else if (direction === Direction.Down) {
var addedLines = head.line - anchor.line;
console.log(addedLines);
activeView.editor.replaceRange(
'\n' + textToCopy,
{line: head.line, ch: headLength}
);
activeView.editor.setSelection(
{line: anchor.line + addedLines + 1, ch: anchor.ch},
{line: head.line + addedLines + 1, ch: head.ch},
);
}
else {
console.log("Something went wrong...");
}
return;
}
}

34
src/SelectLine.ts Executable file
View File

@@ -0,0 +1,34 @@
import { App, MarkdownView } from "obsidian";
import ObsidianTweaksPlugin from "./main";
export class SelectLine {
public app: App;
private plugin: ObsidianTweaksPlugin;
constructor(app: App, plugin: ObsidianTweaksPlugin) {
this.app = app;
this.plugin = plugin;
}
selectLine(): void {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView) {
return;
}
var anchor = activeView.editor.getCursor("from");
var head = activeView.editor.getCursor("to");
var headLength = activeView.editor.getLine(head.line).length;
// Modifying and passing the EditorPosition objects does not work
// reliably.
// Use this approach instead.
activeView.editor.setSelection(
{line: anchor.line, ch: 0},
{line: head.line, ch: headLength}
)
return;
}
}

69
src/ToggleHeading.ts Executable file
View File

@@ -0,0 +1,69 @@
import { App, EditableFileView, Editor, MarkdownView } from "obsidian";
import ObsidianTweaksPlugin from "./main";
export enum Heading {
H1 = 1,
H2,
H3,
H4,
H5,
H6,
}
export class ToggleHeading {
public app: App;
private plugin: ObsidianTweaksPlugin;
constructor(app: App, plugin: ObsidianTweaksPlugin) {
this.app = app;
this.plugin = plugin;
}
toggleHeading(heading: Heading): void {
// Three possibilities:
// 1: Currently no heading text. Set the heading.
// 2: Currently this heading text. Remove the heading.
// 3: Currently some other heading. Remove the heading and set this heading.
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView) {
return;
}
var headingText = "#".repeat(heading);
var anchor = activeView.editor.getCursor("from");
var lineText = activeView.editor.getLine(anchor.line);
var regex: RegExp = /^((#*) )?.*/;
var matches = regex.exec(lineText);
if (matches[2] !== undefined) {
// Some heading
if (matches[2] === headingText) {
// Option 2
activeView.editor.replaceRange(
'',
{line: anchor.line, ch: 0},
{line: anchor.line, ch: matches[2].length + 1}
)
return;
} else {
// Option 3
activeView.editor.replaceRange(
headingText + " ",
{line: anchor.line, ch: 0},
{line: anchor.line, ch: matches[2].length + 1}
)
return;
}
}
activeView.editor.replaceRange(
headingText + " ",
{line: anchor.line, ch: 0},
)
return;
}
}

127
src/main.ts Executable file
View File

@@ -0,0 +1,127 @@
import { App, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
import { SelectLine } from 'SelectLine';
import { DirectionalCopy, Direction } from 'DirectionalCopy';
import { ToggleHeading, Heading } from 'ToggleHeading';
interface ObsidianTweaksSettings {
}
const DEFAULT_SETTINGS: ObsidianTweaksSettings = {
}
export default class ObsidianTweaksPlugin extends Plugin {
public settings: ObsidianTweaksSettings;
private selectLine: SelectLine;
private directionalCopy: DirectionalCopy;
private toggleHeading: ToggleHeading;
async onload() {
await this.loadSettings();
this.selectLine = new SelectLine(this.app, this);
this.directionalCopy = new DirectionalCopy(this.app, this);
this.toggleHeading = new ToggleHeading(this.app, this);
// Select line
this.addCommand({
id: 'select-line',
name: 'Select Current Line(s)',
callback: () => {
this.selectLine.selectLine();
}
});
// Directional Copy
this.addCommand({
id: 'copy-line-up',
name: 'Copy Current Line(s) Up',
callback: () => {
this.directionalCopy.directionalCopy(Direction.Up);
}
});
this.addCommand({
id: 'copy-line-down',
name: 'Copy Current Line(s) Down',
callback: () => {
this.directionalCopy.directionalCopy(Direction.Down);
}
});
// Set heading
this.addCommand({
id: 'toggle-heading-1',
name: 'Toggle Heading - H1',
callback: () => {
this.toggleHeading.toggleHeading(Heading.H1);
}
});
this.addCommand({
id: 'toggle-heading-2',
name: 'Toggle Heading - H2',
callback: () => {
this.toggleHeading.toggleHeading(Heading.H2);
}
});
this.addCommand({
id: 'toggle-heading-3',
name: 'Toggle Heading - H3',
callback: () => {
this.toggleHeading.toggleHeading(Heading.H3);
}
});
this.addCommand({
id: 'toggle-heading-4',
name: 'Toggle Heading - H4',
callback: () => {
this.toggleHeading.toggleHeading(Heading.H4);
}
});
this.addCommand({
id: 'toggle-heading-5',
name: 'Toggle Heading - H5',
callback: () => {
this.toggleHeading.toggleHeading(Heading.H5);
}
});
this.addCommand({
id: 'toggle-heading-6',
name: 'Toggle Heading - H6',
callback: () => {
this.toggleHeading.toggleHeading(Heading.H6);
}
});
this.addSettingTab(new SampleSettingTab(this.app, this));
}
onunload() {
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class SampleSettingTab extends PluginSettingTab {
plugin: ObsidianTweaksPlugin;
constructor(app: App, plugin: ObsidianTweaksPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
let {containerEl} = this;
containerEl.empty();
containerEl.createEl('h2', {text: 'Obsidian Tweaks'});
containerEl.createEl('p', {text: 'Currently nothing here!'});
}
}

4
styles.css Normal file → Executable file
View File

@@ -1,4 +0,0 @@
/* Sets all the text color to red! */
body {
color: red;
}

4
tsconfig.json Normal file → Executable file
View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"baseUrl": ".",
"baseUrl": "src",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
@@ -17,6 +17,6 @@
]
},
"include": [
"**/*.ts"
"src/**/*.ts"
]
}

3
versions.json Normal file → Executable file
View File

@@ -1,4 +1,3 @@
{
"1.0.1": "0.9.12",
"1.0.0": "0.9.7"
"0.1.0": "0.12.3"
}