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.