Building TNT: A Markdown Heading Fixer for My Meeting Notes Workflow
Ever have that one tiny friction point in your workflow that drives you nuts? For me, it was dealing with markdown heading levels when moving meeting transcripts from Tactiq into my Obsidian notes. Here's how I built a simple tool to fix it, learned some Python along the way, and got my feet wet with Replit deployment.
The Problem: Markdown Hierarchy Headaches
My note-taking workflow is pretty dialed in these days. I use Tactiq.io to capture meeting transcripts, and their OpenAI integration helps me generate detailed summaries with citations. The export comes as a markdown-formatted .txt file, which is perfect since I live in Obsidian.md and markdown is basically my second language at this point.
But there was this one annoying snag: my daily note template in Obsidian has a specific hierarchy, with meetings living under an H2 (##) heading. When I'd paste in Tactiq exports, any H1s or H2s would mess up my careful organization and make it harder to collapse and navigate my notes. I'd have to manually adjust heading levels every single time. Not the end of the world, but definitely a small time tax on every meeting.
Enter TNT (Teddy's Neat Titles)
I decided to build something to fix this - a simple web app that would let me drop in markdown files and adjust the heading levels automatically. The result is TNT, and while it's not perfect (I'm already dreaming about turning it into a proper Obsidian plugin that could fetch from Tactiq's API), it gets the job done and taught me a bunch in the process.
How It Works
The core functionality is pretty straightforward:
- Drop a markdown/txt file into the web interface
- TNT processes it, bumping heading levels down (e.g., h1 → h4, h2 → h5)
- Copy the processed markdown and paste it into Obsidian
Behind the scenes, it's using a regex pattern to identify markdown headings and adjust their levels. The key part lives in the JavaScript:
```js
function processMarkdown(content) {
return content.replace(/^(\s*)(#{1,6})\s/gm, (match, spaces, hashes) => {
const level = hashes.length;
const newLevel = Math.min(level + 3, 6);
return spaces + '#'.repeat(newLevel) + ' ';
});
}
The Tech Stack
I kept things intentionally simple:
- Flask for the backend server (my first real Python project!)
- Vanilla JavaScript for the frontend functionality
- HTML5 File APIs for drag-and-drop and file handling
- Clipboard API for easy copy/paste
Everything runs client-side once the page loads, so it's super fast and doesn't need to send files to any server. Drop a file, and the JavaScript immediately processes it and offers you the adjusted markdown.
The Learning Experience
This was one of my early ventures into Python development, and I learned a ton about:
- Setting up a Flask application
- Handling static files and routing
- Deploying on Replit (including dealing with their specific environment requirements)
- Using AI as a development tool and learning resource
- Troubleshooting deployment issues (the fun never ends!)
What's Next?
While TNT does what I need it to do, I've got some ideas for the future:
- Building a proper Obsidian plugin that could fetch directly from Tactiq's API
- Adding customizable heading level adjustments
- Maybe some batch processing features
But for now, it's scratched my particular itch and removed one small friction point from my daily workflow. Plus, it was a fun way to dip my toes into Python development and deployment.
Want to Try It?
The code is available on Replit, and you're welcome to try it out or adapt it for your own workflow. It's a simple solution to a specific problem, but sometimes those are the most satisfying projects to build.
Have you built something similar to smooth out your own workflow? I'd love to hear about it in the comments!