Optimizing the links in the book for first time readers
Writing isolated pieces of documentation can be entertaining, but sometimes we want to improve the discoverability of those pieces. Linking them with other pieces can be a good way to achieve that.
We had the same problem when writing this book: we wanted to make it possible for people to start at Glamorous Toolkit and be able to reach as many pages as possible by following direct links.
To help us find new linking opportunities, we built a visualization. We took inspiration from the visualization described in How to visualize the current knowledge base. First, we collect the pages.
pages := thisSnippet database pages reject: [:each | each title = 'Glamorous Toolkit Book']
Then we collect the pages reachable from Glamorous Toolkit by deeply traversing all outgoing links using DeepTraverser.
getStartedPage := pages detect: [:each | each title = 'Glamorous Toolkit']. allReachedPages := getStartedPage deepCollect: [:each | each allChildOutgoingTextualLinks collectAsSet: [:x | x target ifNotNil: #page] ].
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 wouldn't it be better to show the pages as they appear in the book's 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.
Then we put it together in a simple visualization of the snippets, using the allReachedPages computed above to highlight the corresponding snippets:
m := GtMondrian new. m nodes stencil: [ :each | | title color | title := (each contentAsString trim removePrefix: '[[') removeSuffix: ']]'. (allReachedPages anySatisfy: [ :aPage | aPage title = title ]) ifTrue: [ color := Color red alpha: 0.4 ] ifFalse: [ color := Color gray alpha: 0.2 ]. 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 to also show the size of pages.
To this end, we can count the words in 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 measures than words. For example, we can also consider the number of snippets in a page.
sizeCounter := [ :aPage | aPage allChildrenBreadthFirst size ]. maxSize := pages max: sizeCounter.