Skip to content
Merged
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.4.6] - 2026-03-06

### Added

- Configurable characters per page in reader mode via `reader.charsPerPage` in `config.yaml`. Users may not need to change this at all. But the idea is to keep it configurable in case there are some visual artifacts in the reader mode. This is responsible for splitting the pages for the reader mode.

### Fixed

- Reader mode now correctly paginates lists, code blocks, and blockquotes instead of truncating them mid-element.
- Body of Work page now appears on first deploy when using a custom slug; reliably.

## [1.4.5] - 2026-03-05

### Fixed
Expand Down
1 change: 1 addition & 0 deletions build/defaults/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ exclude:
pieces:
-
reader:
charsPerPage: 2200
order:
default: descending
rss:
Expand Down
3 changes: 2 additions & 1 deletion build/generate-body-of-work.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const config = yaml.load(fs.readFileSync(configPath, 'utf-8')) as {
};
};

const bodyOfWorkSlug = config?.bodyOfWork?.slug || 'body-of-work';

type Piece = {
slug: string;
title: string;
Expand Down Expand Up @@ -73,7 +75,6 @@ const sortedKeys = Object.keys(grouped).sort((a, b) => {
});

const bodyOfWorkTitle = config?.bodyOfWork?.title || 'Body of Work';
const bodyOfWorkSlug = config?.bodyOfWork?.slug || 'body-of-work';
const bodyOfWorkFilePath = path.join(publicDir, 'content', 'pages', `${bodyOfWorkSlug}.md`);

let markdown = '---\n';
Expand Down
51 changes: 7 additions & 44 deletions build/paginate-pieces.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import fs from 'fs';
import path from 'path';
import fm from "front-matter";
import yaml from 'js-yaml';
import { chunkContent } from './utils/markdown-chunker';

const publicDir = path.join(__dirname, '..', 'public');
const piecesPath = path.join(publicDir, 'content', 'pieces');
const pagesIndexPath = path.join(publicDir, 'generated', 'index', 'pieces-pages.json');
const piecesIndexPath = path.join(publicDir, 'generated', 'index', 'pieces.json');
const configPath = path.join(publicDir, 'config.yaml');

const CHARS_PER_PAGE = 2200;
const configRaw = fs.readFileSync(configPath, 'utf-8');
const config = yaml.load(configRaw) as any;
const CHARS_PER_PAGE = config?.reader?.charsPerPage ?? 2200;

type PiecePage = {
pieceSlug: string;
Expand All @@ -23,49 +28,6 @@ type PiecePageIndex = {
};
};

function chunkContent(content: string, charsPerPage: number): string[] {
const chunks: string[] = [];
const paragraphs = content.split('\n\n');

let currentChunk = '';

for (const paragraph of paragraphs) {
const trimmedParagraph = paragraph.trim();
if (!trimmedParagraph) continue;

if (currentChunk.length > 0 && (currentChunk.length + trimmedParagraph.length + 2) > charsPerPage) {
chunks.push(currentChunk.trim());
currentChunk = '';
}

if (trimmedParagraph.length > charsPerPage) {
const sentences = trimmedParagraph.match(/[^.!?]+[.!?]+/g) || [trimmedParagraph];

for (const sentence of sentences) {
if (currentChunk.length > 0 && (currentChunk.length + sentence.length) > charsPerPage) {
chunks.push(currentChunk.trim());
currentChunk = sentence;
} else {
currentChunk += sentence;
}
}
currentChunk += '\n\n';
} else {
currentChunk += trimmedParagraph + '\n\n';
}
}

if (currentChunk.trim()) {
chunks.push(currentChunk.trim());
}

if (chunks.length === 0) {
chunks.push(content);
}

return chunks;
}

const piecesIndexRaw = fs.readFileSync(piecesIndexPath, 'utf-8');
const piecesIndex = JSON.parse(piecesIndexRaw);

Expand Down Expand Up @@ -104,3 +66,4 @@ piecesIndex.forEach((piece: any) => {

fs.writeFileSync(pagesIndexPath, JSON.stringify(pageIndex, null, 2));
console.log(`[pagination]: created pieces-pages.json with ${Object.keys(pageIndex).length} pieces`);

Loading