v0.5.0
Fixed
- Hero terminal: the section switch reaches the terminal. v0.4.0 taught the hero
that its "view projects" button must not point at a section that does not
render, but the terminal directly above it went on typingls projects/and
listing the repositories of a section the reader could not scroll to. Both
remaining commands now follow the plan:ls projects/follows theprojects
section, andcat stack.txtfollowsabout, which is where the skills live. - Hero terminal: a command with no output is no longer typed at all. Only the
blog listing had ever followed that rule;cat stack.txtandls projects/
were unconditional, so a site that filled nothing in — the one CI has been
building all along — greeted its reader with two commands and two blank lines
under them. This half is fixed in the script rather than the template, so an
empty value produces no command whatever put it there.whoamiis the stated
exception and always runs: its output is the page's own identity, not a
section's data, so no switch can empty it. - Hero terminal: the reserved height counts the rows the text takes, not the
lines it is written in.--hero-lineswas a count of logical lines, and
.term__bodywraps — so on a 360px phone five of the exampleSite's fifteen
lines take two rows each, the box reserved 369px for 480px of text, and it grew
under the reader line by line as the animation typed: the exact shift the
reservation exists to prevent, on the viewport where it is scored hardest. Only
the browser knows the box's width and the font's advance, soterminal.jsnow
measures both and writes the corrected count back before the first character is
typed — and again whenever the box changes size or JetBrains Mono arrives under
font-display:swap. Width is counted in terminal cells rather than characters,
so a full-width glyph costs the two it really takes: a Japanese subtitle would
otherwise model at half its width and the box would come out short again, which
is this bug wearing the fix as a disguise. The blinking cursor is counted too —
it belongs to no line of the script, and the closing prompt is where it comes to
rest. What the template emits is unchanged and becomes the floor: the number a
reader gets before the script runs, or without it. Not a regression from the
entry below: v0.4.0 reserved the identical height by a different route and
carried the samepre-wrap, so this is older than both. - Hero terminal: the reserved height follows the commands that actually render.
It was a constant12lines plus one per post, which was right only while all
three commands always rendered; drop one and the box reserved three lines it
never filled, leaving a block of dead space under the hero — the same class of
bug assection--lastsitting on a section that was no longer last. The
template now counts the lines it is about to emit and passes them as
--hero-lines, replacing the--hero-postsvariable and the
.term__body--postsclass, which are gone. CI recomputes the count from the
rendereddata-*attributes and holds the variable to it. - Config: a param written as the wrong type no longer takes the build down.
sections.htmlhas always stated the rule in its header — no configuration
may fail the build — and normalised the four section tables to keep it. But
every partial that readsite.Params.<x>straight bypassed that, so the
promise held in the one file that made it and nowhere else. Thirteen
configurations aborted a build:[params.hero],[params.contact],
[params.footer]and[params.navbar]written as scalars;
[params.footer.socialNetworks],[params.hero.socialLinks]and
[params.about.skills]the same one level down;itemswritten as a scalar
underabout.skills,projectsandexperience; ajobslist inside an
experience entry; and a list of bare strings whereprojects,experienceor
socialLinksexpect a list of tables. Each now warns, names the param, and
falls back.
Four of the thirteen broke every page, not the home alone —head.html,
footer.htmlandnav.htmlrun site-wide, so a scalarhero,contact,
footerornavbartook the 404 and every blog post with it.navbarwas
the sharpest of them:nav.htmlis the file the invariant names, and
sections.htmlalready guarded that same param for itsshowBloggate, so
the theme disagreed with itself about[params.navbar]in two files. - Config: three new partials carry that —
params-table.html,
params-list.htmlandparams-scalar.html— and every consumer reads through
them. A guard on a parent says nothing about its children, so nested tables go
through the same helper; that is the whole reason six of the thirteen existed.
Lists are tested for being lists rather than for truth:firstdoes not
reject a string, it slices its bytes, soitems = "Go"would have rendered
71 · 111instead of failing anywhere a consumer could see it. Entries
dropped from a list are counted in the warning, so a typo that silently
removes one project from six is reported rather than simply absent — and
entries are checked in both directions now, so a table written where a bare
skill belongs is dropped and counted rather than renderingmap[a:1]into a
skill chip. - Config: the mirror direction is covered too — a scalar-shaped param written as
a table or a list. It fails differently and had to be guarded separately:
these never reach arangeor a field lookup, they reachplainify,
relURL,absURL,markdownifyorurlize, all of which cast to string and
abort when the cast fails. Eight more configurations, on top of the thirteen
above:[params.description],[params.favicon]and[params.ogImage], read
inhead.html, so all three took whole sites down; a post's ownimage, from
its front matter, inhead.htmlforog:imageand again insingle.htmlfor
the featured banner; and the prose fields —[params.about] content, a
project entry'scontentandtitle, and an experience job'scontent.
The per-entry ones name the entry in the warning, byrepoor bytitleonce
the title itself has been through the guard, so a list of six says which one.
A param that is only ever printed is left alone on purpose:map[a:1]on the
page is wrong but does not stop the build, and warning about it would report
the same mistake twice for the reads that do go through a cast. - Config: gate 3 type-checks
[params.about] contentfor the same reason it
type-checks the lists — a table there is truthy, so it admitted the section
and then rendered it as an empty shell. - Config: a warning about an experience entry with no
companyno longer
degrades into printf's own error syntax.%qon a nil printed the warning as
items %!q(<nil>) jobs must be a list, turning the half that names the broken
entry into noise. CI now watches the warnings for printf garbage as well as
the pages, since the log is the only place this one could ever appear. - Config: gate 3 in
sections.htmlnow type-checks the list-shaped params as
well as testing them for emptiness. A scalaritemsis truthy, so it used to
pass the gate and reach the partial; with the partials guarded the build
survives, but the section would render with nothing in it — the defect v0.4.0
closed. The warning has to come from the gate for the same reason: once the
gate drops the section its partial never runs, so a guard that only warned
inside the partial would go quiet exactly when the config is wrong. - SEO: a site that never set
[params.hero] subtitleno longer publishes
<title>Site — %!s(<nil>)</title>. Go'sprintfhas no nil case for
%s, so the missing param was formatted straight into the page — on the home
page of every site that skipped it, including the bare site CI has been
building all along. Nothing warned, so--panicOnWarningcould not see it and
the build stayed green. The title now falls back to the site name alone. CI
asserts no built page contains printf's error syntax, across every fixture. - SEO:
jobTitleanddescriptionare omitted from the JSON-LD when unset
instead of emitted asnull.jsonifyrenders a nil as valid JSON, so this
was never the defect above — but"jobTitle":nullasserts that the person has
no job title, where saying nothing asserts only that this site left the field
empty.
Changed
- Hero terminal:
cat stack.txtnow follows[params.about.skills] enable,
which has to be an explicittrue— an absent key counts as off, the same
reading the skills block inside the about section and the section index have
always used. The switch used to hide that block while the terminal above kept
announcing the same list — one switch that meant two different things
depending on where you looked. Sites that writeenable = true, the
exampleSite among them, are unaffected. A site that listsitemswithout
anenablekey does losecat stack.txt: it was already not rendering the
skills block, and the terminal now agrees with the page instead of announcing
a list the reader cannot find. - Docs:
RELEASING.mdnow says what0.xdoes with a breaking change. The
SemVer table had no row for it, and themajorrow's example read0.3.0→
1.0.0— which taken literally made v0.4.0, breaking by the table's own
definition, a1.0.0. It was cut as0.4.0deliberately, per
SemVer §4: while the theme is0.xa
breaking change goes in the minor, and1.0.0is reserved for the deliberate
statement that the contracts are stable. Themajorrow now counts from
1.0.0so it stops implying otherwise. Wording only — the script chooses
nothing, the number is still yours to pass.
Full changelog: v0.4.0...v0.5.0