Skip to content

Commit e96db5a

Browse files
committed
Link to other files and update readme
1 parent f7f7b61 commit e96db5a

File tree

4 files changed

+157
-97
lines changed

4 files changed

+157
-97
lines changed

README.md

Lines changed: 28 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,37 @@
1-
# Obsidian Sample Plugin
1+
# Obsidian Meta Bind Plugin
2+
This plugin can create input fields inside your notes and bind them to metadata fields.
23

3-
This is a sample plugin for Obsidian (https://obsidian.md).
4+
### How to use
5+
To create an input field you have to write an inline code block starting with `INPUT`. Then in square brackets the type of input field and what metadata field to bind to.
46

5-
This project uses Typescript to provide type checking and documentation.
6-
The repo depends on the latest plugin API (obsidian.d.ts) in Typescript Definition format, which contains TSDoc comments describing what it does.
7+
Examples:
8+
- `INPUT[toggle]` will create an unbound toggle
9+
- `INPUT[slider:rating]` will create a slider bound to the metadata field `rating` of this note
10+
- `INPUT[text:taks#completedOn]` will create a text input bound to the metadata field `completedOn` of the note with the name `task`
711

8-
**Note:** The Obsidian API is still in early alpha and is subject to change at any time!
12+
Be aware that the plugin might do unwanted things when you have multiple files with the same name in your vault.
913

10-
This sample plugin demonstrates some of the basic functionality the plugin API can do.
11-
- Changes the default font color to red using `styles.css`.
12-
- Adds a ribbon icon, which shows a Notice when clicked.
13-
- Adds a command "Open Sample Modal" which opens a Modal.
14-
- Adds a plugin setting tab to the settings page.
15-
- Registers a global click event and output 'click' to the console.
16-
- Registers a global interval which logs 'setInterval' to the console.
14+
### How to install
15+
Currently, there is no release. When there is one, follow the steps below.
1716

18-
## First time developing plugins?
17+
You must manually download the zip archive from the latest release here on GitHub.
18+
After downloading, extract the archive into the `.obsidian/plugins` folder in your vault.
1919

20-
Quick starting guide for new plugin devs:
20+
The folder structure should look like this:
21+
```
22+
[path to your vault]
23+
|_ .obsidian
24+
|_ plugins
25+
|_ obsidian-meta-bind-plugin
26+
|_ main.js
27+
|_ manifest.json
28+
|_ styles.css
29+
```
2130

22-
- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with.
23-
- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it).
24-
- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder.
25-
- Install NodeJS, then run `npm i` in the command line under your repo folder.
26-
- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`.
27-
- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`.
28-
- Reload Obsidian to load the new version of your plugin.
29-
- Enable plugin in settings window.
30-
- For updates to the Obsidian API run `npm update` in the command line under your repo folder.
31+
### Problems, unexpected behavior or improvement suggestions?
32+
You are more than welcome to open an issue on [GitHub](https://github.com/mProjectsCode/obsidian-meta-bind-plugin/issues).
3133

32-
## Releasing new releases
34+
### Contributions
35+
Thank you for wanting to contribute to this project.
3336

34-
- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release.
35-
- 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.
36-
- 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
37-
- 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.
38-
- Publish the release.
39-
40-
> You can simplify the version bump process by running `npm version patch`, `npm version minor` or `npm version major` after updating `minAppVersion` manually in `manifest.json`.
41-
> The command will bump version in `manifest.json` and `package.json`, and add the entry for the new version to `versions.json`
42-
43-
## Adding your plugin to the community plugin list
44-
45-
- Check https://github.com/obsidianmd/obsidian-releases/blob/master/plugin-review.md
46-
- Publish an initial version.
47-
- Make sure you have a `README.md` file in the root of your repo.
48-
- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin.
49-
50-
## How to use
51-
52-
- Clone this repo.
53-
- `npm i` or `yarn` to install dependencies
54-
- `npm run dev` to start compilation in watch mode.
55-
56-
## Manually installing the plugin
57-
58-
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`.
59-
60-
## Improve code quality with eslint (optional)
61-
- [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.
62-
- To use eslint with this project, make sure to install eslint from terminal:
63-
- `npm install -g eslint`
64-
- To use eslint to analyze this project use this command:
65-
- `eslint main.ts`
66-
- eslint will then create a report with suggestions for code improvement by file and line number.
67-
- 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:
68-
- `eslint .\src\`
69-
70-
71-
## API Documentation
72-
73-
See https://github.com/obsidianmd/obsidian-api
37+
Contributions are always welcome. If you have an idea, feel free to open a feature request under the issue tab or even create a pull request.

src/InputField.ts

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,94 @@
1-
import {MarkdownRenderChild, SliderComponent, TextComponent, ToggleComponent} from 'obsidian';
1+
import {MarkdownRenderChild, SliderComponent, TextComponent, TFile, ToggleComponent} from 'obsidian';
22
import MetaBindPlugin from './main';
33

44
export class InputField extends MarkdownRenderChild {
5-
text: string;
6-
fieldName: string;
7-
type: string;
85
plugin: MetaBindPlugin;
96
metaData: any;
10-
filePath: string;
7+
error: string;
118

12-
constructor(containerEl: HTMLElement, text: string, plugin: MetaBindPlugin, metaData: any, filePath: string) {
9+
declaration: string;
10+
inputFieldType: string;
11+
isBound: boolean;
12+
boundMetadataField: string;
13+
file: TFile;
14+
15+
constructor(containerEl: HTMLElement, fullDeclaration: string, plugin: MetaBindPlugin, filePath: string) {
1316
super(containerEl);
1417

18+
//console.log(this, 2)
19+
20+
this.error = '';
21+
1522
const regExp = new RegExp(/\[.*?\]/);
16-
let a = regExp.exec(text)[0];
17-
a = a.replace('[', '').replace(']', '');
23+
let declaration = regExp.exec(fullDeclaration)[0];
24+
declaration = declaration.replace('[', '').replace(']', '');
1825

19-
let aParts = a.split(':');
26+
let declarationParts: string[] = declaration.split(':');
27+
let boundTo: string = declarationParts[1] ?? '';
28+
this.isBound = !!boundTo;
29+
if (this.isBound) {
30+
let boundToParts = boundTo.split('#');
31+
if (boundToParts.length === 1) { // same file
32+
this.boundMetadataField = boundTo;
33+
this.file = plugin.getFileByName(filePath);
34+
} else if (boundToParts.length === 2) {
35+
this.boundMetadataField = boundToParts[1];
36+
this.file = plugin.getFileByName(boundToParts[0]);
37+
} else {
38+
this.error = 'invalid binding';
39+
}
2040

21-
this.text = text;
22-
this.type = aParts[0].toLowerCase();
23-
this.fieldName = aParts[1] ?? '';
41+
this.metaData = plugin.getMetaDataForFile(this.file);
42+
}
43+
44+
this.declaration = fullDeclaration;
45+
this.inputFieldType = declarationParts[0].toLowerCase();
2446
this.plugin = plugin;
25-
this.metaData = metaData;
26-
this.filePath = filePath;
47+
48+
// console.log(this, 3)
49+
}
50+
51+
async updateMetaData(value: any) {
52+
if (this.isBound) {
53+
await this.plugin.updateMetaData(this.boundMetadataField, value, this.file);
54+
}
55+
}
56+
57+
getInitialValue() {
58+
if (this.isBound) {
59+
return this.metaData[this.boundMetadataField];
60+
}
2761
}
2862

2963
onload() {
30-
console.log(this);
64+
//console.log(this, 1)
3165

3266
const container = this.containerEl.createDiv();
3367
container.addClass('meta-bind-plugin-input-wrapper');
3468

35-
if (this.type === 'toggle') {
69+
if (this.error) {
70+
container.innerText = ` \`Error ${this.error}\``;
71+
this.containerEl.replaceWith(container);
72+
return;
73+
}
74+
75+
if (this.inputFieldType === 'toggle') {
3676
const newEl = new ToggleComponent(container);
37-
newEl.setValue(this.metaData[this.fieldName]);
77+
newEl.setValue(this.getInitialValue());
3878
newEl.onChange(async (value) => {
39-
await this.plugin.updateMetaData(this.fieldName, value, this.filePath);
79+
await this.updateMetaData(value);
4080
});
41-
} else if (this.type === 'slider') {
81+
} else if (this.inputFieldType === 'slider') {
4282
const newEl = new SliderComponent(container);
43-
newEl.setValue(this.metaData[this.fieldName]);
83+
newEl.setValue(this.getInitialValue());
4484
newEl.onChange(async (value) => {
45-
await this.plugin.updateMetaData(this.fieldName, value, this.filePath);
85+
await this.updateMetaData(value);
4686
});
47-
} else if (this.type === 'text') {
87+
} else if (this.inputFieldType === 'text') {
4888
const newEl = new TextComponent(container);
49-
newEl.setValue(this.metaData[this.fieldName]);
89+
newEl.setValue(this.getInitialValue());
5090
newEl.onChange(async (value) => {
51-
await this.plugin.updateMetaData(this.fieldName, value, this.filePath);
91+
await this.updateMetaData(value);
5292
});
5393
}
5494

src/Utils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export function getFileName(path: string) {
2+
return path.split('/').at(-1);
3+
}
4+
5+
export function removeFileEnding(fileName: string) {
6+
const fileNameParts = fileName.split('.');
7+
if (fileNameParts.length === 1) {
8+
return fileName;
9+
} else {
10+
let newFileName = fileNameParts[0];
11+
for (let i = 1; i < fileNameParts.length - 1; i++) {
12+
newFileName += '.' + fileNameParts[i];
13+
}
14+
return newFileName;
15+
}
16+
}

src/main.ts

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import {FrontMatterCache, Plugin, stringifyYaml, TFile} from 'obsidian';
1+
import {Notice, Plugin, stringifyYaml, TFile} from 'obsidian';
22
import {DEFAULT_SETTINGS, MetaBindPluginSettings, MetaBindSettingTab} from './settings/Settings';
33
import {InputField} from './InputField';
4+
import {getFileName, removeFileEnding} from './Utils';
45

56
export default class MetaBindPlugin extends Plugin {
67
settings: MetaBindPluginSettings;
@@ -12,8 +13,6 @@ export default class MetaBindPlugin extends Plugin {
1213
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
1314

1415
});
15-
// Perform additional things with the ribbon
16-
ribbonIconEl.addClass('my-plugin-ribbon-class');
1716

1817

1918
this.registerMarkdownPostProcessor((element, context) => {
@@ -22,13 +21,16 @@ export default class MetaBindPlugin extends Plugin {
2221
const codeblock = codeblocks.item(index);
2322
const text = codeblock.innerText;
2423
const isEmoji = text.startsWith('INPUT[') && text.endsWith(']');
25-
console.log(context.sourcePath);
24+
// console.log(context.sourcePath);
2625
if (isEmoji) {
27-
context.addChild(new InputField(codeblock, text, this, context.frontmatter, context.sourcePath));
26+
context.addChild(new InputField(codeblock, text, this, context.sourcePath));
2827
}
2928
}
3029
});
3130

31+
this.registerEvent(this.app.vault.on('modify', () => {
32+
// console.log('file modified')
33+
}));
3234

3335
this.addSettingTab(new MetaBindSettingTab(this.app, this));
3436
}
@@ -37,20 +39,58 @@ export default class MetaBindPlugin extends Plugin {
3739

3840
}
3941

40-
async updateMetaData(key: string, value: any, filePath: string) {
41-
const file = await this.app.vault.getAbstractFileByPath(filePath) as TFile;
42+
async updateMetaData(key: string, value: any, file: TFile) {
43+
if (!file) {
44+
console.log('file not found');
45+
return;
46+
}
47+
4248
let fileContent: string = await this.app.vault.read(file);
4349
const regExp = new RegExp('^(---)\\n[\\s\\S]*\\n---');
4450
fileContent = fileContent.replace(regExp, '');
4551

46-
let metadata: FrontMatterCache = this.app.metadataCache.getFileCache(file).frontmatter;
47-
delete metadata.position;
52+
let metadata: any = this.getMetaDataForFile(file);
53+
if (!metadata) {
54+
return;
55+
}
56+
4857
metadata[key] = value;
4958

5059
fileContent = `---\n${stringifyYaml(metadata)}---` + fileContent;
5160
await this.app.vault.modify(file, fileContent);
5261
}
5362

63+
getFileByName(name: string): TFile {
64+
// console.log(getFileName(removeFileEnding(name)))
65+
const files = this.app.vault.getFiles();
66+
for (const file of files) {
67+
// console.log(getFileName(removeFileEnding(file.name)));
68+
if (getFileName(removeFileEnding(file.name)) === getFileName(removeFileEnding(name))) {
69+
return file;
70+
}
71+
}
72+
73+
return null;
74+
}
75+
76+
getMetaDataForFile(file: TFile): any {
77+
let metadata: any;
78+
try {
79+
metadata = this.app.metadataCache.getFileCache(file).frontmatter;
80+
} catch (e) {
81+
new Notice('Waring: ' + e.toString());
82+
return;
83+
}
84+
85+
if (metadata) {
86+
delete metadata.position;
87+
} else {
88+
metadata = {};
89+
}
90+
91+
return metadata;
92+
}
93+
5494
async loadSettings() {
5595
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
5696
}

0 commit comments

Comments
 (0)