obsidian

🏠

I've discovered an incredible tool, Obsidian, on one of my favourite technology forums, Hacker News.

obsidian.png

Holy moly. Love at first byte. I really love the graph view, and the keyboard bindings which enable me to just jump around in a graph-based, non-linear way.

That's really fantastic, since thought is graph-based, and non-linear. Jumping from one thing to another is a very normal default way of thinking. It's important to respect thought's chaotic state--and to capture it properly--and only then try to make it linear.

So i quickly wrote a tornadoweb server, to serve up my obsidian cluster as a site. This will of course evolve over time, but currently it looks like this:

  1 import os
  2 import re
  3 
  4 import tornado.ioloop
  5 import tornado.web
  6 from tornado.template import Template
  7 
  8 from markdown import markdown
  9 from glob import glob
 10 from pathlib import Path
 11 
 12 extensions = ['fenced_code', 'codehilite', 'tables', 'footnotes', 'admonition', 'nl2br', 'toc']
 13 
 14 extension_configs = {
 15     'codehilite': {
 16         'linenums': False, # look at CSS before enablings this
 17     }
 18 }
 19 
 20 class Note(str):
 21 
 22     @property
 23     def path(self):
 24         for sep in ['-', '_']:
 25             path = f'./{self.replace(sep, " ")}.md'
 26             if os.path.exists(path):
 27                 return path
 28 
 29     @property
 30     def name(self):
 31         return os.path.basename(os.path.splitext(self)[0]).replace('-', ' ').replace('_', ' ')
 32 
 33     @property
 34     def href(self):
 35         return os.path.splitext(self)[0].replace(' ', '-')
 36 
 37 class MainHandler(tornado.web.RequestHandler):
 38     def get(self):
 39         with open('./index.md') as f:
 40             t = Template(f.read())
 41             dailies = []
 42             for d in sorted(glob('dailies/*-*-*.md'), reverse=True):
 43                 dailies.append({
 44                     'name': os.path.splitext(d)[0]
 45                 })
 46             blog_posts = []
 47             for d in sorted(glob('blog/*.md'), reverse=True):
 48                 blog_posts.append(Note(d))
 49             self.write(markdown(t.generate(dailies=dailies, blog_posts=blog_posts).decode('utf-8'), extensions=extensions, extension_configs=extension_configs))
 50 
 51 def get_notes():
 52     notes = {}
 53     for path in (os.path.splitext(x)[0] for x in Path('.').rglob('*.md')):
 54         path, file = os.path.split(path)
 55         notes[file] = f'{path or "."}/{file}'
 56     return notes
 57 
 58 class PageHandler(tornado.web.RequestHandler):
 59     def get(self, url):
 60         notes = get_notes()
 61         note = Note(url)
 62         with open(note.path) as f:
 63             content = f.read()
 64             content = self.fix_link(content, notes)
 65             content = self.fix_attachment(content)
 66             rendered_markdown = markdown(content, extensions=extensions, extension_configs=extension_configs)
 67             t = Template(open('base.html').read())
 68             rendered = t.generate(title=note.name, content=rendered_markdown).decode('utf-8')
 69             self.write(rendered)
 70 
 71     def fix_link(self, content, notes):
 72         """
 73         Finds links in the document, e.g [[this is a link]]
 74         and replaces them with the correct syntax.
 75         """
 76         for m in re.findall(r"\[\[([^\]]+)\]\]", content):
 77             link = notes.get(m, None)
 78             if link and os.path.exists(f'{link}.md'):
 79                 content = content.replace(f'[[{m}]]', f"[{m}](/{link.replace(' ', '-')})")
 80         return content
 81 
 82     def fix_attachment(self, content):
 83         matches = re.finditer(r"!\[\[(.*)\]\]", content, re.MULTILINE)
 84         for matchNum, match in enumerate(matches, start=1):
 85             tag, filename = match.group(0), match.group(1)
 86             content = content.replace(tag, f'![{filename}](/Attachments/{filename})')
 87         return content
 88 
 89 
 90 def make_app():
 91     return tornado.web.Application([
 92         (r"/", MainHandler),
 93         (r"/(favicon\.ico)",tornado.web.StaticFileHandler, {"path": "./static"},),
 94         (r"/static/(.*)",tornado.web.StaticFileHandler, {"path": "./static"},),
 95         (r"/Attachments/(.*)",tornado.web.StaticFileHandler, {"path": "./Attachments"},),
 96         (r"/(.*)", PageHandler),
 97     ], debug=True)
 98 
 99 if __name__ == "__main__":
100     app = make_app()
101     app.listen(8282)
102     tornado.ioloop.IOLoop.current().start()

Sorry if this looks like, well, a big unorganised chunk of code. :) But if you run it in your obsidian directory, with a bit of tweaking, you should get it to serve up your cluster as a site.

obsidian_definition.png

And to make things even more trippy: it turns out Obsidian is the exact English equivalent to my Sanskrit name Shyal. If that's not a sign, then i don't know what is. Adopted! And i am now thinking about how to just start using Obsidian for everthing, including storing of my knowledge, thing i'm currently doing with Anki.

Let's see if i can move my old blog articles, e.g [[hitting-the-front-page-of-hacker-news-and-reddit]]


Playground:

admonitions

This is an admonition box without a title.

Don't try this at home

blah blah

latex*

Footnotes

Footnotes1 have a label2 and the footnote's content.


  1. This is a footnote content. 

  2. A footnote on the label: "@#$%"