From f2724972af779355dac7e303f835f76bb1ee8e15 Mon Sep 17 00:00:00 2001 From: code Date: Thu, 10 Sep 2026 20:58:37 +0000 Subject: [PATCH] Add typed text pages; serve CSS as compiled-on-demand /css/main.css Sidecar-less page bundles whose directory ends in a MediaType extension (.txt/.css/.xml/.json/.js) now render raw (no HTML layout, autoescape off), are served with that extension's Content-Type, and cache as a flat file (public/cache/) so Apache serves them directly via a new .htaccess rule. They're excluded from the content index. CSS becomes one of these: novaconium/pages/css/main.css/ holds index.twig ({{ sass('main.sass') }}), main.sass, and _colors.sass. App\SassExtension's sass() Twig function resolves the entry file through the overlay and shells out to Dart Sass on a cache miss; Lib\SassCompiler wraps the CLI. This drops the committed App/css/main.css build artifact and the public/css bind mount. Override styling by copying the whole bundle to App/pages/. Also ship robots.txt and humans.txt as framework bundles, and drop the php -S dev router (public/router.php) - Docker/Apache only now. New: novaconium/src/MediaType.php, SassExtension.php, lib/SassCompiler.php. Moved: novaconium/sass/ -> novaconium/pages/css/main.css/ (+ App/sass merged). Docs: AGENTS.md, new /admin/docs/text-pages, and styling/caching/docker/ design-notes/getting-started/project-layout/config docs updated. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018Q9cAXC9xcXYnmP7qjsnw7 --- AGENTS.md | 83 +-- App/css/main.css | 503 ------------------ App/pages/blog/novaconium-features/index.twig | 6 +- App/pages/blog/style-guide/index.twig | 2 +- App/sass/_colors.sass | 25 - Dockerfile | 12 +- README.md | 11 +- docker-compose.yml | 1 - novaconium/bootstrap.php | 4 +- novaconium/lib/SassCompiler.php | 52 ++ .../pages/admin/docs/_layout/layout.twig | 1 + .../pages/admin/docs/caching/index.twig | 2 + novaconium/pages/admin/docs/config/index.twig | 2 +- .../pages/admin/docs/design-notes/index.twig | 9 +- novaconium/pages/admin/docs/docker/index.twig | 4 +- novaconium/pages/admin/docs/forms/index.twig | 2 +- .../admin/docs/getting-started/index.twig | 17 +- .../admin/docs/project-layout/index.twig | 11 +- .../pages/admin/docs/styling/index.twig | 51 +- .../pages/admin/docs/text-pages/index.twig | 70 +++ novaconium/pages/css/main.css/_colors.sass | 23 + novaconium/pages/css/main.css/index.twig | 1 + .../{sass => pages/css/main.css}/main.sass | 30 +- novaconium/pages/humans.txt/index.twig | 7 + novaconium/pages/robots.txt/index.twig | 6 + novaconium/sass/defaults/_colors.sass | 27 - novaconium/src/Cache.php | 12 +- novaconium/src/ContentIndexer.php | 19 +- novaconium/src/MediaType.php | 38 ++ novaconium/src/Renderer.php | 23 +- novaconium/src/SassExtension.php | 62 +++ public/.htaccess | 9 +- public/router.php | 37 -- 33 files changed, 442 insertions(+), 720 deletions(-) delete mode 100644 App/css/main.css delete mode 100644 App/sass/_colors.sass create mode 100644 novaconium/lib/SassCompiler.php create mode 100644 novaconium/pages/admin/docs/text-pages/index.twig create mode 100644 novaconium/pages/css/main.css/_colors.sass create mode 100644 novaconium/pages/css/main.css/index.twig rename novaconium/{sass => pages/css/main.css}/main.sass (87%) create mode 100644 novaconium/pages/humans.txt/index.twig create mode 100644 novaconium/pages/robots.txt/index.twig delete mode 100644 novaconium/sass/defaults/_colors.sass create mode 100644 novaconium/src/MediaType.php create mode 100644 novaconium/src/SassExtension.php delete mode 100644 public/router.php diff --git a/AGENTS.md b/AGENTS.md index 5a5dc3c..e5387f9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,18 +50,19 @@ vendored as plain source files. ## The two-root split - **`App/`** — the project: `App/pages/`, `App/lib/` (`Lib\` classes), - `App/config.php`, `App/migrations/`, `App/sass/`. The only directory a - site author is expected to touch. + `App/config.php`, `App/migrations/`. The only directory a site author is + expected to touch. - **`novaconium/`** — the framework: router/renderer core - (`novaconium/src/`), default pages/libs, vendored Twig, autoloader, - config, bootstrap. + (`novaconium/src/`), default pages/libs (including the `css/main.css/`, + `robots.txt/`, `humans.txt/` bundles), vendored Twig, autoloader, config, + bootstrap. Routing/rendering resolve against **both roots, `App/` first** (via `novaconium/src/Overlay.php` for pages, `novaconium/autoload.php` for `Lib\` classes) — same override-by-presence mechanism used for -`config.php`, Twig's `FilesystemLoader`, and Sass (see below). A project -only lists the config keys it's changing in `App/config.php`; never edit -`novaconium/config.php` directly. +`config.php` and Twig's `FilesystemLoader`. A project only lists the config +keys it's changing in `App/config.php`; never edit `novaconium/config.php` +directly. **`db_connections` is the one config key that isn't a plain shallow-merge.** `Lib\Db::config()` (and the duplicate in `bin/migrate.php`) merges it one @@ -139,19 +140,39 @@ syntax (`{% %}`/`{{ }}`) — highlight.js has no Twig grammar and a restricted auto-detect still always guesses wrong without this class. Any new Twig-syntax code sample needs it; PHP/Bash samples don't. -## Sass override quirk +## Typed text pages (CSS, robots.txt, humans.txt, …) -`novaconium/sass/main.sass` does `@use 'colors' as *` with **no** -`_colors.sass` sibling in `novaconium/sass/` — on purpose. Dart Sass -resolves a bare `@use` relative to the importing file's own directory -*before* `--load-path`, so a sibling file would always win and silently -defeat the `App/sass/_colors.sass` override. The framework default lives -at `novaconium/sass/defaults/_colors.sass` instead. Don't move it back. +A sidecar-less page bundle whose **directory segment ends in an extension** +in `App\MediaType::MAP` (`.txt` `.css` `.xml` `.json` `.js`) is a *typed +text page*: `Renderer` renders its `index.twig` with **no** HTML layout and +**autoescaping off**, serves it with that extension's Content-Type, and +`Cache` writes it as a flat file (`public/cache/css/main.css`, not +`.../index.html`). A rewrite in `public/.htaccess` (rule 2b) then serves +that file straight from Apache on every later request — so the extension +list is duplicated there; keep it in sync with `MediaType::MAP`. These +pages are excluded from the content index (`ContentIndexer::isCrawlable()`). +See `/admin/docs/text-pages`. -Every color rule in `main.sass` reads a CSS custom property (`var(--bg)`, -etc.), never a Sass variable directly — required for the runtime dark/light -toggle. Adding a color means adding both the plain and `-light` variable in -**both** `_colors.sass` files and wiring it into both `:root` blocks. +The framework ships three: `novaconium/pages/css/main.css/` (route +`/css/main.css`), `novaconium/pages/robots.txt/`, `novaconium/pages/humans.txt/`. +A project overrides one the normal App-over-novaconium way — put its own +bundle at the same relative path under `App/pages/`. + +CSS is the interesting one: `novaconium/pages/css/main.css/` holds +`index.twig` (`{{- sass('main.sass') -}}`), `main.sass`, and `_colors.sass`. +The `sass()` Twig function (`App\SassExtension` → `Lib\SassCompiler`) +resolves the entry file through the overlay (App first) and shells out to +Dart Sass with that file's own directory as the sole `--load-path`, so +`@use 'colors'` resolves to the sibling `_colors.sass` in whichever copy of +the bundle won. To customise styling, copy the **whole** `css/main.css/` +bundle to `App/pages/css/main.css/` — there's no partial "just +`_colors.sass`" override. +Output is cached like any typed page; run +`php novaconium/bin/clear-cache.php` after editing Sass. Every color rule +reads a CSS custom property (`var(--bg)` …), never a Sass variable directly +— required for the runtime dark/light toggle; adding a color means adding +its plain and `-light` variable in `_colors.sass` and wiring both into the +two `:root` blocks in `main.sass`. ## Input handling @@ -166,12 +187,14 @@ to be hashed) read `$_POST` directly — see login/users sidecars. ## Running it +Only via the Docker image built from `Dockerfile` (Apache reads +`public/.htaccess` directly — there is no `php -S` dev router). Typically: + ``` -php -S 127.0.0.1:8000 -t public public/router.php +docker compose up # image + volumes from docker-compose.yml ``` -`public/router.php` is dev-only, mimics `public/.htaccess`. There is no -test suite — verification is manual route-by-route (see +There is no test suite — verification is manual route-by-route (see `/admin/docs/design-notes`'s Verification section). After testing, clear stray cache with `php novaconium/bin/clear-cache.php` and remove any test-only debris from `App/lib/`/`App/pages/` — nothing there is gitignored @@ -190,13 +213,11 @@ except `public/cache/*` and `novaconium/contact-log.txt`. - `novaconium/bin/` holds standalone CLI entry points (`php novaconium/bin/