Skip to content

Commit 1c9660c

Browse files
authored
Merge pull request #141 from KubaO/staging
Render the online site, offline site, and a pdf "site" that'll be fed to paged.js
2 parents 53f04dd + f35538a commit 1c9660c

24 files changed

Lines changed: 5935 additions & 143 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.claude/
22
CLAUDE.md
33
/.htmltest.yml
4+
node_modules/

BOOKPLAN.md

Lines changed: 578 additions & 0 deletions
Large diffs are not rendered by default.

WIP.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,16 +420,18 @@ WIP.md itself (and other files outside `docs/`) is not part of the Jekyll site a
420420

421421
## Scripts and tooling
422422

423-
Any new helper script (content conversion, link checks beyond `check.bat`, etc.) should be written in **Python**. Do not add new Ruby code to this repo. The only Ruby allowed is the existing Jekyll/`just-the-docs` build chain (`Gemfile`, `Gemfile.lock`, `_plugins/`) — that stays as-is. The one carve-out is `_plugins/offlinify.rb`, the link rewriter that powers the offline build (see [Build / preview](#build--preview)); future build-time concerns that are tightly coupled to Jekyll's internal model may go there too, but anything that can stand alone should still be Python.
423+
**Anything that participates in rendering the online site, the offline site, or the PDF book is handled by Jekyll** — Liquid templates, includes, layouts, data files (`_data/*.yml`), and Ruby plugins under `_plugins/`. Build-time concerns tightly coupled to Jekyll's internal model (URL knowledge, page model, render order, `site.data` injection) belong in a `_plugins/*.rb` plugin. Existing examples: `_plugins/offlinify.rb` (the offline-site link rewriter) and `_plugins/build-info.rb` (the git commit-hash capture that stamps the PDF title page). Adding a Python pre-build step that writes a YAML file into `_data/` and then invokes Jekyll is **not** the way — `bundle exec jekyll build` and `book.bat` must remain self-contained.
424+
425+
Python scripts are reserved for non-render concerns: one-off content conversion (e.g. `scripts/convert_em_dash_separators.py`), repo audits, dev tooling, link checks beyond `check.bat`, anything that runs *outside* a Jekyll build. They should never be a prerequisite for the render pipeline.
424426

425427
## Build / preview
426428

427429
From `docs/`:
428430

429-
- `bundle exec jekyll build` (or `build.bat`) — builds the online copy to `_site/` **and** a `file://`-browsable copy to `_site-offline/` in a single Jekyll run. The offline pass adds ~3-5s on top of the normal ~13s build; activated by `also_build_offline: true` in `_config.yml`. After Jekyll's WRITE phase, `_plugins/offlinify.rb` walks `_site/`, copies binary assets verbatim into `_site-offline/`, and for each HTML and CSS file rewrites every root-absolute `href` / `src` / `url()` to a page-relative path with the resolved file extension (`/FAQ` → `../../FAQ.html`, `/Tutorials/CEF/` → `../../Tutorials/CEF/index.html`). It also patches the offline copy of `assets/js/just-the-docs.js` in two places — `navLink()` to match the active nav entry by resolved DOM `link.href` rather than `document.location.pathname` (the upstream pathname-vs-attribute compare returns no match under `file://`, leaving the sidebar with no `.active` class so the nav appears collapsed on every navigation), and `initSearch()` to read the lunr index from `window.SEARCH_DATA` rather than fetching `search-data.json` over `XMLHttpRequest` (XHR to `file://` resources is blocked by browsers; classic `<script src=>` is not). To support that, the plugin (a) generates `_site-offline/assets/js/search-data.js` once per build by wrapping the rendered `search-data.json` in `window.SEARCH_DATA = {...};`, and (b) injects two `<script>` tags per page right before `just-the-docs.js`: one that sets `window.OFFLINE_SITE_ROOT` to the per-page relative prefix to the offline site root, and one that loads `search-data.js`. The patched `initSearch()` rewrites every `doc.url` from a root-absolute permalink (`/tB/Core/Const`) to a page-relative path (`<OFFLINE_SITE_ROOT>tB/Core/Const.html`) so search-result clicks land on the actual file regardless of which page the user is on.
431+
- `bundle exec jekyll build` (or `build.bat`) — builds three trees in a single Jekyll run: the online copy at `_site/`, a `file://`-browsable copy at `_site-offline/`, and the sparse pagedjs source at `_site-pdf/`. The offline pass (`_plugins/offlinify.rb`, activated by `also_build_offline: true` in `_config.yml`) adds ~3-5s and the PDF pass (`_plugins/pdfify.rb`, activated by `also_build_pdf: true`) adds <1s on top of the normal ~13s build. The PDF plugin copies `_site/book.html` (the concatenated chapter document rendered via `_layouts/book-combined.html`) verbatim into `_site-pdf/`, along with `assets/css/print.css`, `assets/css/rouge.css`, and every relative `<img src=>` target -- just what pagedjs needs to render the book PDF. After the copy, the plugin deletes `_site/book.html`: the concatenated document is a build artifact for the PDF render path alone, not a public page on the online site. The companion `offline_exclude: [..., book.html]` entry in `_config.yml` keeps `offlinify.rb` from copying it into `_site-offline/`. The two safeguards are independent -- the exclude pattern works regardless of whether offlinify walks `_site/` before or after pdfify's delete, and pdfify's delete works regardless of whether offlinify is enabled. After Jekyll's WRITE phase, the offline plugin walks `_site/`, copies binary assets verbatim into `_site-offline/`, and for each HTML and CSS file rewrites every root-absolute `href` / `src` / `url()` to a page-relative path with the resolved file extension (`/FAQ` → `../../FAQ.html`, `/Tutorials/CEF/` → `../../Tutorials/CEF/index.html`). It also patches the offline copy of `assets/js/just-the-docs.js` in two places — `navLink()` to match the active nav entry by resolved DOM `link.href` rather than `document.location.pathname` (the upstream pathname-vs-attribute compare returns no match under `file://`, leaving the sidebar with no `.active` class so the nav appears collapsed on every navigation), and `initSearch()` to read the lunr index from `window.SEARCH_DATA` rather than fetching `search-data.json` over `XMLHttpRequest` (XHR to `file://` resources is blocked by browsers; classic `<script src=>` is not). To support that, the plugin (a) generates `_site-offline/assets/js/search-data.js` once per build by wrapping the rendered `search-data.json` in `window.SEARCH_DATA = {...};`, and (b) injects two `<script>` tags per page right before `just-the-docs.js`: one that sets `window.OFFLINE_SITE_ROOT` to the per-page relative prefix to the offline site root, and one that loads `search-data.js`. The patched `initSearch()` rewrites every `doc.url` from a root-absolute permalink (`/tB/Core/Const`) to a page-relative path (`<OFFLINE_SITE_ROOT>tB/Core/Const.html`) so search-result clicks land on the actual file regardless of which page the user is on.
430432
- `bundle exec jekyll serve` (or `serve.bat`) — local server at `localhost:4000`. Note that `_site-offline/` is also produced on the initial build, but live-reload only updates `_site/`; manual rebuild needed for offline updates.
431433
- `check.bat` — link check (offline Lychee against `_site/`).
432-
- `build-offline.bat`produce **only** the offline copy, writing directly to `_site-offline/` (no `_site/` is generated). Layers `_config_offline.yml` over `_config.yml` to set `offline_build: true` (activates the plugin's standalone in-place mode) and override `also_build_offline: false`. The output tree is byte-equivalent to what the combined build writes to `_site-offline/` — same URL rewriting, same JS patches, same offline-search wiring. Faster than the combined build when only the offline copy is wanted (no `_site/` rendered, no per-file copy step). Useful for shipping just the offline copy as a downloadable bundle.
434+
- `book.bat`renders the PDF from `_site-pdf/book.html` via `pagedjs-cli` into `_pdf/book.pdf`. Run `build.bat` first to populate `_site-pdf/`.
433435

434436
## Site integrity check
435437

docs/.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
_site
2-
_site-offline
1+
_site/
2+
_site-offline/
3+
_site-pdf/
4+
_pdf/
35
.sass-cache
46
.jekyll-cache
57
.jekyll-metadata

docs/Miscellaneous/Documentation Development.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,15 @@ Also ensure that Jekyll is in the PATH. To adjust the path on Windows, press <kb
183183

184184
### Building
185185

186-
To build the documentation, i.e. render it from `.md` files to the `_site` folder:
186+
To build the documentation, i.e. render it from `.md` files into the `_site/` (online), `_site-offline/` (offline mirror), and `_site-pdf/` (sparse PDF source) folders:
187187

188188
bundle exec jekyll build
189189

190190
or, on Windows only:
191191

192192
build.bat
193193

194-
To produce **only** the offline-browsable copy (no `_site/`), writing directly to `_site-offline/`:
195-
196-
build-offline.bat
194+
A single Jekyll run produces all three trees; toggle `also_build_offline` / `also_build_pdf` in `_config.yml` to skip a sibling output if you only want `_site/`.
197195

198196
### Checking Link Integrity
199197

docs/_config.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,22 @@ gh_offline_link_url: "https://github.com/twinbasic/documentation/releases/latest
134134
# the online _site/ output is unaffected.
135135
also_build_offline: true
136136

137+
# When true, the Pdfify plugin (_plugins/pdfify.rb) runs at the end of
138+
# the build and emits a sparse _site-pdf/ tree containing only the
139+
# files pagedjs-cli needs: book.html, the two stylesheets the book
140+
# layout links (print.css, rouge.css), and every image referenced
141+
# from book.html. book.bat then renders the PDF from _site-pdf/book.html.
142+
# Adds <1s to the build (it's a few file copies); the online _site/
143+
# output is unaffected.
144+
also_build_pdf: true
145+
137146
# Patterns for files Jekyll produces in _site/ that have no purpose
138147
# in the offline tree -- Pages / crawler metadata, jekyll-redirect-
139148
# from output, Windows batch scripts Jekyll picks up from the source
140-
# directory. The online _site/ keeps them; offlinify strips them
141-
# from _site-offline/.
149+
# directory, and the concatenated `book.html` that exists only to
150+
# feed `_plugins/pdfify.rb` (which copies it to _site-pdf/ and deletes
151+
# the _site/ copy). The online _site/ keeps the metadata files;
152+
# offlinify strips them from _site-offline/.
142153
#
143154
# Patterns are File.fnmatch-style with FNM_PATHNAME, matched against
144155
# each file's path relative to the site root. `*` does NOT cross
@@ -148,6 +159,7 @@ offline_exclude:
148159
- CNAME
149160
- robots.txt
150161
- sitemap.xml
162+
- book.html
151163

152164
# Excludes for both the build (Jekyll won't try to process these as
153165
# source) and the watcher (`jekyll serve` won't trigger a rebuild
@@ -157,5 +169,6 @@ offline_exclude:
157169
# rebuild loop. Keep this in sync with also_build_offline above.
158170
exclude:
159171
- _site-offline
172+
- _site-pdf
160173
- redirects.json
161174
- "*.bat"

docs/_config_offline.yml

Lines changed: 0 additions & 27 deletions
This file was deleted.

docs/_data/book.yml

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# Chapter manifest for the twinBASIC documentation PDF.
2+
#
3+
# `front_matter` is an ordered list of sections that emit between the
4+
# title page and the first numbered Part. No divider page, no part
5+
# number; each entry just contributes its chapter(s) inline. Each
6+
# entry has:
7+
# title -- the conceptual section name (used by future TOC and as
8+
# the anchor seed when the URL would otherwise produce an
9+
# empty anchor, e.g. the root `/` page).
10+
# page -- single absolute URL (alternative to `prefixes`); use
11+
# when the section is exactly one page (the root index).
12+
# prefixes -- URL-prefix list (alternative to `page`); same shape as
13+
# on a numbered part.
14+
#
15+
# `parts` is an ordered list of numbered book parts. Each part has:
16+
# title -- shown on the part divider page and in the global TOC.
17+
# subtitle -- optional second-line descriptor on the divider.
18+
# prefixes -- one or more URL prefixes; book.html gathers every
19+
# page in `site.pages` whose URL contains a prefix,
20+
# sorts by URL, and emits each as a chapter. Used by
21+
# "flat" parts that map directly to a folder of pages.
22+
# page -- single absolute URL alternative to `prefixes`, used
23+
# for one-chapter parts (e.g. the FAQ). Match is
24+
# exact, not contains-based, so a one-page section
25+
# doesn't accidentally sweep in siblings.
26+
# foreword_page -- single absolute URL emitted as a `<article
27+
# class="part-foreword">` right after the part
28+
# divider, before any chapter dividers. No running
29+
# header on these pages (CSS suppresses chrome via a
30+
# named page). Used by the Packages part to surface
31+
# the `/tB/Packages/` landing as introductory
32+
# material for the whole section.
33+
# chapters -- ordered list of per-chapter entries (1.9). Each
34+
# chapter has its own divider page and its own
35+
# chapter-level content; the part becomes a
36+
# container for several distinct subjects rather
37+
# than a flat folder. Each entry has:
38+
# title -- the chapter divider title.
39+
# subtitle -- optional descriptor.
40+
# landing_page -- the chapter's intro page; always
41+
# emitted first within the
42+
# chapter, and the plugin strips
43+
# its source H1 so the chapter
44+
# divider's H2 is the sole
45+
# outline entry for the chapter.
46+
# prefixes -- additional URL prefixes for
47+
# content pages (members, etc.);
48+
# sorted by URL after the
49+
# landing. A page that matches
50+
# a prefix AND is the landing is
51+
# emitted only once (as the
52+
# landing).
53+
#
54+
# Index pages (URL ending in `/`) sort before sibling content pages
55+
# under ASCII lexicographic order, so each part naturally opens with
56+
# its package landing page followed by the individual symbols.
57+
58+
front_matter:
59+
- title: Introduction
60+
page: /
61+
62+
parts:
63+
- title: Features
64+
subtitle: Language additions, attributes, tooling, and packaging
65+
prefixes:
66+
- /Features/
67+
68+
- title: Frequently Asked Questions
69+
subtitle: Status, scope, and how to get help
70+
page: /FAQ
71+
72+
- title: Tutorials
73+
subtitle: Worked examples for CEF, WebView2, and CustomControls
74+
prefixes:
75+
- /Tutorials/
76+
77+
- title: The Core Language
78+
subtitle: Statements, operators, and built-in keywords
79+
prefixes:
80+
- /tB/Core/
81+
82+
- title: Reference Section
83+
subtitle: Alphabetical indexes of statements, operators, and compiler constants, plus the project glossary
84+
# The live site has a top-level "Reference Section" nav item that groups
85+
# these alphabetical-index pages. The five Reference/* pages plus the
86+
# Controls table and the Glossary live at three different URL prefixes;
87+
# `contains`-match catches all of them. (Attributes also belongs here in
88+
# the live nav but its permalink is /tB/Core/Attributes, which the Core
89+
# part above already sweeps up; we leave it there rather than introduce
90+
# an excludes schema for one page.)
91+
prefixes:
92+
- /Reference
93+
- /tB/Controls
94+
- /tB/Gloss
95+
96+
- title: Packages
97+
subtitle: The runtime and library packages shipped with twinBASIC
98+
# The /tB/Packages/ landing page is the part's foreword; its content
99+
# (an overview of all built-in packages) sits between the part divider
100+
# and the first chapter divider, with no running header. Each package
101+
# below becomes a chapter with a dedicated chapter-divider title
102+
# page; the chapter's own source landing page emits next (with its
103+
# source H1 stripped by the plugin to avoid the redundancy with the
104+
# chapter divider's H2), followed by the package's class / module /
105+
# control pages in URL order.
106+
foreword_page: /tB/Packages/
107+
chapters:
108+
- title: VBA Package
109+
subtitle: Standard runtime modules --- Strings, Math, FileSystem, and the rest
110+
# The VBA landing lives at /tB/Packages/VBA (added when Packages
111+
# was promoted to a top-level nav item), while individual module
112+
# members keep their legacy /tB/Modules/... URLs.
113+
landing_page: /tB/Packages/VBA
114+
prefixes:
115+
- /tB/Modules/
116+
117+
- title: VBRUN Package
118+
subtitle: Runtime types for controls, errors, and the property bag
119+
landing_page: /tB/Packages/VBRUN/
120+
prefixes:
121+
- /tB/Packages/VBRUN/
122+
123+
- title: VB Package
124+
subtitle: Classic VB6 forms and intrinsic controls
125+
landing_page: /tB/Packages/VB/
126+
prefixes:
127+
- /tB/Packages/VB/
128+
129+
- title: WebView2 Package
130+
subtitle: Chromium-based browser control via Microsoft Edge WebView2
131+
landing_page: /tB/Packages/WebView2/
132+
prefixes:
133+
- /tB/Packages/WebView2/
134+
135+
- title: Assert Package
136+
subtitle: Test assertions with Exact, Strict, and Permissive comparison
137+
landing_page: /tB/Packages/Assert/
138+
prefixes:
139+
- /tB/Packages/Assert/
140+
141+
- title: CustomControls Package
142+
subtitle: The WaynesControls suite and its supporting framework
143+
landing_page: /tB/Packages/CustomControls/
144+
prefixes:
145+
- /tB/Packages/CustomControls/
146+
147+
- title: CEF Package
148+
subtitle: Chromium Embedded Framework browser control
149+
landing_page: /tB/Packages/CEF/
150+
prefixes:
151+
- /tB/Packages/CEF/
152+
153+
- title: WinEventLogLib Package
154+
subtitle: Windows Event Log integration
155+
landing_page: /tB/Packages/WinEventLogLib/
156+
prefixes:
157+
- /tB/Packages/WinEventLogLib/
158+
159+
- title: WinNamedPipesLib Package
160+
subtitle: Asynchronous named-pipe framework over IOCP
161+
landing_page: /tB/Packages/WinNamedPipesLib/
162+
prefixes:
163+
- /tB/Packages/WinNamedPipesLib/
164+
165+
- title: WinServicesLib Package
166+
subtitle: Windows Services hosting
167+
landing_page: /tB/Packages/WinServicesLib/
168+
prefixes:
169+
- /tB/Packages/WinServicesLib/
170+
171+
- title: tbIDE Package
172+
subtitle: IDE Extensibility --- the addin SDK
173+
landing_page: /tB/Packages/tbIDE/
174+
prefixes:
175+
- /tB/Packages/tbIDE/
176+
177+
- title: WinNativeCommonCtls Package
178+
subtitle: VB6-compatible Common Controls replacement on top of Win32 ComCtl32
179+
landing_page: /tB/Packages/WinNativeCommonCtls/
180+
prefixes:
181+
- /tB/Packages/WinNativeCommonCtls/

0 commit comments

Comments
 (0)