Text

A badge that says what a post is about

Every post wears a date badge with an icon in it, and the icon says what kind of post it is: text, a picture, a video, a quote. On a blog where nearly everything is text, that icon is the same on nearly every card. Somebody running this engine put it plainly in issue #45: one sees the generic icon so often that it becomes invisible, or clutter.

1.6 lets a tag carry an icon instead.

tag_icons:
  - tag: "build"
    icon_svg: '<svg viewBox="0 0 24 24" …>…</svg>'
  - tag: "comments"
    icon: "chat"

It replaces rather than adds

On a post's date badge the tag's icon takes the place of the content-type icon; in the heading of /tag/<name>/ it takes the place of the generic tag icon. That was the requester's first point, and the one worth asking about rather than guessing: two icons side by side on a 60-pixel badge is not a badge, it is a puzzle. A tag with no entry changes nothing, so the generic icon is still there for everything you have not named.

Číst dál

Where a reader carries a post

A post gets read, and then it goes somewhere: into a toot, a message, an email to one person. Until now the reader did that by copying the address out of the bar. 1.6 puts a row of controls under the post for it — off unless share: names what you want, drawn in the order you name it.

share:
  - mastodon
  - bluesky
  - email
  - copy
  - system

What is prefilled, and what is not

The post's name and its address, where the target takes both. Facebook and LinkedIn take the address alone and read the name off the page. The reader writes the part that is theirs. bluesky, email, facebook, linkedin, threads and x are plain links, one address each, no script behind them.

Three that are not links

Číst dál

Written on a train

A reader who was thinking about moving to this engine wrote to say he would stay where he was. He manages his blog from a phone, on the road, and a blog that can only be worked from a terminal was no use to him however fast it built. He was right.

1.6 answers him with two things, and with one thing it refuses to do.

A page on the blog itself

Set write: true and the build publishes a small editor at /write/: a title, the text, tags, photographs and video, each picture with its description. It wears the blog it writes to — its name, its palette, its language — and it offers the blog's own tags as you type, the ones used in the last twelve months first, so a tag is tapped rather than spelt a second way. A preview shows the post in the blog's own stylesheets. A row of marks above the text wraps what is selected and comes off again on a second tap. Above the send button, a Draft | Publish switch.

Číst dál

A post handed over as a file

Until 1.6 a post came from a person at a terminal. The wizard asked its questions, opened an editor, asked what to do at the end. Nothing else could write one: not a script, not a cron job, not a phone.

./blog.sh add post.md

does the same work with the markdown handed over instead of typed. No editor, no questions, no dialog at the end. A bare filename is looked for in incoming/, so the file can arrive by the same upload as the photographs, and it is deleted once the post is written.

Where the wizard would ask, this refuses

A person can be asked. A script cannot, so every question becomes a refusal that writes nothing: a photo that has not finished uploading, an empty body, a file that is not text, a second filename where one was expected. The alternative — guessing — was measured on the old code, where a byte-order mark in front of the header was taken for body text: three invisible bytes from a Windows editor and the post arrived with no title and no tags, named after the words "title:" and "tags:", exit 0. 1.6 reads the header through them.

--json: one object, nothing else

Číst dál

The time between deciding and the site saying so

Publishing a post used to cost the size of the archive, not the size of the change. A post dated today alters a dozen files. The build rendered every page in the archive and read every one back off disk to find that out.

On the archive this engine was built around — 6,639 posts, on the server it lives on — that was 47 seconds for a rebuild that changed nothing, and 48 from pressing publish to the post being out. Fine for one post in the morning. Less fine for fixing a typo in ten of them.

What the build remembers now

1.6 keeps a record of what went into each page: the post, the template, the locale, the configuration, the engine itself. On the next build it renders only the pages whose record no longer matches, and the rest cost one stat each instead of a full render and a read-back.

The same archive, measured the same way: a rebuild that changes nothing takes a seventh of what it did. Publishing a post dated today takes about two fifths — 16 seconds of build and 2 of upload, where it was 48 — and the build says so itself: Pages already up to date, not rebuilt: 10,160. The ratio is what travels; the seconds are one machine's.

Číst dál

Everything else in 1.5

Five posts covered the shape of this release. Here is the rest of it, a paragraph each.

Číst dál

An archive you cannot see is not empty

This site's approved comments and its post counters are two small JSON files, refreshed by a cron job every half hour and uploaded with the rest of the site.

Through 1.4, a tick that ran while the archive was not there would narrow both files to nothing and upload them. An absent or empty content directory answers with an empty list and no error, and an empty list reads exactly like "every announced post was deleted". The live site lost every approved comment and every counter at once. Exit 0, nothing said, nothing mailed.

The archive lives on a synced volume. A volume that has not mounted yet, or a working copy whose files are still arriving, is not an unusual Tuesday.

Not being able to tell is a reason to keep everything

That sentence is the fix, and it now runs in two tiers.

If the content directory is not there — or if the archive reads as empty while the previous files held anything at all — nothing is narrowed, and the run says why.

Číst dál

Posts nobody meant to publish

An importer's worst failure is not losing a post. It is publishing one.

1.5 went through all eleven import adapters and the machinery underneath them looking for exactly that, and found it three times over, in three different files, made the same way each time.

An archive is the whole account

A Mastodon export is not your public timeline. The outbox holds your followers-only posts and your direct messages beside the public ones — and every standalone toot in it was being written as published. On the archive this was measured against that is 141 posts of 2,548, of which 132 are direct messages, each given its own page, its own sitemap entry and its own item in the feed. Nothing in the run's summary mentioned it.

The scope was in the export all along, exactly where ActivityPub puts it. Public in to is public. Public in cc is unlisted — a real address, out of the listings. Followers-only and direct become drafts: that keeps the author's words in the archive and off the site, which is the only reading of "not public" that cannot publish something by accident.

Číst dál

Cut before it is written

A listing card used to be the whole post. The engine rendered every block of it into the page, and the stylesheet clipped the result at 500 pixels.

On the real front page that meant fourteen cards carrying between 794 and 2,616 pixels of content and showing 500. Of the page's 50,659 characters, 49,312 sat inside the clip. Thirty-three of its 154 focusable elements could be reached with the keyboard while being invisible on screen — links a sighted mouse user could not see and a keyboard user could not skip.

1.5 cuts the card before it is written. The stylesheet hides nothing.

That page went from 89,268 bytes of HTML to 41,155. Its largest card, 7,463 characters, is now 2,566. And "read more" appears exactly when something did not fit, rather than sitting under a card that was already showing everything it had.

The budget is in pixels, not characters

Číst dál

Two pages the site built out of itself

Until now this engine could show you ten posts at a time and nothing else. /page/128/ is a real address on a large archive, and it tells you nothing about whether you have landed in 2009 or in 2014 — pagination is anchored from the oldest post, so the number moves under you every time something is published. The only complete list of anything lived in the terminal.

1.5 adds two pages that read the archive instead of walking it.

/archive/ — the shape of the whole thing

A row per year, with a strip of twelve months beside it. A month is shaded in one of four steps rather than simply lit or dark, because "has posts / has none" is not the question worth answering: on the reference archive a month holds anywhere from one post to eighty-seven, and drawing those the same would hide the one thing a map is for. /archive/<year>/ is one line per post, by month.

Two levels and no more. A third would be some 280 pages of individual months — a great deal of building for a question nobody asked.

Číst dál