[
  {
    "version": "0.11.1",
    "date": "2026-09-22",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.11.1",
    "security": false,
    "body": "### What's fixed\n\n- **A browser that asks for AVIF no longer 500s on a transformed image.** libvips can report a format among the\n  ones it reads without carrying the plugin that writes it — Debian splits AV1 encoding into its own package,\n  so a stock image had every reason to believe it could produce AVIF and none to actually try. The AV1 encoder\n  is now installed, and a format is only offered to a browser once it has genuinely been encoded, not merely\n  found in the list of things libvips claims to support — so the same class of gap degrades to the next format\n  instead of failing the request, on any build where an encoder turns out to be missing."
  },
  {
    "version": "0.11.0",
    "date": "2026-09-22",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.11.0",
    "security": false,
    "body": "### What's new\n\n- **A folder of Markdown is synced when the container boots.** `bin/rails nibble:content:markdown` now runs\n  beside `nibble:upgrade` before the server accepts a request, so merging a page publishes it and deleting one\n  trashes it with no step to remember. The sync is idempotent, so a boot that changes nothing writes nothing.\n\n### Changed\n\n- **`nibble:content:markdown` over no collections is no longer an error.** Asked to sync every folder when a\n  site has none, it now says so and exits cleanly. It used to exit non-zero, which — as of this release, where\n  it runs at boot — would have stopped the container starting for every site that writes no Markdown."
  },
  {
    "version": "0.10.1",
    "date": "2026-09-22",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.10.1",
    "security": false,
    "body": "### What's fixed\n\n- **A site no longer inherits Nibble's own local git excludes.** `bin/setup` hides the files Nibble generates\n  but must never commit — `config/nibble.yml`, `config/deploy.yml`, `.nibble/`, the credentials, `db/schema.rb`\n  — by writing them into `.git/info/exclude`. A site commits every one of those, so in a site the list was\n  backwards: the files never appeared in `git status`, and a generated file left untracked is one a deploy\n  builds without. A theme's generated `types.d.ts` went missing from the image this way, failing the build.\n  `bin/setup` now leaves a site's excludes alone.\n\n### Upgrade\n\n- **If you installed before this release, clear the list once**: open `.git/info/exclude`, delete everything\n  under `# Generated for this checkout`, then check what `git status` reveals. A theme's\n  `.nibble/types.d.ts` in particular has to be committed — the production build reads it."
  },
  {
    "version": "0.10.0",
    "date": "2026-09-22",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.10.0",
    "security": false,
    "body": "### Changed\n\n- **Uploads go to local storage until S3 is configured.** Production and staging named `:amazon` whatever the\n  settings said, so a site with no bucket could not serve an upload at all. Name `AWS_BUCKET_NAME` and it is S3\n  as before; leave it unset and files are stored on the server, under the volume a Kamal deploy already keeps.\n  A site can now run before it has object storage.\n\n### What's fixed\n\n- **A folder of Markdown with a grid field no longer rewrites itself on every sync.** A grid row carries an id\n  the control panel follows it by, a file carries none, and one was minted each time — so the page never looked\n  unchanged, and every deploy wrote a revision, purged its cache and raised events for a page nobody had\n  touched. The row ids already stored are now kept. The same applies to a replicator.\n\n### Upgrade\n\n- **If you run on S3 without setting `AWS_BUCKET_NAME`**, uploads were going to a bucket named\n  `nibble-<environment>` by default. That default no longer selects S3 at all: without the variable this\n  release stores files on the server instead, and anything already in that bucket stops being found. Set\n  `AWS_BUCKET_NAME` before upgrading and nothing changes. A site that has never used S3 needs to do nothing."
  },
  {
    "version": "0.9.1",
    "date": "2026-09-22",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.9.1",
    "security": false,
    "body": "### What's fixed\n\n- **A site's own content migrations no longer reach Nibble's tests.** The migration runner built its own list of\n  schema layers and read the site's `schema/migrations/` directly, so adding a migration to a site made Nibble's\n  suite fail in a site that had done nothing wrong. It now reads the layers the rest of the schema does."
  },
  {
    "version": "0.9.0",
    "date": "2026-09-22",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.9.0",
    "security": false,
    "body": "### What's new\n\n- **A folder of Markdown decides its own addresses.** A collection written as files now answers where its files\n  sit: `content/docs/index.md` is `/docs`, `content/docs/something.md` is `/docs/something`, and\n  `content/docs/a/b.md` is `/docs/a/b`. Give the collection `route: /docs/{slug}` and a view, and that is the\n  whole of it — nested pages no longer need `{parent_slugs}` spelled out, and a folder's own `index.md` no\n  longer lands at `/docs/home`.\n  A route that already places its pages — one naming `{parent_slugs}` or `{parent_uri}` — is left exactly as\n  written.\n\n### What's fixed\n\n- **Changing where a collection lives now moves the pages already in it.** A route is not a file, so the\n  Markdown sync had nothing to compare and reported no changes: the schema said one address and the database\n  held another, with nothing reporting the difference. The next edit to a single page would then have moved\n  that page on its own. The sync now treats a page whose address no longer matches its route as out of date,\n  like any other difference between the folder and the site.\n\n### Changed\n\n- **Nibble has a public home.** The code is at `https://github.com/mah3uz/nibble.git`, and the project's site is\n  at `https://nibble.ink`. `install.sh`, the README and the constants that say where releases come from all\n  name them now.\n- **The control panel reads the release notes from `https://nibble.ink/api/v1/releases`** rather than a raw file\n  in a git repository. It serves the same notes, so nothing looks different; the address is one we can keep\n  serving whatever happens to where the code is hosted.\n\n### Upgrade\n\n- **A Markdown collection routed `/x/{slug}` will see its nested pages move.** A page written at\n  `content/x/a/b.md` answered at `/x/b` and now answers at `/x/a/b`, and a root `index.md` moves from `/x/home`\n  to `/x`. The move happens on the next `bin/rails nibble:content:markdown`, and **no redirects are recorded\n  for it** — if anything links to the old addresses, write the redirects yourself before running it. A\n  collection whose route already names `{parent_slugs}` or `{parent_uri}` is unaffected.\n- **This release moves where upgrades are fetched from, which the release before it cannot know.**\n  `bin/nibble-upgrade` reads the address out of the version you already have, so the upgrade that takes you to\n  this one still asks the old host. Run it once as\n  `NIBBLE_REPO=https://github.com/mah3uz/nibble.git bin/nibble-upgrade`, and every upgrade after this one finds\n  its own way.\n- **A control panel older than this release keeps asking the old address for its notes**, so its update check\n  quietly stops finding anything once that address goes. Upgrading is the fix; nothing else is affected."
  },
  {
    "version": "0.8.0",
    "date": "2026-09-22",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.8.0",
    "security": false,
    "body": "### What's new\n\n- **A collection can have a landing page of its own.** Give it an `index_route`, and optionally an\n  `index_template`, and that address renders the collection rather than one of its entries — the same pair\n  taxonomies already had. A second single-entry collection carrying an index is no longer the way to do it.\n- **A feed.** `feed: true` on a collection publishes it at `/feed-<handle>.xml`, everything that publishes one\n  is merged into `/feed.xml`, and every page points at it so a reader finds it without being told. Atom, so\n  dates need no interpreting. Set `feed: { title:, limit: }` to say more.\n- **A visitor's light or dark choice survives the first paint.** The shell reads the `nibble_theme` cookie and\n  writes `data-theme` on `<html>`, so a visitor who has chosen against their system preference no longer sees\n  the other register flash before the page settles. A theme that writes that cookie needs no other change.\n- **An entry's author reaches the theme**, as `author: { id, name }` on its props, loaded once for a whole\n  listing rather than once per entry. Anyone who may edit a collection's entries can reassign it in the\n  control panel.\n- **A listing can sort by a blueprint field**, not only by a column, so a collection can be ordered by what it\n  actually holds.\n- **`{parent_slugs}` in a route.** A structured collection can now sit under a prefix of its own —\n  `/docs{parent_slugs}/{slug}` — where `{parent_uri}` would have repeated the prefix at every level.\n- **`position` can be set in frontmatter**, so a folder of Markdown can state its own order instead of\n  depending on publication dates.\n- **`delete_collection`** is a content migration operation. Renaming a collection used to leave its old records\n  behind, reported by `nibble:check` with nothing available to clear them.\n\n### What's fixed\n\n- **The theme named in `config/nibble.yml` is the one that gets served.** The build compiled the theme the\n  settings named while the page asked for the default theme's stylesheet, so a site with its own theme rendered\n  its own markup with someone else's CSS and nothing said why. There was one way to resolve the active theme;\n  now there is.\n- **A folder of Markdown with an `index.md` at its root imports.** That file used to be refused for having no\n  slug left, and the refusal discarded **every other page in the collection** along with it. It now becomes the\n  collection's own root entry.\n- **The page cache no longer outlives a local asset rebuild.** After `bin/vite build` the cached HTML went on\n  naming hashed files that no longer existed until the process restarted. A deploy was never affected.\n- **Nibble's own tests no longer assume the site around them is empty.** They took the site's schema, theme and\n  routes as their own, so declaring a collection, bringing a theme or adding a route made Nibble's suite fail in\n  a site that had done nothing wrong.\n\n### Changed\n\n- A theme that ships no `styles/theme.css` is served no stylesheet link, rather than being handed another\n  theme's. A theme that has one whose build is missing still raises.\n- `bin/ssr-smoke` proves the render for whatever theme is active, instead of looking for the default theme's\n  404 copy.\n\n### Upgrade\n\n- **If you set `NIBBLE_THEME` to work around the theme being ignored, you can stop.** `config/nibble.yml`\n  decides now, and the variable only overrides it. Where the two disagreed, the file wins — check they name the\n  theme you meant before deploying.\n- **If a folder of Markdown has an `index.md` at its root**, the next `bin/rails nibble:content:markdown`\n  creates a page for it where it previously created nothing at all for that collection."
  },
  {
    "version": "0.7.0",
    "date": "2026-09-21",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.7.0",
    "security": false,
    "body": "### What's new\n\n- **A collection can be written as Markdown files.** Name a folder inside `content/` in the collection's schema\n  and `bin/rails nibble:content:markdown` makes the collection match it: a file is a page, a folder's `index.md`\n  is the page its files sit under, frontmatter becomes fields, and a page whose file has gone is trashed. It is\n  safe to run again, so a deploy can run it every time.\n  - A link to another `.md` file becomes a link to that page, resolved when the page renders, so it survives\n    either page moving. An image beside the pages becomes an asset, filed where it was written and uploaded\n    again only when it changes.\n  - The folders are a navigation tree, ordered by `order` in the frontmatter — the sidebar a documentation site\n    needs, without maintaining one by hand.\n  - Those pages are read-only in the control panel, and each one says which file it is written in and what to\n    run after changing it.\n\n### Changed\n\n- `bin/nibble-release` offers the push rather than printing it, and offers to publish the feed in the repository\n  it was written to. `--push` answers both without asking."
  },
  {
    "version": "0.6.0",
    "date": "2026-09-21",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.6.0",
    "security": false,
    "body": "### What's new\n\n- A **markdown** fieldtype: write Markdown in any blueprint, with a preview rendered by the server so it shows\n  what a theme will actually receive. Assets are referenced rather than linked, so replacing one does not leave\n  documents pointing at the old file.\n- Release notes in **Updates** are rendered rather than shown as Markdown source.\n- **Code blocks are highlighted while you write them,** and carry the language you chose to the published page.\n  A selector on the block names it, the control panel colours it as you type, and a theme highlights the page\n  from the same name — Crumbs does, with the thirty-seven languages the selector offers, fetching only the ones\n  a page actually uses.\n- **Tab indents inside a code block,** two spaces at a time, and Shift-Tab takes them away. A selection spanning\n  lines moves together.\n- Writing in the control panel reads as prose: headings, quotes, tables, callouts and a code panel that looks\n  like code, in both colour schemes.\n\n### Changed\n\n- **The documentation is no longer part of Nibble.** It is a site of its own, written in Markdown, which is also\n  what publishes the release notes your control panel shows. Every site that installed Nibble was carrying a copy\n  of our documentation and a feed file it never read from disk.\n  **Upgrade:** taking this release deletes `docs/` from your tree. If you had put your own files there, git stops\n  on the conflict — keep your side, or move them first.\n- **The release feed moved with it, and is generated rather than appended to.** It is built from the changelog\n  each time, so an entry corrected after its release now reaches every site instead of staying wrong.\n  **Upgrade:** a control panel still on 0.5.0 asks for the feed at its old address, which this release removes,\n  so its update check quietly fails until it upgrades. Upgrading is the fix; nothing else is affected.\n- Choosing a term or an entry says **Choose…**, or whatever `placeholder` the field sets, rather than naming what\n  the control is made of.\n\n### What's fixed\n\n- **A Markdown field with an image in it could not be imported.** Exporting wrote the asset's path where its id\n  had been, and importing that file failed on the first slash, so content carrying one could not move between\n  sites at all.\n- Every field in a panel drew a hard line across its top in dark mode: an input's inset highlight, which belongs\n  to an input standing on its own, painted inside the group that draws its border.\n- Server rendering takes its port from `INERTIA_SSR_PORT` at both ends. A second instance — a test server beside\n  a development one — fought the first for 13714, and the loser sat in a restart loop."
  },
  {
    "version": "0.5.0",
    "date": "2026-09-21",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.5.0",
    "security": false,
    "body": "### Changed\n\n- **Everything of Nibble's now lives under `lib/nibble/`, and `app/` is yours.** Your models, controllers, jobs\n  and views sit where a Rails application puts them, with nothing of ours beside them, and your views are looked\n  in before Nibble's. Class names are unchanged.\n  **Upgrade:** if you added files under `app/`, they stay exactly where they are and keep working. If you had\n  changed one of Nibble's files in place — which `bin/rails nibble:check` reports — git will stop on it during the\n  merge, because that file has moved.\n- `bin/rails nibble:check` no longer reports files you added under a directory Nibble also uses as edits to ours.\n  It reports what you changed or deleted of Nibble's, which is what an upgrade can actually stop on.\n- `.github/` is gone. Workflows and dependency updates of ours have no business running in your repository,\n  against your budget, on rules you did not write — and a site is a clone, so the only way not to send them is\n  not to have them. `bin/ci` runs every check Nibble runs on itself. `.github/` is yours, empty or otherwise.\n  **Upgrade:** taking this release deletes our copy from your tree. If you had put your own workflows there, git\n  stops on the conflict — keep your side.\n\n### What's new\n\n- `site/test/` is yours for your own tests, run by `bin/ci` and by `bin/rails test site/test`, with Nibble's\n  `test_helper` available. Nibble's own tests stay in `test/`.\n- The README says how to send a change back rather than carry it, and states the ownership rule in one line."
  },
  {
    "version": "0.4.0",
    "date": "2026-09-21",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.4.0",
    "security": false,
    "body": "### What's new\n\n- `bin/rails nibble:generate:view NAME --collection=posts` writes a view and its query sidecar into your own\n  theme, typed for that collection's records, and says how to wire it up. It refuses to write into Nibble's\n  theme, which is what `nibble:generate:theme` is for."
  },
  {
    "version": "0.3.0",
    "date": "2026-09-21",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.3.0",
    "security": false,
    "body": "### What's new\n\n- `bin/rails nibble:generate:theme HANDLE` copies the starter theme into `themes/HANDLE`, names it as this site's\n  theme and leaves ours alone, so a site never edits `themes/crumbs` to change how it looks.\n\n### What's fixed\n\n- The asset build ignored the theme named in `config/nibble.yml` and always bundled `themes/crumbs`, so a site\n  with its own theme edited `.vue` files that were never built — silently, with nothing to see in any log. The\n  build now resolves the theme the way the rest of Nibble does: your settings first, `NIBBLE_THEME` second.\n- An upgrade stopped partway through on any site with its own theme or its own npm packages: `npm ci` refuses a\n  lockfile that is not exactly ours.\n- `nibble:generate:theme` registers the new theme with npm, so a later `npm ci` — in your own CI, or a container\n  build — doesn't fail naming a workspace nothing told you about.\n- A theme view that throws while rendering on the server now says which view and which URL in the log, instead of\n  an anonymous stack trace. Vue drops only the piece that threw, so the page is otherwise served as normal.\n- `bin/rails nibble:upgrade` writes `db/schema.rb` when there isn't one, so taking 0.2.1 — which stopped shipping\n  ours — leaves a site with its own. **Upgrade:** 0.2.1 said to run `bin/rails db:prepare` for this; that only\n  writes the file when there is something to migrate. If you have no `db/schema.rb`, run\n  `bin/rails db:schema:dump` once, or take this release and it is written for you."
  },
  {
    "version": "0.2.1",
    "date": "2026-09-21",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.2.1",
    "security": false,
    "body": "### What's fixed\n\n- The Updates screen showed only \"Up to date\" when you were already on the newest release, hiding every release's\n  notes. It now lists them whatever version you are on, with the one you are running marked.\n\n### Changed\n\n- `package-lock.json` is shared, not ours alone: a site with its own theme or packages has to change it. An\n  upgrade now rebuilds it with `npm install` rather than installing from it, so a merged lockfile repairs itself.\n  **Upgrade:** if git reports a conflict on it, take either side and run `npm install` — the result is the same\n  either way, and commit it.\n- `db/schema.rb` is yours, and Nibble no longer ships one. It is generated from migrations, so two copies of it —\n  ours and yours — conflicted on every upgrade that carried a migration, on a file neither of us edits by hand.\n  **Upgrade:** the merge removes our copy. Run `bin/rails db:prepare` to regenerate yours, then commit it. If git\n  reports a conflict on it instead, keep your side and run the same command."
  },
  {
    "version": "0.2.0",
    "date": "2026-09-21",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.2.0",
    "security": false,
    "body": "### What's new\n\n- `bin/nibble-upgrade` takes this site to a release: it snapshots the database, gates on the incoming release's\n  requirements, merges, installs dependencies and migrates. It runs on a workstation only.\n- The upgrade offers to re-render the files you own that we generate, and reports what changed in our copy of\n  anything you ejected.\n- **Updates** in the sidebar: releases newer than the one you are running, what each one changed, and the command\n  that takes it, with a count on the menu item that turns red when one of them is a security fix. A daily\n  background check remembers the count; the page itself reads the file again when you open it, so no other page\n  ever waits on it and there is nothing to set up. Nothing about your site is sent, and a switch on that screen\n  stops the checks entirely.\n- Settings a person changes in the control panel are kept in a `settings` table, separate from `config/nibble.yml`,\n  which stays a file you edit by hand.\n\n### Changed\n\n- The first administrator is made by asking, not by environment variables: `bin/rails nibble:admin:create` asks\n  for a name, email address and password, and refuses a password the rules reject. `ADMIN_EMAIL`,\n  `ADMIN_PASSWORD` and `ADMIN_NAME` no longer do anything — `db:seed` seeds roles and tells you to run the\n  command.\n- `.nibble/install.yml` now records the answers the install was given, which is what lets an upgrade re-render\n  your settings without asking again. Installs made before this keep working; the upgrade says it cannot\n  re-render them."
  },
  {
    "version": "0.1.0",
    "date": "2026-09-20",
    "url": "https://github.com/mah3uz/nibble/releases/tag/v0.1.0",
    "security": false,
    "body": "### What's new\n\n- `schema/` holds a site's own collections, blueprints, taxonomies, globals, navigation and forms. It is read after\n  Nibble's and after the theme's, so a file here replaces one of ours whole.\n- `site/` holds a site's control panel changes: `site/pages/<same path as ours>.vue` replaces one of our screens,\n  `site/slots/{Logo,SidebarExtra,Scripts}.vue` replace parts of the chrome, and `site/initializers/*.rb` runs at\n  boot.\n- `bin/rails nibble:eject PATH` copies one of our files into `site/` and records it in `.nibble/ejected.yml`.\n- `bin/rails nibble:check` reports ejected copies whose original has since changed, and files of ours edited in\n  place rather than ejected.\n- `bin/rails nibble:check --support` prints an install summary to paste into an issue.\n- Load hooks `:nibble_entry`, `:nibble_term` and `:nibble_asset`, so a site adds behaviour without reopening our\n  classes.\n- `Gemfile.local` for a site's own gems.\n- `config/nibble.yml` accepts `load_defaults`, which is how new behaviour gets turned on — never by upgrading.\n- A licence: MIT (`LICENSE`). Use it, change it, sell it, rebrand the control panel; keep the copyright notice\n  with copies. No warranty, no support promise.\n\n### Changed\n\n- Passwords must be at least 12 characters and mix lower case, upper case, a number and a symbol. The rule is on\n  the model, so the control panel, password resets, invitations and the installer all apply it.\n  **Upgrade:** existing passwords keep working; the rule applies the next time one is set. If you seed an\n  administrator with `ADMIN_PASSWORD`, that value must now satisfy it.\n\n- Every setting has a default in code, so `config/nibble.yml` can be absent or partial.\n- Reserved paths are a floor a site adds to rather than a list it replaces: `/admin` can no longer be claimed by an\n  entry.\n- `config/routes.rb` belongs to the site. Nibble's routes are drawn before and after it.\n- Nibble's migrations live in `lib/nibble/db/migrate`, leaving `db/migrate` and `db/schema.rb` to the site."
  }
]
