2021-12-12 Arrows Storytelling

While Marc and Ward were talking about a path from storytelling to model building, I was trying to recreate some grouping out of the Arrows SVG files.

UPDATE: we had submitted a suggestion to Arrows developers. We wrote the code on this page after getting no response for a while to that suggestion. They have since implemented our suggestion to offer semantic grouping to the SVG. As a result this code no longer enriches the SVG correctly. github .

See our new implementation.

We have enjoyed constructing graphs with Neo4J's Arrows app. Here we offer a workflow to enrich the SVG export from Arrows with wiki internal links. The combination makes for a fairly nice concept mapping tool. app

Our goal is to invent a way to move from stories to graphs and back again to that the limitations of stories and graphs can be cover come by combining the strengths of both in an iterative process. This is meant to be used by regular folk as then deal with neighborhood-level systems.

export async function emit(el) { el.innerHTML = `<h2>Hello, Arrows!</h2>` el.insertAdjacentHTML('beforeend', ` <input name="title" type="text" placeholder="page title"></br> <input name="source" type="text" placeholder="URL to svg file"><br> <button>ghost</button> `) } export async function bind(el) { document.querySelector('button') .onclick = async event => { let title = document .querySelector('input[name=title]').value let url = document .querySelector('input[name=source]').value console.log({here:"button click", title, url}) let graph = await go(url) console.log({here:"button click", graph}) window.parent.postMessage({ action: "showResult", page: { title, story: [ { id: "asldfasdfasf", type: "html", text: graph.outerHTML } ] }, keepLineup: event.shiftKey }, "*") } window.parent.postMessage({ action: "resize", height: document.body.offsetHeight }, "*") }

async function go(url) { let res = await fetch(url) let string = await res.text() let dom = new DOMParser().parseFromString( string, "image/svg+xml") let svg = dom.documentElement svg.removeAttribute("width") svg.removeAttribute("height") groupNodes(svg).forEach(anchorGroup) groupEdges(svg).forEach(anchorGroup) return svg }

function anchorGroup(group) { let anchor = document.createElementNS( "http://www.w3.org/2000/svg", "a" ) group[0].insertAdjacentElement( "beforebegin", anchor) for (let el of group) { anchor.parentElement.removeChild(el) anchor.appendChild(el) } let title = Array.from( anchor.querySelectorAll("text") ).map(text => text.textContent.trim()).join(" ") anchor.setAttribute("class", "internal") anchor.setAttribute("data-title", title) anchor.onclick = event => { event.preventDefault() event.stopPropagation() window.parent.postMessage({ action: "doInternalLink", title }, "*") } }

function groupNodes(svg) { let selector = list(5, (_,j) => list(j+1, (_, i)=> i?"text":"circle" ).join("+") ).join(",") return Array.from(svg .querySelectorAll(selector) ).reduce(groupStartsWith("circle"), []) }

function groupEdges(svg) { let selector = list(4, (_,i) => 'path+polygon+rect+text'.split("+") .slice(0,i+1).join("+") ).join(",") return Array.from(svg .querySelectorAll(selector) ).reduce(groupStartsWith("path"), []) }

const list = (size, map) => Array.from({length: size}, map) const groupStartsWith = tagName => (acc, shape) => { if (shape.tagName == tagName) { acc.unshift([shape]) } else { acc[0].push(shape) } return acc }

In the frame below we can view the results.


pages/2021-12-12 Arrows Storytelling