# Personal Obsidian Plugin > [!info] > This post is not a guide; it's mostly for inspiration and to showcase what is possible. > > Leave a comment below or send me [[About#Links|a message]] if you'd like to learn more about any specific topic. For this month's #challenge, I decided to write my own plugin for three reasons: - To reduce startup time, especially for [[Obsidian Quick Capture Shortcut|quick scratch notes]]. I have a sub-second startup requirement on all my devices – currently, it takes 700-900ms to start fresh with 4.5k notes and 8 active plugins - To have complete control and customization - To learn something new It's a mixed bag of features, containing multiple unrelated but useful functionalities for me. These can be grouped into two main methods of input plus an additional unsorted category: - Commands - Code Blocks - Unsorted (special handling) ## Commands ### Blog Helper **Blog Helper** is a command that updates the [[Home]] and [[Posts]] pages with new blog posts. Previously, I managed this via the unofficial Obsidian API, but this solution is much better. ### Attachments Cleaner **Attachments Cleaner** identifies all attachments that are not currently used or referenced by any note and allows them to be removed. Alternatively, I can remove them directly. ### Jump to TOP and BOTTOM Scrolling in iOS and iPadOS apps is quite frustrating (it sometimes jumps back to the starting position), so I needed something better. I added two commands to jump directly to the bottom and to the top. It works flawlessly in all three modes (Live/Source/Preview). ## Code Block Processor This is a classic code block, but instead of the language name, the registered handler name should be used. Below is a Code Block API example for the [[#Daily Note Helper]]. Other functions are similar, differing only in the `template` and `args` fields. ```yaml version: 1 template: daily-note-header args: date: 2025-01-16 ``` ```yaml version: 1 template: countdown args: date: 2025-01-16 targetDate: 2025-12-31 template: <p>${result.years} years, ${result.months} months, and ${result.days} days left for Something</p> ``` ### Templates #### Codapi Integration Runnable code snippets with support for templates, powered by [Codapi](https://codapi.org/). ```yaml version: 1 template: codapi args: hide: false templateName: main-with-imports.go template: | package main import ( "fmt" ) func main() { ##CODE## } ``` > [!info] FYI: Full integration involves more than just the code processor. This example only demonstrates template definitions. ![[Pasted image 20250126103438.png|Codapi Integration]] #### Daily Note Header The top header for Daily Notes includes arrows to navigate to the previous and next day. I wanted to avoid standard connections since chaining days doesn't make sense in this context and would clutter graph views. Another great functionality is the link to my [[Docs/Synology|Synology]] Photos app (a self-hosted replacement for Google or Apple Photos). This link is special because it filters photos/videos taken on a specific day. By using the plugin, if [[Synology]] ever updates its API (or I migrate to different app), I can update the link inside the plugin, and all previously created daily notes will continue working without requiring updates. ![[Pasted image 20250126101733.png|Daily Note Header]] #### Countdown This feature provides an arbitrary countdown to a target date. I use it exclusively in Daily Notes. ![[Pasted image 20250126101904.png|Countdown]] #### Dashboards for Habits Currently a work in progress, inspired by [kepano's post](https://www.reddit.com/r/ObsidianMD/s/VytVp8OhEq). #todo/wiki This section will be updated in the future. ## Unsorted ### Jupyter Notebook Support The plugin supports both real [Jupyter](https://jupyter.org/) notebooks and read-only generated Jupyter HTML snapshots. Embeddable as: ```html <iframe class="dynamic-height-iframe" width="100%" height="1500px" frameborder="0" scrolling="auto" loading="eager" src="https://lb.example.com/Notebook.html?mode=view"></iframe> ``` Where the `mode` parameter can be: - `edit` – The iframe redirects to a real Jupyter server. For security, this only works locally on LAN (or VPN). If I forget to change it to `view` before publishing, it defaults to `view` when accessed outside my home network. ![[Pasted image 20250126102850.png|Sample Jupyter Notebook with Edit Mode]] - `view` – Rendering is handled by the `jupyter nbconvert` command, with my custom patch for improved fonts and other tweaks. This is where the plugin shines – it automatically expands the iframe to the maximum size, eliminating inner scrollbars and blending seamlessly with my theme. ![[Pasted image 20250126102729.png|Sample Jupyter Notebook with View Mode]] ## Conclusion Obsidian plugins can be incredibly useful. While you can combine multiple official [hub](https://obsidian.md/plugins) plugins to achieve specific functionality, it often comes with caveats. Plugins are powerful because they aren't sandboxed. They can access everything in your vault and on your computer. They execute with the same permissions as your computer's user (unless accessed remotely, such as via [Obsidian's Docker image](https://docs.linuxserver.io/images/docker-obsidian/)). My advice: - Never store sensitive data (like passwords) in an Obsidian vault. If you must, use a tool like [[1Password#`op inject` command|this]] - Use fewer plugins to improve startup time - Prefer plugins from reputable developers - Ideally, check the source code, build pipeline, and diffs before updating any plugin (e.g., for the *Recent Files* plugin: [source](https://github.com/tgrosinger/recent-files-obsidian/tree/refs/tags/1.7.4), [source diff for 1.7.3 → 1.7.4](https://github.com/tgrosinger/recent-files-obsidian/compare/1.7.3...1.7.4), [build pipeline](https://github.com/tgrosinger/recent-files-obsidian/blob/refs/tags/1.7.4/.github/workflows/release.yml), [build job for 1.7.4](https://github.com/tgrosinger/recent-files-obsidian/actions/runs/12601546616/job/35122856078)) > [!quote] > With great power comes great responsibility. For anyone interested in creating a custom plugin, I recommend starting [here](https://docs.obsidian.md/Plugins/Getting+started/Build+a+plugin). --- > [!info] > If I add more functionality to the plugin, I will update this post.