Skip to content

Instantly share code, notes, and snippets.

@elight
Last active March 1, 2023 18:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elight/beee6b13cac3a08614ed821f5861cf6e to your computer and use it in GitHub Desktop.
Save elight/beee6b13cac3a08614ed821f5861cf6e to your computer and use it in GitHub Desktop.
Generating mermaid flow diagram based on @elight "project-tagged" Tasks using Obsidian CustomJS and baked in mermaid support
class Utils {
escapeRegex(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
}
  • #pr/github/testproject/1 First node
  • #pr/github/testproject/2a/1 Second node
  • #pr/github/testproject/2b/1 Second alternate
  • #pr/github/testproject/3/2a/2b End state

Visualization for pr/github/testproject

const {Project} = customJS;
new Project.Project2Flow(dv.current().source, dv.current().prtag, dv).run();

That emits mermaid JS that looks like this:

flowchart TD
id1[text]
id1 --> id2a[text]
id1 --> id2b[text]
id2a --> id3[text]
id2b --> id3[text]

... that renders as something that looks like this:

  1
 / \
2a  2b
 \ /
  3

... except all of the identifiers are replaced by the text on each task.

class Project {
Project2Flow = class {
source;
prtag;
pattern;
buffer;
constructor(source, prtag, dv) {
this.source = source;
this.prtag = prtag;
this.dv = dv;
const {Utils} = customJS;
this.pattern = new RegExp(Utils.escapeRegex("#" + prtag + "/"));
this.buffer = "";
this.span("```mermaid");
this.span("flowchart TD");
}
span(code) {
this.buffer = this.buffer.concat(`${code}\n\n`);
}
extra_edges_from(tag) {
var pr_tag = tag.text.split(' ')[0];
//this.dv.span(`- ${pr_tag}\n`);
// +1 for leading # and +1 for trailing '/'
var edges = pr_tag.substring(this.prtag.length + 2).split("/");
//this.dv.span(`- Edges: ${edges}\n`);
return edges;
}
task_to_description_only(t) {
var desc = "";
// chop off project tag
var parts = t.text.split(' ').slice(1);
//this.dv.span(`- Desc No PR Tag: ${parts}`);
// excise due, start, and scheduled dates
parts = parts.filter(w => !w.match(/^[📅🛫⏳]\d{4}-\d{2}-\d{2}/));
// excise date
parts = parts.filter(w => !w.match(/\d{4}-\d{2}-\d{2}/));
// excise priority
parts = parts.filter(w => !w.match(/^[⏫🔼🔽]/));
// excise inline annotations
parts = parts.filter(w => !w.match(/^\[.*\]$/));
parts = parts.map(w => {
var m = w.match(/\[(.+)/) || w.match(/(.+)\]\(.+/);
//this.dv.span(`\n\n${w}\n\n`);
if (m) {
//this.dv.span(`\n\nm[1]: ${m[1]}\n\n`);
return m[1];
} else {
return w;
}});
desc = '"' + parts.join(' ') + '"';
return desc;
}
node_to_string(id, desc = "") {
id = `id${id}`;
if (desc.length > 0) {
desc = `[${desc}]`;
}
return `${id}${desc}`;
}
emit_root(id, desc) {
this.span(this.node_to_string(id, desc));
}
emit_branch(id, desc, src_id) {
this.span(`${this.node_to_string(src_id)} --> ${this.node_to_string(id, desc)}`);
}
run() {
this.dv.pages(this.source).file.tasks
.where(t => t.text.match(this.pattern))
.map(t => {
//span(t.text);
var edges = this.extra_edges_from(t);
//span(`- Edges: ${edges}`);
var dest = edges[0];
var src_ids = edges.slice(1);
//span(`- src IDs: ${src_ids}`);
var dest_desc = this.task_to_description_only(t);
//span(`- desc: ${dest_desc}`);
if (src_ids.length == 0) {
this.emit_root(dest, dest_desc);
} else {
for(var i = 0; i < src_ids.length; i++) {
this.emit_branch(dest, dest_desc, src_ids[i]);
}
}
return t;
});
this.span("```");
this.dv.span(this.buffer);
}
}
}
@elight
Copy link
Author

elight commented Jun 4, 2022

This renders as
Screen Shot 2022-06-04 at 4 08 09 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment