< More writing

Writing an Obsidian Plugin

01-15-2024

Last year I published Chronostory. Deploying a project I've thought about for a while has been satisfying despite it not catching the traction I anticipated. It can be discouraging when a project's impact doesn't match expectations.

Because I haven't yet been able to get Chronostory to grow— and I still believe it has value for helping others who haven't tried out the Storyclocking process— I decided to bring parts of it into an open source plugin for Obsidian (check it out).

Chronostory is a Next.js app with Redux state management and uses Supabase for database management, but all I needed for the Obsidian plugin were a couple of React components, the <StoryClock/> and <MomentTick/> components.

To get started, I just jumped into Obsidian's Sample Plugin and started to poke around with their many examples.

As advised, I created a Dev Vault specifically to install the plugin locally. There is also someone that created a hot reload plugin that you can install on your Dev Vault, so that was an unexpected nice thing to have.

Shared with a friend early

My first night working with it, I just wanted to see the <StoryClock/> Component render in Obsidian, no logic or state — just see it displayed. So looking through the docs, I was conveniently able to figure out how to add React components. And at first I had it displaying in the right-side pane (Obsidian calls this a Leaf). I showed it to a friend who recommended I just try to the clock rendering in the editor itself, and this made the whole problem much more straightforward.

first-draft-poking-around-obsidan

Using Codeblocks

Following this recommendation, I landed on using Obsidian's built-in post-processor for codeblocks, registerMarkdownCodeBlockProcessor linked here. So here's all the main.ts looks like.

import { Plugin } from 'obsidian';
import { CodeBlockProcessor } from './src/CodeBlockProcessor';

export default class StoryClockPlugin extends Plugin {

	async onload() {
		this.registerMarkdownCodeBlockProcessor("storyclock", (source, el) => {
			const processor = new CodeBlockProcessor(this.app);
			processor.run(source, el);
    });
	}

	onunload() {

	}
}

The CodeBlockProcessors then takes in two types of lines. One type of line is the runtime of the story (e.g. the runtime of The Fabelmans is 02:31:00), and the rest of the lines begin with a timestamp and describe what happened at each moment. Here's an example for The Prestige from Nolan:

codeblock-example

I also landed on some in-component validation that could also be used for in-component help for someone trying out the component for the first time:

storyclock-validation-cropped

So it would show your moments on a default runtime of 2 hours, just so if you're missing your runtime, you'll still see a few beats in the story and have an idea of how to interact with it.

The Obsidian review process

Obsidian has some really decent docs and built-in CI/CD that gave me automated feedback, and that made me feel pretty confident in my plugin ahead of its review. Just needed to make a pull request, and they pointed out if I was missing any properties in my plugin's manifest, made sure I had a GitHub release with all the right included files, and a couple other nice checks.

Mine was a simpler plugin, but perusing through others' reviews, it's great what Obsidian does to make sure a plugin is ready to be published. It took a couple of weeks to be approved, but given the detail they were putting into each review, this seems very reasonable.

Approved and merged

Then one morning the PR was merged, and there was rejoicing! I really hope there are a few out there that can use this visual to clock all types of stories: reviewing user stories, studying films, breaking down the timing of a speech. There are a plenty of use cases for it, and I hope to hear about how others have used it.

obsidian-listing< More writing