Profpatsch/users/Profpatsch/blocks

blocks(1)

blocks - block-based authoring for blog posts, stored in SQLite

blocks serve -db path [-addr host:port]
blocks render -db path -slug slug [-asset-prefix prefix]

blocks is a local web application for writing blog posts as an ordered list of typed blocks, in the manner of a notebook: a paragraph of prose, a code listing, a photograph and a 3D model are each their own block, moved and edited independently rather than being buried in one long file.

Dragging a file onto the page uploads it and inserts a matching block immediately after whichever block it was dropped on, so illustrations land where they belong instead of being appended and then moved.

Everything lives in a single SQLite file: the posts, their blocks, the uploaded originals and every derived rendition. Nothing is written to the filesystem, so the database file is the complete authoring state and can be copied, versioned or backed up as one unit.

markdown

Markdown source, rendered with GitHub-flavoured extensions. Fenced code blocks inside it are syntax-highlighted like a dedicated code block.

code

A code listing with an explicit language, syntax-highlighted. An unrecognised language falls back to content analysis rather than being rejected.

image

A PNG, JPEG or WebP upload, with alt text and an optional caption.

stl

A 3D model in either STL flavour, shown in an interactive viewer.

Uploads are accepted as PNG, JPEG or WebP and always served as WebP, at widths of 1600 and 800 pixels, offered to the browser through srcset. An image is never enlarged: a source narrower than a target width is stored at its own size.

The pristine upload is kept alongside the derivatives, so the widths and quality can be revised later without re-encoding an already lossy copy.

EXIF metadata is stripped, because it carries GPS coordinates, camera serial numbers and timestamps. Since that also discards the orientation tag a browser would otherwise honour, the rotation it describes is applied to the pixels first, and photographs taken on a phone come out upright.

Encoding is performed by cwebp(1), which the packaged binary carries on its PATH. Neither the Go standard library nor golang.org/x/image can write WebP, and the pure-Go encoders are lossless-only, which for photographs yields files larger than the JPEG they came from.

Both the binary and ASCII STL formats are read. The file is parsed on the server into a packed vertex buffer with per-face normals, centred on the origin and scaled to fit a unit box; the browser fetches that buffer and hands it to WebGL unchanged, with no parsing and no third-party JavaScript.

Normals absent from the file, which many exporters omit, are recomputed from the triangle winding.

A download link for the original file sits beneath every viewer, as a sibling rather than as a fallback the viewer replaces, so the file stays reachable whether or not the viewer starts: without JavaScript, without WebGL, and equally when the model displays perfectly and the reader simply wants the STL.

A viewer is inert until the reader activates it, and shows a "Click to interact" button. While inert it consumes no input at all: the wheel scrolls the page and a finger swipes past it, exactly as they would over an image.

This matters because a model sits in the middle of a page of prose. An embed that grabs the wheel is a trap: the reader scrolls, the pointer crosses the model, and the page stops moving.

Activating it, by clicking the button or the model, takes over the wheel, touch gestures and the arrow keys:

Drag

Orbit the model.

Wheel

Zoom.

Arrow keys

Orbit, so the model is usable without a mouse.

+ and -

Zoom.

Esc

Release the viewer.

Clicking anywhere outside the viewer, or moving focus away, also releases it, so the reader never has to hunt for a control to get their scroll wheel back. A reminder of the controls and of Esc is shown for as long as the viewer is active.

serve -db path [-addr host:port]

Run the editor and preview server. The database is created if it does not exist.

The default address is 127.0.0.1:8791. There is no authentication and no CSRF protection, which is a deliberate consequence of it being a single-user tool on the author's own machine; both would be required before binding it to a public interface.

Pages served:

/

the list of posts

/edit/{slug}

the block editor

/posts/{slug}

the rendered preview

render -db path -slug slug [-asset-prefix prefix]

Print a post's rendered HTML fragment to standard output.

Asset links are built from prefix (default /asset), which is the same seam the preview uses to point at blobs in the database and that a static export will use to point at published files.

path

The SQLite database given to -db, holding posts, blocks and assets. WAL mode is used, so .sqlite-wal and .sqlite-shm files appear alongside it while the server runs.

Start the editor on the default port:

$ blocks serve -db ~/posts.sqlite

Render a post to a file:

$ blocks render -db ~/posts.sqlite -slug my-post > my-post.html

cwebp(1), sqlite3(1)

Profpatsch