Webux Lab

By Studio Webux

Obsidian Plugin

TG
Tommy Gingras Studio Webux 2023-02-07

Custom Obsidian Plugin

Goal: Learning !


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:

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…

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


Search