Nibble 0.13.0
What’s new
- A folder of Markdown is served without a database copy of it. Declare a collection with
files: docs, give it a view, and the folder is the collection: its shape is the address, a folder’s ownindex.mdanswers for the folder, and the folders are the navigation tree. Nothing is synced, nothing is migrated, and there is no step to remember on a deploy.- Each page declares an
id:in its frontmatter, which is what links to it hold. Moving or renaming a file changes its address and keeps its identity; editing it changes neither. - Resolving one of these addresses is a lookup in memory rather than a query, so it is quicker than the rows it replaces. In development a file watcher rebuilds the index as you edit.
- Each page declares an
- An image in a page is offered at the widths a browser can choose between. Whether it was written beside
the page or uploaded in the control panel, it renders with a
srcset— so a phone stops fetching a screenshot meant for a desktop.sizesdefaults to the full viewport, which is what a browser assumes anyway, and a theme narrows it in CSS where it knows the layout. - An image beside a page is published, not uploaded. Write it next to the page, reference it as
, and it is published under a digested name at/nibble-assets/…and served as a static file — cacheable forever, changing address only when the image itself changes. There is no upload step and nothing to keep in step. Only images are published, and a published file whose original has gone is removed. bin/rails nibble:buildchecks everything derived from files and generates what a build needs. It opens no database, so it runs in CI and in the image build — a page with noid:, unreadable frontmatter, a field no blueprint has, two pages claiming one id, or a page under a folder with noindex.mdall fail there rather than in a container. It checks the schema too — everythingnibble:checkdoes that needs no database.
Changed
-
How a site builds and deploys is generated at install, not shipped.
Dockerfile,.dockerignore,.kamal/secretsand aCLAUDE.mdstub are written once, alongsideconfig/deploy.yml, and belong to the site from then on. They used to be Nibble’s, which meant an upgrade merged over anything a site changed. -
NIBBLE-ARCHITECTURE.mddescribes the CMS to whoever reads it, an agent included: where the code lives, how a request becomes a page, what a site owns. It arrives with each release; theCLAUDE.mdbeside it is the site’s and says so. -
There is no staging environment. Whether a site has one, what it is called and how it is protected are the site’s to decide, so Nibble ships neither the environment, its deploy file, nor the basic auth that guarded it.
-
asset_pathcovers the whole ofpublic/, so images published fromcontent/bridge a deploy the way Vite’s bundles already did. -
files:replacessource:on a collection, and a collection written as files no longer has rows: no drafts, revisions, workflow, trash, content migrations or recorded redirects, none of which mean anything for a page whose truth is a file in git. Deleting the file is the whole of deleting the page. -
nibble:content:markdownis gone, and with it the sync on the boot path.bin/docker-entrypointrunsnibble:upgradeand nothing else, so a folder of files can no longer stop a container starting. -
A record cannot be saved onto an address a file already holds; it is refused where the save is made.
-
A theme no longer ships content. A theme is layouts, views, components, styles and schema; content is a site’s own. The example pages and posts a new install offers are Nibble’s now, not the starter theme’s, and the question
nibble:installasks is unchanged. -
nibble:content:importtakes the directory to import. It used to default to the active theme’scontent/, which is the assumption above in another form. A site keeps its package wherever it likes and names it.
What’s fixed
-
An image in Markdown is served at the field’s preset.
image_presetwas declared on the field, shown when editing it, and never reached the resolver — so an upload was rendered at whatever size it arrived at. -
A page written as a file serves its words again. The body of a Markdown file reached the theme as an empty string, so every file-backed page rendered its title and nothing else. It fills the field the blueprint writes as Markdown.
-
A command given a path that is not a content package says so in one line, instead of printing a Ruby backtrace. Every
nibble:*command now fails the same way.
Upgrade
- This release deletes files it used to ship and you now own.
Dockerfile,.dockerignore,.kamal/secrets,.kamal/hooks/andCLAUDE.mdwere ours and are now generated once at install, so the merge removes them from your checkout — without a conflict, because you had not changed them. A hook you wrote yourself goes with them, so check.kamal/hooks/before merging and keep a copy of anything real. Afterwards,bin/rails nibble:install --only=deploywrites the deploy files back andbin/rails nibble:installwritesCLAUDE.md. - Your
config/deploy.ymlis left alone, so it keepsasset_path: /rails/public/vite. Images published fromcontent/live underpublic/too now, so widen it to/rails/publicor a changed image’s old address disappears mid-deploy. - A collection written as files needs
files:where it saidsource:, and every page needs anid:in its frontmatter.bin/rails nibble:buildnames every file that is missing one.