v0.0.69

16 July 2026

App-level HTMX response wrappers, a new View class for editable view definitions, addon-scoped @id/ fieldtype references, and cascading page deletes.

Added

  • App-level wrappers over the Hx toolkit, mirroring Hx:: methods but branching on $request->isPartial() so a direct (non-HTMX) request gets a real redirect instead of an inert header-only response: $app->location(), refresh(), pushUrl(), replaceUrl(), retarget(), reswap(), reselect().
  • Phlat\View: a Record wrapping a view's own index.json (assigned fields and their per-view overrides), the site-author-editable counterpart to Views\PageView's template resolution. $app->view(name), viewNames(), views(), forgetView(name), plus $app->fieldNames() for the equivalent field-name listing.
  • @id/name addon-scoped references for fieldtypes and fields (e.g. @reverse-field/reverse), resolved by App::typeLookupRoots(). A bare name still searches the normal site-overrides-addon-overrides-phlat chain; the @id/ prefix pins the search to one addon's own root.
  • Page::delete(): deletes a page and its persisted subtree, emitting page.delete:before for every descendant (and the page itself) ahead of removal.
  • Admin UI: view editing (assign/remove fields, per-view field overrides) and a delete button on the fields admin.

Fixed

  • PageFiles::build(): Page::file() always passed $isImage = false, so a file that mime-sniffs as an image but was fetched through file() instead of image() never got PageImage's eager public-mirror, and its url() silently 404'd. $isImage is now a minimum, not an override: true if either the caller passed it or the mime sniffs as an image.

Changed

  • Removed the addon alias override system: aliases are no longer configurable via config.addons.{id}.alias. Every addon now gets an automatic, fixed alias from its id's last segment (phlat/admin becomes admin), registered directly in AddonRegistry::register().
  • App::trigger() no longer exits: it was never-returning (calling Hx::trigger() then an empty response), now it just sets the header and returns, so it can be combined with other response calls.