v0.5.1

<p>Locked records and addon-shipped pages are enforced, pages can be moved, the query engine is split out of the registry, and fields gain previews, a pages fieldtype and color palettes.</p>

1 October 2026

Added

  • Page::move($newPath) moves a page and its subtree. It refuses a locked page or descendant, an existing destination, a missing parent and a move into itself. It drops the image variants recorded under the old URL and re-indexes both subtrees. page:move uses it.
  • User::new(), User::save() and User::delete(). Saving emits user.update:after and deleting emits user.delete:before, so the field index follows. user:create, user:delete, make:page, page:delete and page:move use the record API instead of writing folders themselves.
  • App::item($uuid) and the item() template function. Item uuids are site-wide unique, so Item::registryKeyFor() takes only the uuid, and Item::fromRow() builds an item from a registry row.
  • phlat addons:mount [alias] creates the real page an addon's mount slug needs.
  • History::changes() and History::revertField(): field-level changes between HEAD and the working tree, and reverting one field. The Changes page lists them.
  • Users are indexed for search (username and profile fields). Fields nested in stack, blocks and repeater are judged by their own type through FieldType::indexChildren(). Index text is the stored value, not rendered output (FieldType::indexText()). Registry schema is 13.
  • FieldType::preview() and an optional fieldtypes/{name}/preview.latte (App::fieldtypePreviewTemplate(), FieldValue::previewTemplate()). The admin shows a collapsed field's preview. Entries in stack, blocks and repeater collapse with a summary.
  • The pages fieldtype: an ordered list of pages stored as uuids, so a rename or move never breaks one. It skips pages that no longer exist.
  • color takes #hex, rgb(), hsl() and oklch(), stored in the notation it was written in. A palette option limits it to swatches. rgb(), hsl(), hex(), contrast(), lighten() and darken() are available in templates, backed by the new Toolkit\Color.
  • code stores {language, code} with a language per entry. A bare string still reads as code in the field's default language.
  • number takes min, max, step, unit and display (input or range), and validates min/max. select takes display (dropdown or buttons) and badge. datetime takes format. list has a tag editor.
  • A view's fields entry with no global definition can define itself inline: {"primary": {"type": "color", "label": "Primary"}}.
  • Global option fields badge, display, format, max, min, palette and step.
  • A bookmarks field on the admin root page. Each page becomes a shortcut in the admin nav.
  • Click-to-edit: data-edit-field regions on the front end highlight and open their field while the edit panel is open.
  • Arr::setPath(), Str::matchAll(), and woff/woff2 in F::mime().
  • Tasks in phlat/taskfile.yml for build, watch, watch:admin, sync, deploy, deploy:rollback and dep. npm run build also builds the admin JS (build:admin:js). deploy.example.php is rewritten for the current layout.
  • System tests for addon mounts, addon page locks, field definitions, locked records and page move, and a test for Toolkit\Color.

Changed

  • Addon mounting is explicit. A mount needs both the config slug and a real page in data/pages/ whose view is the addon's. The addon's content and saves live in that page, and the addon's own pages/data.json supplies defaults under what was saved (AddonPage::publishedData()). The addon is not served until the page exists.
  • AddonPage::children() lists from the addon's own pages/ folder, and AddonPage::url() always builds from the mount URL.
  • The Clockwork mount is clockwork instead of __clockwork, and its API is at /clockwork/. phlat/addons/clockwork/pages/index.json is gone.
  • The admin's docs, registry and templates pages moved under system, and logout under profile.
  • Locked records are enforced. Record::save() accepts only a change to locked on a locked record. delete() and Page::autosave() throw ForbiddenException, and Page::delete() refuses when a descendant is locked.
  • Records and files are only written inside data/ (Record::assertInData(), a write guard in PageFiles). Sub-pages an addon ships and the guest user are read-only.
  • Record reads its envelope from index.json only. The registry $row constructor argument is removed.
  • The query DSL compiler moved from Registry to Data\Query, and image variant bookkeeping moved from Registry to PageFiles.
  • PageFiles methods no longer take $parentUuid. PageFiles::buildImage() rejects anything that isn't a resizable image.
  • History re-indexes after restore, pull and switchBranch, since git rewrites files the registry never saw.
  • Error pages render with the site's global controller data.
  • A response rendered for a signed-in session is never cached, whatever the view's cache says.
  • Admin routes return 403 when requested from an iframe (Sec-Fetch-Dest: iframe).
  • phlat/Caddyfile restarts the worker when a .latte file changes.
  • page:delete, page:move and make:page no longer maintain a folders list in the parent's index.json.

Fixed

  • An addon view with no index.json fell through to the site view of the same name.
  • FieldTypeList stored the empty entries a tag editor posts.

Removed

  • FieldIndex no longer indexes field definitions.

Comments

No comments yet. Be the first!

Loading form…