Introducing Shaku - a family of tools that help write tech articles
My frustration on Markdown
Since 2 years ago, I have been writing about how React works internally in the series of React Internals Deep Dive, yet I constanly feel frustrated about the writing experience.
My posts are written in Markdown, which is a quite good choice except that:
- Markdown generated posts lack interactivity
- it is hard to explain code snippet, especially for longer ones
For technical posts, interactivity is very important to provide readers with good learning experience. Looking at great posts from nan.fyi or web.dev, we can see that great posts should have following components.
- interactive demo
- code editor
- typography components
- quizzes
- diagrams
- âŚ
And there doesnât seem to be existing solutions to my needs, so I decided to create some by myself.
Introducing Shaku - a family of tools that help write tech articles
é- elucidate, explain
Iâm building a bunch of modules based on my writing experience from jser.dev, it might be a UI component, markdown parser plugin or anything that might be useful, all will be put under the umbrella of âShakuâ
shaku-code-annotate - annotate code snippets in markdown
Letâs take a look the first tool from Shaku - shaku-code-annotate, to understand what I mean by âhelp write tech articlesâ.
Suppose I need to explain code about binary search authored by others, so I add following snippet to markdown.
markdown
```js/*** search the index of target, or the position to insert* @param {number[]} arr* @param {number} target*/function binarySearch(arr, target) {let i = 0let j = arr.lengthwhile (i <= j) {// choose the center every time and move cursor// to one of the halvesconst mid = Math.floor((i + j) / 2)if (arr[mid] === target) {return mid} else if (arr[mid] < target) {i = mid + 1} else {j = mid - 1}}return i}```
markdown
```js/*** search the index of target, or the position to insert* @param {number[]} arr* @param {number} target*/function binarySearch(arr, target) {let i = 0let j = arr.lengthwhile (i <= j) {// choose the center every time and move cursor// to one of the halvesconst mid = Math.floor((i + j) / 2)if (arr[mid] === target) {return mid} else if (arr[mid] < target) {i = mid + 1} else {j = mid - 1}}return i}```
Problem is that I want to emphasize that the while
condition is i <= j
, not i < j
,
because me myself often get it wrong.
I could add some more comments to the line of code, but the original code already has 2 lines of comments, which makes it hard to tell the context if I just put them there, like below.
js
function binarySearch(arr, target) {let i = 0let j = arr.lengthwhile (i <= j) {// ^// [attention that it is <= here, not <]// [it makes sure one more of round of comparison when i and j meets]// choose the center every time and move cursor// to one of the halvesconst mid = Math.floor((i + j) / 2)if (arr[mid] === target) {return mid} else if (arr[mid] < target) {i = mid + 1} else {j = mid - 1}}return i}
js
function binarySearch(arr, target) {let i = 0let j = arr.lengthwhile (i <= j) {// ^// [attention that it is <= here, not <]// [it makes sure one more of round of comparison when i and j meets]// choose the center every time and move cursor// to one of the halvesconst mid = Math.floor((i + j) / 2)if (arr[mid] === target) {return mid} else if (arr[mid] < target) {i = mid + 1} else {j = mid - 1}}return i}
The core issue is that I want to annotate the code in the context of the post, not the context of the code, which cannot be addressed with just a simple code snippet.
Now with shaku-code-annotate, we are able to annotate code in a new context, out of the code itself.
To start, we need to first add the right plugin. This website is built with astro, it uses remark
to parse Markdown, so I added remark-shaku-code-annotate.
We only need to add annotate
to the code block meta to kick it off
diff
-```js+```js annotatefunction binarySearch(arr, target) {let i = 0let j = arr.length...}```
diff
-```js+```js annotatefunction binarySearch(arr, target) {let i = 0let j = arr.length...}```
For the binary search example, we are able to see it rendered like this.
function binarySearch(arr, target) {let i = 0let j = arr.lengthwhile (i <= j) {attention that it is <= here, not <
it makes sure one more of round of comparison when i and j meets
// choose the center every time and move cursor// to one of the halvesconst mid = Math.floor((i + j) / 2)if (arr[mid] === target) {return mid} else if (arr[mid] < target) {i = mid + 1} else {j = mid - 1}}return i}
function binarySearch(arr, target) {let i = 0let j = arr.lengthwhile (i <= j) {attention that it is <= here, not <
it makes sure one more of round of comparison when i and j meets
// choose the center every time and move cursor// to one of the halvesconst mid = Math.floor((i + j) / 2)if (arr[mid] === target) {return mid} else if (arr[mid] < target) {i = mid + 1} else {j = mid - 1}}return i}
Isnât it cool? Below is an example showing a lot more options, including underlines and highlights.
const blog = "https://jser.dev"JSer.dev is the homepage for JSer.
Check it out!
// This is normal comments from source code.const blog = "https://jser.dev"~~~~~~~~JSer.dev is the homepage for JSer.
Check it out!
const blog = "jser.dev"--------Check it out!
const blog = "jser.dev"........Check it out!
const blog = "jser.dev"........const blog = "jser.dev"--------const blog = "jser.dev"~~~~~~~~function useSomeEffect({blog}) {~~~~~~~~~~~~~useEffect(() => {// do some stuffreturn () => {location.href = 'https://jser.dev'}This cleanup function is super important
}, [blog])}
const blog = "https://jser.dev"JSer.dev is the homepage for JSer.
Check it out!
// This is normal comments from source code.const blog = "https://jser.dev"~~~~~~~~JSer.dev is the homepage for JSer.
Check it out!
const blog = "jser.dev"--------Check it out!
const blog = "jser.dev"........Check it out!
const blog = "jser.dev"........const blog = "jser.dev"--------const blog = "jser.dev"~~~~~~~~function useSomeEffect({blog}) {~~~~~~~~~~~~~useEffect(() => {// do some stuffreturn () => {location.href = 'https://jser.dev'}This cleanup function is super important
}, [blog])}
visit Shaku Playground to see the source code.
This is just the first tool from the box, stay tuned and follow shaku on github.