Obsidian-like behaviour

I’m sure I could craft a URL but that is already taken by the link back to the draft in Drafts. Another one would be nice.

I’m investigating how smart nodes can be in .dot language - such as running code and putting up context menus.

Hi Martin,
The actions make sense to me…
#1 should live at the bottom, under a line-break?
#2 Sounds like a plausible approach.

I’m not much of coder - more of a script tinkerer…

2 Likes

I think the output has to be svg. So links will be supported.[quote=“sylumer, post:2, topic:8290”]
What is the benefit of the graph
[/quote]

It might be interesting in use of visual indexes of a knowledge base.

Still get @sylumer point…

1 Like

You’re tempting me - but unfortunately I have too many “balls in the air” codingwise right now. :frowning:

1 Like

SVG could probably have event handlers on it, particularly if embedded in HTML. But then we’re into making this a browser-based thing. Unless Drafts HTML support can really stretch that far.

  1. I have an action that basically does this– places a link to a selected draft at cursor in current draft, then opens selected draft and allows you to place the paired link wherever’s appropriate. It’s doable. If you’re not comfortable with making your own actions, I could tidy mine up enough to share it for experimentation…

  2. You asked for auto-updates for updated titles. Manual actions aren’t the same, I know,
    but here’s an action and an alternative take on that action to handle title changes.

This is one of the (many) reasons I love Drafts. It doesn’t try do everything (nor should it), but it’s extensible enough to do a lot.

I also wonder if Drafts’ cross-linking philosophy is just different from Roam’s. Roam shows up back-links and references automatically; Drafts makes it easy to link to a draft, but also makes really easy to see links to the current draft. Maybe it’s not about pushing Drafts to be more Roam-like, but more about embracing Drafts as is?

2 Likes

I was more thinking about a Single Page Application that will open in a Browser to do this. The links back to drafts should be doable that way too.

1 Like

I agree. I’m building on what @agiletortoise has added this year and seeing how close we get - with useful stuff. That was the point of me asking the question in the first place.

I’m certain now I can do all I want in Drafts, without resorting to Roam (etc).

1 Like

Yes, I think in-browser is realistic. As I alluded to, I’ve tested with SVG output from GraphViz on Mac. I think that can be wrangled - to provide extra function - and then thrown into a browser. (In my case Firefox.)

1 Like

@jsamlarose You make excellent points - Drafts does drafts well - no need to make it another tool. Backlinks could make it a primary tool - for me, is where I am coming from.

If you would not mind sharing your actions, I would be grateful.

The resources you’ve shared - I will read more into, thank you for being so generous with your post.

I love the flexibility Drafts offers - why I am so attracted to it as my primary tool.

2 Likes

Looks like I shared this at some point before. It’s unlisted, so here’s a direct link: https://actions.getdrafts.com/a/18Y

And I totally hear you about back-links. When they were first announced in Drafts, my first two thoughts were “this is great!” and “but this isn’t like Roam… :frowning:”. My immediate response was to put together a couple of actions to handle what I thought was missing. It’s only just recently that I’ve come to appreciate that Drafts’ native back-linking works well without that much additional tweaking. I just had to unlearn the expectations established by experimenting with Roam and other similar tools. Maybe it’s a little like the old PC/Mac issue for switchers, when some people complained about OS(X) because it didn’t behave like the Windows they were used to. We can adjust our tools to our thinking or our thinking to our tools…

One more thing: we don’t have a great deal of discussion here about how back-links are actually used in Drafts, whereas Roam and Obsidian users have access to a fair amount of conversation about the value of back-links and how they’re making use of the functionality. This is only one aspect of what Drafts does, but a bit more conversation around how people are actually using back-links might help users appreciate the functional/philosophical differences between Drafts and other tools, and might also spur more meaningful experiments with actions that support what Drafts already does…

2 Likes

You are so right:

We can adjust our tools to our thinking or our thinking to our tools…

Thank you for helping me to adjust my thinking…

This conversation has helped me to realize the power of drafts and linking - and I have more to explore.

I suspect, as you said, so do many others. With backlinking now a hot topic and at the forefront of writers and note takers minds - this is an opportunity for Agile to share the power of backlinking inside Drafts…

Thank you again for sharing your resources - I look forward to exploring this weekend!

2 Likes

So, with the advent of Markers, I updated the code to handle them…

  1. If there is a reference to a marker I create an edge with a clickable link that takes you to that marker / section. The label on the edge is the section name.
  2. If the reference is to a draft the edge has a clickable link to the draft. For now I label the edge with a dot. (Better ideas than a dot welcome - but you need something to click on. I could just suppress the clickability of the link as the node takes you to the draft anyway.
    One other thing I would’ve liked to do is to place the first 100 chars or so of the draft on a node but it looks a mess. I need a function to turn the top of a draft into clean text to do that.

Anyhow hack on and test this, please.

Here’s the code:

// Takes the drafts from the current workspace and makes a
// GraphViz .dot file on the clipboard.

// Get an array of the drafts in the current workspace
drafts = app.currentWorkspace.query("inbox")

// Process each draft to get its title and the titles of the drafts it links to
draftTitles = []
draftInternalLinks = []
draftExternalLinks = []
foundExternalLinks = []

//internalLinkRegex = /\[\[(.*?)\]\]/g
internalLinkRegex = /\[\[.*?\]\]/g

//externalLinkRegex = /\[(.*?)\]\((.*?)\)/g
externalLinkRegex = /\[.*?\]\(.*?\)/g

// Iterate over the drafts in this workspace
for(i = 0 ; i < drafts.length ; i++){
    theDraft = drafts[i]
    draftTitles.push(theDraft.title)
    
    // Get internal links - if any
    matches = theDraft.content.match(internalLinkRegex)
    if (matches != null){
        draftInternalLinks[i] = matches.join("\n").replaceAll("[","").replaceAll("]","").split("\n")
    }else{
        draftInternalLinks[i] = []
    }
    
    // Get external links - if any
    matches = theDraft.content.match(externalLinkRegex)
    if (matches != null){
        draftExternalLinks[i] = matches.join("\n").replaceAll("[","").replaceAll("]","").replaceAll("(","!!!XYZZY!!!").replaceAll(")","").split("\n")
    }else{
        draftExternalLinks[i] = []
    }
}

// Compose the .dot output
GraphViz = 'digraph {\n'
missingDrafts = []
for(i = 0 ; i < drafts.length ; i++){
    theDraft = drafts[i]
    draftURL = 'drafts5://open?uuid=' + theDraft.uuid
    // Write the node out
    GraphViz += '    N' + i.toString() + '[label="' + theDraft.title + '" ; URL = "' + draftURL + '"]\n'
    
    // Write each internal link out - if found
    for(j = 0; j < draftInternalLinks[i].length; j++){
        otherDraftWithSectionName = draftInternalLinks[i][j].split("/")
        otherDraftName = otherDraftWithSectionName[0]
        if (otherDraftWithSectionName.length == 2){
            otherDraftSectionName = otherDraftWithSectionName[1]
            markerString = '&marker=' + encodeURI(otherDraftSectionName)
            label = otherDraftSectionName
        }else{
            otherDraftSectionName = ""
            markerString = ''
            label = '.'
        }
        otherDraft = draftTitles.indexOf(otherDraftName)
        if(otherDraft > -1){
            // Have another draft to point at
            otherDraftURL = 'drafts5://open?uuid=' + drafts[otherDraft].uuid + markerString
            GraphViz += '        N' + i.toString() + ' -> N' + otherDraft.toString() + '[label="' + label +'" href = "' + otherDraftURL + '"]' + '\n'
        }else{
            // This is a missing draft
            missingDraftIndex = missingDrafts.indexOf(otherDraftName)
            if (missingDraftIndex == -1) {
                // Missing draft not already in the list
                missingDrafts.push(otherDraftName)
                missingDraftIndex = missingDrafts.length - 1
                // Write the node out
                GraphViz += '    M' + missingDraftIndex.toString() + '[fillcolor = lightcoral ; style = filled ; label="' + otherDraftName + '"]\n'
            }
            
            GraphViz += '        N' + i.toString() + ' -> M' + missingDraftIndex.toString() +' [color = lightcoral]\n'
        }
    }
    
    // Write each external link out - if found
    for(j = 0; j < draftExternalLinks[i].length; j++){
        if(foundExternalLinks.indexOf(draftExternalLinks[i][j]) == -1){
            // Register fresh external link name/URL combo
            foundExternalLinks.push(draftExternalLinks[i][j])
            linkNumber = foundExternalLinks.length - 1
            
            // Create node for external name/URL combo
            splitLink = draftExternalLinks[i][j].split("!!!XYZZY!!!")
            otherPageName = splitLink[0]
            otherPageURL = splitLink[1]
            GraphViz += '    E' + linkNumber.toString() + '[label = "' + otherPageName + '"; fillcolor = darkseagreen2; style = filled; URL="' + otherPageURL + '" ]' + '\n'
        }
            GraphViz += '        N' + i.toString() + ' -> E' + linkNumber.toString() +' [color = darkseagreen2]\n'
    }
}
GraphViz += '}\n'
app.setClipboard(GraphViz)



1 Like

Good to see this update, Martin! Tried the script in an action quickly last night. Got a couple of errors that I need to track down— have a feeling that it might be down to some illegal characters in the drafts (words or phrases enclosed in quotation marks) but like I say, it was only a quick look. I’ll dig deeper when I can…

1 Like

Thanks. I’m back to the day job today so my time on this will inevitably be small.

Let me know what you discover.

Did you get it to work with any workspace at all?

As I said, conditioning a draft’s text for display is a tough one.

Thanks

I was playing a bit with mermaidJS and nomnoml but graphviz is still the best maybe with https://github.com/magjac/d3-graphviz

I will report back on that

2 Likes

Yes, please experiment with that. Would be lovely if that stack were built into Drafts as a visualisation engine.

Okay. Yes— got this to work (kinda) with a workspace. Again, I think my issue previously was probably to do with a reserved character somewhere in whatever text the script is passing on; might need a replace to trap those reserved characters?

Came back to this again this evening and got this from the list of my drafts modified today. There are only about 12 drafts in the workspace, but some of them are essentially indexes. The online Graphviz client pretty much exceeded its functional limit in rendering these files, and this one (Dreampuf) didn’t really allow for zooming in.

Let me know if there’s anything else I can do here to help test further. I wonder if, rather than pushing to a clipboard, the script could produce a URL that could then open in a preview window in Drafts, which would make this feel a little more seamless? For example, I URL-encoded the contents of “GraphViz” and appended it to “Graphviz Online”, then set the action to open that…

2 Likes

Glad you enjoyed it. I’m wondering why the layout is so wide and short.

And, yes. You can do whatever you like with the text. I just put it on the clipboard so I can paste it into BBEdit and then save in a file.

(I don’t think this is my project for this weekend. Automating paste a CSV into Excel is what I want to work on.)

1 Like

Wow, the code bubbling up in this thread is very impressive.

Some notes:

  1. Graphviz — this is used by org-roam to render graphs that are presented in your browser-of-choice. The graphs rendered by org-roam are superior to the current one from Obsidian, IMO.

  2. I wonder if you can comment on scalability of generating a graph from all notes in a workspace. The action needs to process everything each time you want to update the graph. Ideally, the graph stays updated as one adds notes in Drafts. To accomplish this for any reasonably-sized workspace, I assume some intermediate caching is necessary?? This would allow the action to focus on only Recently-Modified notes.

  3. One of the core and long(est) standing features of Drafts has a very big advantage in this space, IMO. It is the fact that Drafts does not require a Title for a note. Obsidian and org-roam require a title when generating a note, and I (it may just be me) find this simple step can be a barrier to smooth note-taking.

  4. Would it be possible to keep one Workspace in Drafts in sync with Dropbox? If so, that Dropbox location could be made into a vault for Obsidian. This would of course let Obsidian do the heavy-lifting of caching and producing graphs. This would entail hardwiring compatibility between Obsidian’s version of markdown/linking notation and that used in Drafts. Since Drafts allows different Markdown versions, perhaps Obsidian’s version could be another? An advantage of this approach is that Drafts could continue to focus on its core strength without getting spread thin.

I think this is an extremely exciting potential direction for Drafts. If its notes could be ported/interfaced with graphical view of interconnected nodes, it would be a game-changer in a very hot area of app dev.

Good luck!