Custom Obsidian Plugin
Goal: Learning !
- Experiment with the
Custom Protocol
, to be more precise theregisterObsidianProtocolHandler
.- I’ve played with it in the past (for another obsidian plugin and it works great !)
- This article will cover a simple prototype that
- Receive a JSON Array (from anywhere… To be defined in fact …)
- Convert the JSON objects to flatten object
- Convert all Objects to a CSV format (with Header)
- Convert the CSV File to Markdown Table
- Save the content in a predefined page in obsidian
Note
I will only copy/paste the modified file in this article to avoid overloading it; Here is the official template I used : https://marcus.se.net/obsidian-plugin-docs/getting-started
The Code !
npm i --save @json2csv/plainjs@^6.1.2
npm i --save csv-to-markdown-table@^1.3.0
npm i --save-dev open
main.js
import {
App,
normalizePath,
Notice,
Plugin,
PluginSettingTab,
Setting,
} from "obsidian";
import { Parser } from "@json2csv/plainjs";
import csvToMarkdown from "csv-to-markdown-table";
interface Settings {
filename: string;
endpoint: string;
}
const DEFAULT_SETTINGS: Settings = {
filename: "default.md",
endpoint: "endpoint-local",
};
function flattenJSON(obj: any = {}, res: any = {}, extraKey: string = "") {
for (const key in obj) {
if (typeof obj[key] !== "object") {
res[extraKey + key] = obj[key];
} else {
flattenJSON(obj[key], res, `${extraKey}${key}_`);
}
}
return res;
}
export default class DataCollectorPlugin extends Plugin {
settings: Settings;
processJson(data: string, withHeader: boolean): string {
try {
const _data = JSON.parse(Buffer.from(data, "base64").toString("utf-8"));
const opts = {};
const parser = new Parser(opts);
const csv = parser.parse(_data.map((_d: Object) => flattenJSON(_d)));
return csvToMarkdown(csv, ",", withHeader);
} catch (err) {
console.error(err);
return "\n";
}
}
async onload() {
await this.loadSettings();
// Listen for local callback
this.registerObsidianProtocolHandler(
this.settings.endpoint,
async (params) => {
try {
console.debug("Received data on local endpoint");
console.debug(params);
const filename = normalizePath(`/${this.settings.filename}`);
const exists = await this.app.vault.adapter.exists(filename, true);
if (exists) {
const data = this.processJson(params.data, true);
const s = await this.app.vault.adapter.append(filename, data);
} else {
const data = this.processJson(params.data, true);
const w = await this.app.vault.create(filename, data);
}
} catch (e) {
new Notice(e.message);
}
}
);
// This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new SettingTab(this.app, this));
}
onunload() {}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class SettingTab extends PluginSettingTab {
plugin: DataCollectorPlugin;
constructor(app: App, plugin: DataCollectorPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl("h2", { text: "Settings" });
new Setting(containerEl)
.setName("filename")
.setDesc("Filename to store the json objects")
.addText((text) =>
text
.setPlaceholder("Enter your filename")
.setValue(this.plugin.settings.filename)
.onChange(async (value) => {
this.plugin.settings.filename = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("endpoint")
.setDesc("Endpoint to call")
.addText((text) =>
text
.setPlaceholder("Enter your endpoint")
.setValue(this.plugin.settings.endpoint)
.onChange(async (value) => {
this.plugin.settings.endpoint = value;
await this.plugin.saveSettings();
})
);
}
}
To test the plugin:
node test.js
test.js
const open = require("open");
(async () => {
const data = Buffer.from(
JSON.stringify([
{
foo: "bar",
one: {
plus: "two",
is: { three: true },
},
},
])
).toString("base64");
await open(`obsidian://endpoint-local/?data=${data}`);
})();
Will append this table in your file:
| "foo" | "one_plus" | "one_is_three" |
| ----- | ---------- | -------------- |
| "bar" | "two" | true |
I did go further, I was trying to figure out how to use NodeJS to send the data directly, but so far I didn’t found any way…
My goal was to centralize the data in obsidian for further processing and links, but at the end it doesn’t seem to be a good idea…
I’ve tried:
- axios
- node-fetch
- Net
- Https
All of them were not happy because of the unsupported protocol (obsidian://
)
Next Step(s)
Mozilla Firefox Extension
I’ve created one for another application, it is easy and quick to create something custom.
I did not fully tested the approach, but using window.open(url)
works…
- https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension
- https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests
- https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
- Didn’t work with custom protocol…
I’ll think about what I want to achieve, because I need puppeteer to parse and extract my data… So maybe this is simply the wrong way to proceeed…
Source
- https://marcus.se.net/obsidian-plugin-docs/getting-started
- https://stackoverflow.com/questions/18534591/how-to-register-a-url-protocol-handler-in-node-js
- Interesting one if you want to implement your own Protocol Handler
- https://www.npmjs.com/package/protocol-registry
- https://www.tutorialspoint.com/flattening-a-json-object-in-javascript
- https://www.npmjs.com/package/@json2csv/plainjs
- https://www.programcreek.com/typescript/?api=obsidian.Plugin
- https://www.programcreek.com/typescript/?api=obsidian.FileSystemAdapter
- https://marcus.se.net/obsidian-plugin-docs/reference/typescript/classes/Plugin_2#registerobsidianprotocolhandler
- https://stackoverflow.com/questions/836777/how-to-detect-browsers-protocol-handlers/837933#837933
- https://developer.mozilla.org/en-US/docs/Web/API/Window/open
- https://stackoverflow.com/questions/8500326/how-to-use-nodejs-to-open-default-browser-and-navigate-to-a-specific-url
- https://stackoverflow.com/questions/6182315/how-can-i-do-base64-encoding-in-node-js
- https://www.npmjs.com/package/csv-to-markdown-table
- https://github.com/donatj/CsvToMarkdownTable