spread the demo's features across the pages that would carry them

notes-on-water had become a kitchen sink: every feature from the last eight
loops on one page, which demonstrates the engine and misrepresents the site.

Now each lands where a real site would put it. The text marks, the abbreviation,
authored <kbd> and the margin note go to writing/typography, which is already
the page about what the engine does to words. Icons and the grouped panels go to
pages/colophon, which is the page about the theme — install instructions are
what tabs are actually for. about gets a lone expando, the-flood gets a chapter
note, and notes-on-water keeps what it is really for: the include and its
namespaced footnotes, the gauge table, the glossary and a contents list over its
own headings.

Seven bundles carry features now instead of two, and the demo cases moved with
them, so each still names the page it proves.
This commit is contained in:
Claude Opus 5
2026-08-01 22:22:40 +06:00
committed by bdeshi
parent fdc76ba1b9
commit 91c733a0e0
8 changed files with 75 additions and 54 deletions
+3 -1
View File
@@ -66,7 +66,9 @@ is the accepted format, not a list of what runs.
`examples/demo-site/` is a complete site kept in the repository to be read and served — 30 bundles across six
sections, one case per feature in `TestTheExampleSiteExercisesEveryFeature`, and `khosra check` run over it by
`verify.sh` (ADR-0051). `make demo` serves it.
`verify.sh` (ADR-0051). `make demo` serves it. Features are spread across the bundles where a real site would
put them rather than piled on one page: text marks and the margin note on `writing/typography`, icons and
panels on `pages/colophon`, the include, table, glossary and contents list on `writing/notes-on-water`.
A `Dockerfile` ships the binary alone: the site root is a mounted volume, never copied in (ADR-0010, ADR-0011).
+1 -1
View File
@@ -314,7 +314,7 @@ chrome.go 110 · render.go 483 · view.go 130
- view.go:111 type Item struct
- view.go:122 type Alternate struct
## internal/web — 734 lines + 1619 test
## internal/web — 734 lines + 1622 test
asset.go 58 · discover.go 71 · extras.go 93 · feed.go 125 · resolve.go 170 · web.go 217
@@ -9,3 +9,7 @@ Chapter two, written as a directory bundle so it can own the picture below.
::figure{src=water.jpg alt="A wall of grey water" caption="Day three"}
It has a chapter on either side, so the sequence nav shows both, plus its position in the series.
:::note{title="On the timeline"}
Day three by the gauge, though the chapter order is set in frontmatter rather than by date.
:::
@@ -7,3 +7,8 @@ a publication date, which is a fact already on disk.
Every file under `examples/demo-site/` in the khosra repository is here to be *served* and read. If the engine
can do something this site does not show, that is a gap in the demo; if the demo asks for something the engine
cannot do, that is a gap in the engine.
:::details{summary="What is deliberately not shown"}
Anything the engine cannot do yet. The demo is a test as much as a brochure, so an empty section here would
be a failing case rather than a tidy page.
:::
@@ -6,3 +6,18 @@ Built by khosra: one Go binary, four dependencies, no JavaScript anywhere in the
The stylesheet is inlined into every page, so a site root with no `static/` still renders. A real theme would
replace it, and `templates/list.html` in this site root shows how — that one file overrides a single named
block and inherits the rest of the document.
:tip: An icon is a name the engine hands to the theme; this reference theme answers with a character, while a
theme of your own might answer with an SVG. A name it does not know, like :nosuchicon:, keeps its text.
:::details{group=run summary="Serve the demo" open=yes}
`make demo` — this site, on `localhost:8080`.
:::
:::details{group=run summary="Serve your own"}
`khosra -site /path/to/site`. Grouped panels open one at a time, and nothing here runs a script.
:::
:::tip{title="Nothing is hidden"}
Panels are `<details>`, which every browser opens without help. A reader with scripting off sees the same page.
:::
@@ -14,50 +14,26 @@ This bundle also has an `extras/` directory, so the theme offers a link to it at
::toc
## The dialect
## Readings
The page has a footnote of its own[^page], a table, a definition list and ~~a struck phrase~~.
The notation marks cover what prose needs most often: the gauge measures H~2~O to about 10^-3^ m, and the
==important== column is the second. Where there is no mark for something, HTML does it: the reading is taken
with <kbd>Shift</kbd> held. Content in the site root is the author's own, so it renders rather than being
dropped. Readings go to the NIWA archive, and the definition below expands every use of it.
*[NIWA]: National Institute of Water and Atmospheric Research
:::warn{title="Calibration"}
The south gauge reads low after a storm, so readings are corrected against the north one. A container's body
is Markdown: *emphasis*, links and H~2~O all work inside it.
:::
:warn: The south gauge reads low after a storm — the theme decides what that icon is, and an unknown one like
:nosuchicon: keeps its text. Times like 10:30:15 and pairs like key:value:pair are left alone.
The page has a footnote of its own[^page], numbered from one independently of the fragment's.
| Gauge | Reading | Note |
|-------|---------|------|
| North | 41 mm | steady |
| South | 12 mm | falling |
:::aside{title="On the gauges"}
Both were replaced in 2024, which is why the older readings are noisier.
:::warn{title="Calibration"}
The south gauge reads low after a storm, so readings are corrected against the north one. A container's body
is Markdown, so *emphasis* and links work inside it.
:::
:::details{summary="Show the raw transcription"}
Twelve pages of it, kept out of the way until asked for.
:::
:::details{group=readings summary="North gauge" open=yes}
41 mm, steady through the week.
:::
:::details{group=readings summary="South gauge"}
12 mm, falling. Grouped panels open one at a time, with no script.
:::
## Terms
Monsoon
: The season these readings belong to.
Gauge
: A calibrated vessel, read by eye.
: A calibrated vessel, read by eye. Both were replaced in 2024, which is why the older readings are noisier.
[^page]: Numbered from one, independently of the fragment's.
@@ -9,9 +9,25 @@ is the whole of what the engine does to an author's words: a Markdown parser opt
Inside a code span none of it happens: `"quotes" -- and ellipses...` survive byte for byte, because the
typographer works on the parsed tree rather than on the text.
## Marks the dialect adds
Where prose needs more than CommonMark offers there are four marks: water is H~2~O, a millimetre is 10^-3^ m,
the ==second column== is the one that matters, and ~~this phrase~~ was cut. A subscript holds a formula and
never a phrase, so `x^2 + y^2` stays exactly as typed.
Text in NFC is what the engine stores, and every use of that term expands.
*[NFC]: Normalization Form C
Where there is no mark for something, HTML does it: this reading is taken with <kbd>Shift</kbd> held.
:::aside{title="On line breaking"}
Widows are the browser's problem, not the engine's — `text-wrap: pretty` knows where the lines fall and no
Markdown transform does.
:::
Line breaking is not the engine's business at all. This paragraph is deliberately long enough to wrap several
times on a narrow screen, which is where you would notice a lonely last word — and the stylesheet asks the
browser to avoid one with `text-wrap: pretty`, because only the browser knows where the lines actually fall.
times on a narrow screen, which is where you would notice a lonely last word.
A code block, indented.
Also untouched.
+22 -19
View File
@@ -119,27 +119,30 @@ var exampleFeatures = []featureCase{
{what: "an include is parsed as Markdown, and its fragment has no page of its own", path: "/writing/notes-on-water/", code: 200,
expect: []string{`<h2 id="method">Method</h2>`, "<em>Emphasis and links survive</em>"}},
{what: "a fragment is not a bundle", path: "/writing/notes-on-water/_method/", code: 404},
{what: "authored HTML renders, because the site root is trusted", path: "/writing/notes-on-water/", code: 200,
{what: "notation marks become elements, and a single tilde is a subscript not a strike", path: "/writing/typography/", code: 200,
expect: []string{"H<sub>2</sub>O", "10<sup>-3</sup>", "<mark>second column</mark>", "<del>this phrase</del>"},
absent: []string{"<del>2</del>", "<sup>2 + y</sup>"}},
{what: "an abbreviation expands and its definition line renders nothing", path: "/writing/typography/", code: 200,
expect: []string{`<abbr title="Normalization Form C">NFC</abbr>`}, absent: []string{"*[NFC]"}},
{what: "authored HTML renders, because the site root is trusted", path: "/writing/typography/", code: 200,
expect: []string{"<kbd>Shift</kbd>"}, absent: []string{"raw HTML omitted"}},
{what: "notation marks become elements, and a single tilde is a subscript not a strike", path: "/writing/notes-on-water/", code: 200,
expect: []string{"H<sub>2</sub>O", "10<sup>-3</sup>", "<mark>important</mark>", "<del>a struck phrase</del>"},
absent: []string{"<del>2</del>"}},
{what: "an abbreviation expands and its definition line renders nothing", path: "/writing/notes-on-water/", code: 200,
expect: []string{`<abbr title="National Institute of Water and Atmospheric Research">NIWA</abbr>`},
absent: []string{"*[NIWA]"}},
{what: "panels and margin notes need no script", path: "/writing/notes-on-water/", code: 200,
expect: []string{"<summary>Show the raw transcription</summary>", `<details name="readings"`,
`<aside class="side">`, `<p class="side-title">On the gauges</p>`},
absent: []string{"<script"}},
{what: "a table of contents links the page's own headings", path: "/writing/notes-on-water/", code: 200,
expect: []string{`<nav class="toc">`, `<a href="#the-dialect">The dialect</a>`, `class="toc-2"`}},
{what: "a margin note is a container the theme places", path: "/writing/typography/", code: 200,
expect: []string{`<aside class="side">`, `<p class="side-title">On line breaking</p>`}},
{what: "an icon renders through the theme, and an unknown one keeps its text", path: "/pages/colophon/", code: 200,
expect: []string{"💡", ":nosuchicon:"}},
{what: "grouped panels are tabs, and need no script", path: "/pages/colophon/", code: 200,
expect: []string{`<details name="run" open>`, "<summary>Serve your own</summary>"}, absent: []string{"<script"}},
{what: "a lone panel is an expando", path: "/pages/about/", code: 200,
expect: []string{"<details>", "<summary>What is deliberately not shown</summary>"}},
{what: "an admonition renders inside a comic chapter too", path: "/comics/the-long-monsoon/the-flood/", code: 200,
expect: []string{`<aside class="admonition note">`, `<p class="admonition-title">On the timeline</p>`}},
{what: "the dialect renders tables and definition lists", path: "/writing/notes-on-water/", code: 200,
expect: []string{"<table>", "<th>Gauge</th>", "<dl>", "<dt>Monsoon</dt>"}},
{what: "a container renders its body through the theme fragment", path: "/writing/notes-on-water/", code: 200,
expect: []string{`<aside class="admonition warn">`, `<p class="admonition-title">Calibration</p>`,
"<em>emphasis</em>", "H<sub>2</sub>O"}, absent: []string{":::"}},
{what: "an icon renders through the theme, and prose colons are untouched", path: "/writing/notes-on-water/", code: 200,
expect: []string{"⚠️", ":nosuchicon:", "10:30:15", "key:value:pair"}},
{what: "the dialect renders tables, definition lists and strikethrough", path: "/writing/notes-on-water/", code: 200,
expect: []string{"<table>", "<th>Gauge</th>", "<dl>", "<dt>Monsoon</dt>", "<del>a struck phrase</del>"}},
expect: []string{`<aside class="admonition warn">`, `<p class="admonition-title">Calibration</p>`, "<em>emphasis</em>"},
absent: []string{":::"}},
{what: "a table of contents links the page's own headings", path: "/writing/notes-on-water/", code: 200,
expect: []string{`<nav class="toc">`, `<a href="#readings">Readings</a>`, `class="toc-2"`}},
{what: "a fragment's footnote ids are namespaced, so the page's own keep working", path: "/writing/notes-on-water/", code: 200,
expect: []string{`id="fn:1"`, `id="_method-fn:1"`, `href="#_method-fn:1"`}},
{what: "a page offers its extras only when it has them", path: "/writing/notes-on-water/", code: 200,