Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ builds
package-lock.json
public
resources
.codegpt
.codegpt

# Debugging expanded epub files
ebooks/contents/
ebooks/pandoc.log
19 changes: 18 additions & 1 deletion ebooks/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
# wisdom.epub

This is the latest version of the document exported from .md via Pandoc.

To compile this ebook, run the following command in the terminal:

```bash
./ebooks/compile.sh
```

The script assumes you have Pandoc installed. On macOS, you can install it using Homebrew:

This is the latest version of the document exported from .md via Pandoc.
```bash
brew install pandoc
```

## Markdown Pre-Processing

We use Pandoc's lua filters to process the Markdown files before converting them to EPUB.

You can add as many filters as you like in the `ebooks/filters` directory. The filters will be applied in the order they are listed in the `compile.sh` script.

ChatGPT and Claude are pretty good at drafting lua filters.
64 changes: 64 additions & 0 deletions ebooks/compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#! /usr/bin/env bash

# Ensure we are at the root of the project repository,
# regardless of where this script is run from.
cd "$(dirname "$0")/.."

# NOTES:
# Assumes we want exactly one wisdom per page in the EPUB.
# This assumes the md file is grouped into sections by h2 (##)
# and that within each section, each wisdom is a separate list item.

# Export EBOOK_VIEWER environment variable to specify the application
# Reasonable options: `calibre`, `Books`
EBOOK_VIEWER=${EBOOK_VIEWER:-"Books"}
# Append the current date to the EPUB filename
# DESTINATION="ebooks/Wisdom-$(date +%Y-%m-%d).epub"
DESTINATION="ebooks/wisdom.epub"

## Pass --debug to Pandoc for verbose output if needed.
if [[ "$1" == "--debug" ]]; then
PANDOC_DEBUG="--verbose --log=ebooks/pandoc.log"
else
PANDOC_DEBUG=""
fi

echo "Compiling wisdom.md to $DESTINATION..."
pandoc wisdom.md -o $DESTINATION \
--metadata title="Merlin's Wisdom Project" \
--metadata author="Merlin Mann" \
--metadata date="$(date +%Y-%m-%d)" \
--metadata description="Or: “Everybody likes being given a glass of water.”" \
--metadata language="en" \
--css=ebooks/style.css \
--shift-heading-level-by=-1 \
--toc --toc-depth=2 \
--split-level=2 \
--epub-cover-image=ebooks/cover.jpg \
--lua-filter=ebooks/filters/strip-comments.lua \
--lua-filter=ebooks/filters/process-md.lua \
$PANDOC_DEBUG;

if [[ $? -ne 0 ]]; then
echo "Error: pandoc failed to compile wisdom.md."
exit 1
fi

## If debugging, expand the zip file to inspect its contents.
if [[ "$1" == "--debug" ]]; then
echo "Debugging enabled. Compiled EPUB will be expanded for inspection."
unzip -l $DESTINATION
unzip -d ebooks/contents $DESTINATION
echo "EPUB contents extracted to ebooks/contents/"
exit 0
fi

## If the `open` command is available, open the EPUB after compiling.
if command -v open &> /dev/null; then
echo "Opening $DESTINATION..."
open $DESTINATION -a $EBOOK_VIEWER
else
echo "Compiled successfully. You can find the EPUB at ebooks/wisdom.epub"
fi

exit 0
22 changes: 22 additions & 0 deletions ebooks/filters/process-md.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- Enable this to _replace_ horizontal rules with a page break
-- Currently, style.css applies this class to all <hr> elements
-- function HorizontalRule(el)
-- return pandoc.RawBlock('html', '<div class="page-break"></div>')
-- end

function center_and_page_separate(el)
-- Convert to raw HTML to add classes to <li> elements
local items = {}
for i, item in ipairs(el.content) do
local item_html = pandoc.write(pandoc.Pandoc(item), 'html')
-- Remove any existing <p> tags if they wrap the content
item_html = item_html:gsub('<p>(.-)</p>', '%1')
table.insert(items, '<li class="center-page page-break">' .. item_html .. '</li>')
end

return pandoc.RawBlock('html', '<ul>' .. table.concat(items, '\n') .. '</ul>')
end

-- Apply the same transformation to BulletList and OrderedList
BulletList = center_and_page_separate
OrderedList = center_and_page_separate
23 changes: 23 additions & 0 deletions ebooks/filters/strip-comments.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
function RawBlock(el)
if el.format == 'html' then
-- Remove HTML comments
el.text = el.text:gsub("<!%-%-.-%-%->" , "")
-- Clean up extra whitespace
el.text = el.text:gsub("^%s*$", "")
end
-- Return nil to remove empty blocks
if el.text == "" then
return {}
end
return el
end

function RawInline(el)
if el.format == 'html' then
el.text = el.text:gsub("<!%-%-.-%-%->" , "")
if el.text == "" then
return {}
end
end
return el
end
40 changes: 40 additions & 0 deletions ebooks/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
body {
font-family: 'Garamond', serif;
font-size: 18px;
line-height: 1.5;
/* increase the kerning a bit */
letter-spacing: 1px;
}

hr, .page-break {
page-break-after: always;
}

li {
list-style-type: none;
}

/* Content Centering on the page.
Only one of these should be used at a time.
*/
/* body {
display: flex;
align-items: center;
min-height: 100vh;
text-align: center;
} */

/* This is not currently applied to blockquotes and more complex pages. */
.center-page {
display: flex;
align-items: center;
min-height: 100vh;
max-height: 70ch;
text-align: center;
justify-content: center;
}

.center-page em,
.center-page strong {
align-self: center;
}
Binary file added ebooks/wisdom.epub
Binary file not shown.
18 changes: 10 additions & 8 deletions wisdom.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
---
Author: Merlin Mann
title: Merlin's Wisdom Project
author: Merlin Mann
---

<!-- hello, world 2022-10-11 -->
<!-- hi again 2024-06-13 -->
<!-- still here 2025-07-07 -->

# Merlin's Wisdom Project

![Your Author](/i/cover.jpg)
![Your Author](i/cover.jpg)


Or: “*Everybody likes being given a glass of water*.”
Expand All @@ -22,15 +24,15 @@ It's only advice for you because it **had** to be advice for me.

## Epigraphs

> Yet here, Laertes! aboard, aboard, for shame!
> The wind sits in the shoulder of your sail,
> Yet here, Laertes! aboard, aboard, for shame!
> The wind sits in the shoulder of your sail,
> And you are stay'd for.

— *Hamlet*, Act 1, Scene 3.

----

<!--
<!--

TODO needs better citation

Expand All @@ -56,7 +58,7 @@ TODO needs better citation

----

![Something Like Advice](/i/toot-advice.jpg "'Good advice helps you find the solution to your problem. Great advice helps you find you were solving the wrong problem.'")
![Something Like Advice](i/toot-advice.jpg "'Good advice helps you find the solution to your problem. Great advice helps you find you were solving the wrong problem.'")

----

Expand Down Expand Up @@ -246,7 +248,7 @@ Brief introductory remarks regarding the Project:

----

- Everybody is doing the best they can each day. Even though what they can do is rarely enough.
- Everybody is doing the best they can each day. Even though what they can do is rarely enough.
- To entertain a child, it helps to know which things delight them and which things terrify them.
- Related: most kids can be surprisingly entertained by your making them just a little bit terrified.
- Pay attention to the times of day when you tend to have the most and the least energy. Schedule your future days accordingly.
Expand Down Expand Up @@ -557,4 +559,4 @@ Brief introductory remarks regarding the Project:

[**Merlin Mann**](http://hotdogsladies.omg.lol "Merlin's current place for places") is a podcaster and retired project manager who lives in San Francisco.

![Your Author (detail)](/i/MANN-angry-2500.jpg "Your Author")
![Your Author (detail)](i/MANN-angry-2500.jpg "Your Author")