Zettelkasten Forum

Making stream preview work in (most) any app

edited July 2023 in Software & Gadgets

I don't use Marked and didn't really have a need for another markdown editor, but I thought the stream preview thing was kind of cool (see this video if you have no idea what I'm talking about), so I made this awful hack to make it work with pretty much any markdown editor/view/previewer.

Again, this is a terrible hack, you probably shouldn't use it, you may not even want to look at it, I rarely use python, but it was able to do what I needed in OSX, so here we are. I also don't know much about OSX specifc stuff and mostly treat it as *nix.

I run this as a daemon, and get a file on a ramdisk (thanks to Christian for pointing out this probably shouldn't be on disk, especially since /tmp is not a ramdisk on OSX AFAICT).

This file's name and path always stays the same, so I can just point my viewer of choice at it and presuming it knows how to update on file changes, you'll see your preview update.

Christian pointed out to me that we can somewhat check when the pasteboard has been updated, but I haven't done that yet, so in addition to just making this generally better or more pythony, that remains a todo.

#!/usr/bin/env python3

from Foundation import *
import sched,time
import os
import subprocess

pb = NSPasteboard.pasteboardWithName_("mkStreamingPreview")

# TODO: Use changecount to not mindlessly read

RAMDISKNAME = 'stream'
# 2MB
PATH = RAMDISKPATH+'/streampreview.md'

def makeRAMDisk():
    if os.path.exists(RAMDISKPATH):
        # Make a ramdisk, format it HFS+, then mount it with the volume name of stream
        hresult = subprocess.run(["hdiutil", "attach", "-nobrowse", "-nomount", f"ram://{RAMDISKSIZE}"], capture_output=True)
        dev = hresult.stdout.rstrip()
        dresult = subprocess.run(["diskutil", "erasevolume", "HFS+", RAMDISKNAME, dev])

def watchClipboard(scheduler):
    scheduler.enter(1, 1, watchClipboard, (scheduler,))
    data = pb.dataForType_("public.utf8-plain-text")
    if data:
        decodedtext = NSString.alloc().initWithData_encoding_(data, NSUTF8StringEncoding)

        f = open(PATH, "w+", encoding="utf-8")

scheduler = sched.scheduler(time.time, time.sleep)
scheduler.enter(1, 1, watchClipboard, (scheduler,))


The code I posted above ends up making a /Volumes/stream/streampreview.md file.

That /Volumes/stream is a (very small) ramdisk so that the script isn't constantly using your hard drive and risking wearing it out.

Right now every second (though you could adjust it to be faster), whatever note is in the archive is copied into the file when you have the stream preview on.

This means I can open that file, /Volumes/stream/streampreview.md, in for example VSCode and start its markdown preview which can auto-refresh.

This way, as I open different notes in The Archive, I still get a fully rendered preview of it and it doesn't matter that VSCode (or whatever viewer I might use in the future) has no idea what "Marked 2 Stream Preview" is.

(Edited to fix typos and add usage information)

Post edited by Thai on


  • @Thai thanks for sharing the solution!

    Could you add some high-level info about what you're doing with the RAM-disk and how you use this to stream? → What's the path you're streaming to, and how do you use it, for example

    Author at Zettelkasten.de • https://christiantietze.de/

  • Sure thing, I edited the original with a bit of usage info, but happy to answer querstions if folks have them as well. Not sure how useful this will be to others. I just didn't want to have to use one particular viewer when working with my zettel, not sure how many other folks are in a similar boat?

Sign In or Register to comment.