v0.0.31

27 June 2026

Real before/after router hooks, route tokens switched to {name} syntax, controller chain simplified to app + view only.

Added

  • Router::before()/after() : hooks registered from any router file in the dispatch chain. before hooks run once, after every router file has loaded, right before route matching; after hooks run once a route's handler has resolved a result page, right before it's returned. Both receive ($app, $page).
  • Page::route() dispatch order is now explicit: the 'app' router runs first (kept, not discarded), then each ancestor page's router (root → immediate parent, routes discarded), then the current page's own router (routes kept).

Changed

  • Route tokens switched from (:name) to {name} syntax, echoing Latte's own tag delimiters : {any} now matches one non-empty segment (previously zero-or-more), {all} still matches across slashes including empty.
  • Page/Addon controller chain simplified to exactly two layers : global controller (views/controller.php) then the page's own view controller : replacing the old three-layer base/app/parent/view merge. Page::controller() is now public so a controller can fetch another view's data explicitly.
  • Addon no longer overrides render()/route() : it now just overrides viewRoots()/viewRootsOnly() so the shared Page logic resolves an addon's own view tree instead of the site-wide one.
  • Router::clear() renamed to clearRoutes() : hooks are preserved across a clear, only routes are discarded.

Removed

  • Per-page-folder router.php override : router resolution is now purely $page->view()->routerFile().