Yes. People who adopt the code can inject these attributes. In fact in filterCSV I have the following example - as it supports export from iThoughts to .dot format:
filterCSV tries to mimic in .dot language the shapes in iThoughts, mostly succeeding.
One thing that interested me is that the node with text “hello\nworld” has the newline explicitly included. I’d hoped to be able to avoid that and have some kind of autoflow.
In general the boxes could be “souped up”, perhaps with references to tags. I’m not sure if those could be “live” - as I’m not an expert in GraphViz .dot language.
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.
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…
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?
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).
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.)
@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.
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… ”. 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…
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!
So, with the advent of Markers, I updated the code to handle them…
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.
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)
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…
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…