Optimizing the links in the book for first time readers

Writing isolated pieces of documentation can be entertaining, but sometimes we want to enhance the findability of those pieces. Linking them with other pieces can be a good path to achieve this.

We actually had this very same problem when writing this book: We wanted to make it possible for people to start at Glamorous Toolkit and have the chance to reach as many pages as possible by following direct links.

To help us with finding new linking opportunities, we built a visualization. We take inspiration from the visualization described in How to visualize the current knowledge base. We start by collecting the pages.

pages := thisSnippet database pages reject: [:each | 
	each title = 'Glamorous Toolkit Book']
  

Then we collect the pages that are reachable from Glamorous Toolkit by traversing deeply all the outgoing links using DeepTraverser.

getStartedPage := pages detect: [:each | each title = 'Glamorous Toolkit'].
allReachedPages := getStartedPage deepCollect: [:each | 
	each allChildOutgoingTextualLinks collectAsSet: [:x | x target ifNotNil: #page] ].
  

And then we highlight the reached pages in the overall graph:

m := GtMondrian new.
m nodes
	stencil: [ :each | 
		| color size |
		color := (allReachedPages includes: each)
				ifTrue: [ Color red ]
				ifFalse: [ Color black ].
		size := 5 @ 5.
		each = getStartedPage ifTrue: [ color := Color blue. size := 10 @ 10 ].
		BlElement new background: color; size: size;
			when: BlClickEvent
				do: [ :e | e currentTarget phlow spawnTool: each asPhlowTool ] ];
	with: pages.
m edges
	stencil: [ BlLineElement new
			border: Color veryLightGray;
			toHead: (BlArrowheadSimpleArrow new border: Color veryLightGray);
			zIndex: -1 ];
	connectToAll: [ :page | page allOutgoingTextualLinks collectAsSet: #target ].
m layout force nbIterations: 30; charge: -50.
m
  

So, now we have an idea of the parts of the book that are not reachable. But would it not be better to show the pages as they are in the book table of contents instead of as a graph of links? Let's try.

First, we assemble the data structures. Here we traverse the table of contents page itself and get the links from each snippet.

tocPage := thisSnippet database pages
		detect: [ :each | each title = 'Glamorous Toolkit Book' ].
allTocSnippets := tocPage allChildrenBreadthFirst.
  

And then we put it together in a simple visualization of the snippets that uses allReachedPages computed above to highlight the corresponding snippets:

m := GtMondrian new.
m nodes 
	stencil: [ :each | 
		| title color |
		title := (each contentAsString removePrefix: '[[') removeSuffix: ']]'.
		color := Color gray alpha: 0.2.
		(allReachedPages anySatisfy: [ :aPage | aPage title = title ])
				ifTrue: [ color := Color red alpha: 0.4 ].
		title = 'Glamorous Toolkit' ifTrue: [ color := Color blue alpha: 0.6 ].
		BlShrinkingTextElement new
			constraintsDo: [:c |c horizontal exact: 300. c vertical fitContent];
			background: color;
			text: title asRopedText glamorousRegularFont;
			when: BlClickEvent do: [:e | e consumed: true. e target phlow spawnObject: (each outgoingExplicitLinks anyOne target)] ];
	with: allTocSnippets.
m edges fromRightCenter; toLeftCenter; connectToAll: #children.
m layout custom: (GtGraphHorizontalTreeLayout new levelDistance: 150).
m
  

This now gives us a view that is closer to the mental model of a book. The resulting tree shows pages. But some pages are longer than others, and it can be useful sometimes to also show the size of pages.

To this end, we can count the words from the text representation of a page:

sizeCounter := [ :aPage | 
	aPage allChildrenBreadthFirst
		sumNumbers: [ :each | each contentAsString substrings size ] ].
maxSize := pages max: sizeCounter
  

And then we enhance the visualization:

m := GtMondrian new.
m nodes
	stencil: [ :each | 
		| title color linkedPage |
		linkedPage := each outgoingExplicitLinks anyOne target.
		title := linkedPage title.
		color := Color gray alpha: 0.2.
		(allReachedPages anySatisfy: [ :aPage | aPage title = title ])
			ifTrue: [ color := Color red alpha: 0.4 ].
		title = 'Get started' ifTrue: [ color := Color blue alpha: 0.6 ].
		BlShrinkingTextElement new
			constraintsDo: [ :c | 
				c horizontal exact: (((sizeCounter value: linkedPage) / maxSize * 500) max: 30).
				c vertical fitContent ];
			background: color;
			text: title asRopedText glamorousRegularFont;
			padding: (BlInsets all: 2);
			when: BlClickEvent
				do: [ :e | 
					e consumed: true.
					e target phlow spawnObject: linkedPage ] ];
	with: allTocSnippets.
m edges
	fromRightCenter;
	toLeftCenter;
	connectToAll: #children.
m layout custom: (GtGraphHorizontalTreeLayout new levelDistance: 100; layered).
m
  

Of course, in a world of executable snippets, we can find other interesting measurements than words. For example, we can also consider the amount of snippets in a page.

sizeCounter := [ :aPage | aPage allChildrenBreadthFirst size ].
maxSize := pages max: sizeCounter.