Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 161358ef6d |
@@ -370,7 +370,7 @@ docker compose -f compose.yml up --build -d # production
|
||||
stack up and a *minimal* plugin live. Nothing comes before Quick start. Keep its commands
|
||||
copy-pasteable; deeper detail lives in its own section, linked.
|
||||
2. **Returning developer (rest).** A **Contents** ToC right after Quick start, then sections ordered
|
||||
by **what an adopter reaches for first**, not by architectural layering: Goals → Audience → Overview → Glossary → Users, groups
|
||||
by **what an adopter reaches for first**, not by architectural layering: Goals → Audience → Overview → Users, groups
|
||||
& permissions → Building plugins → menu/blocks/interactivity → Configuration → Auth → Email →
|
||||
Architecture → Testing → Production → Observability → JWT-rotation runbook → Project-layout file
|
||||
map → Extending. Place a new section by how early an adopter needs it. **Users, groups &
|
||||
|
||||
@@ -6,12 +6,6 @@ at boot. Entries start at 0.3.0.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### The README defines its terms and states its intended scale
|
||||
|
||||
A new Glossary defines host, plugin, manifest, chrome, nav fragment, permission name, system plugin
|
||||
and denylist. Plugin dependencies also states the scale Plainpages is sized for: a handful of plugins
|
||||
per deployment.
|
||||
|
||||
### The mobile nav stays reachable on engines without `:has()`
|
||||
|
||||
At 860px and narrower, an engine without `:has()` (Chromium below 105, such as webOS TV 22 and 23,
|
||||
|
||||
@@ -73,7 +73,6 @@ From here, render real pages against the app shell and fetch upstream data — s
|
||||
- [Goals](#goals)
|
||||
- [Audience](#audience)
|
||||
- [Overview](#overview)
|
||||
- [Glossary](#glossary)
|
||||
- [Users, groups & permissions](#users-groups--permissions)
|
||||
- [naming a permission](#naming-a-permission)
|
||||
- [a worked example](#a-worked-example)
|
||||
@@ -211,24 +210,6 @@ feature removes the need for JS (theme switching, popovers, disclosure) we use i
|
||||
avoid is shipping a client-side runtime, not using the platform. Markup is semantic and accessible
|
||||
(see [AGENTS.md](AGENTS.md)).
|
||||
|
||||
## Glossary
|
||||
|
||||
- **Host** — the `web` container: the Node process that discovers plugins, serves every page and
|
||||
owns no state of its own. Everything outside it (a plugin's upstream, Ory) is reached over the network.
|
||||
- **Plugin** — a folder under `plugins/` that adds domain features.
|
||||
- **Manifest** — the object a plugin's `plugin.ts` exports as its default: its routes, nav,
|
||||
permissions and hooks. The host checks it at boot and refuses a bad one ([The manifest](#the-manifest)).
|
||||
- **Chrome** — everything on a page that isn't the page's own content: brand, menu, user, theme and
|
||||
CSRF token. A handler gets it as `ctx.chrome` ([RequestContext](#requestcontext)).
|
||||
- **Nav fragment** — the `nav` entries one plugin contributes. The host merges every plugin's
|
||||
fragment into the one global menu and filters it per user ([The menu system](#the-menu-system)).
|
||||
- **Permission name** — a `<resource>:<action>` string such as `scheduling:write`, granted in Keto and
|
||||
carried in the session JWT's `permissions` claim ([Naming a permission](#naming-a-permission)).
|
||||
- **System plugin** — a plugin that administers Plainpages' own identity stack, through the
|
||||
privileged `ctx.system` ([System capabilities](#system-capabilities-the-ctxsystem-surface)).
|
||||
- **Denylist** — the optional in-memory list, per instance, of subjects revoked just now, so a
|
||||
deactivation or a user's own permission change lands before the JWT expires ([Instant revoke](#instant-revoke-the-optional-denylist)).
|
||||
|
||||
## Users, groups & permissions
|
||||
|
||||
Authorization here is two hops: a **user** — directly, or through a **group** — is granted a
|
||||
@@ -812,9 +793,6 @@ included, since the plugin folder *is* the repo. A baked image needs no extra st
|
||||
above every plugin, and a plugin resolves it from there — nothing to declare, just import it. A
|
||||
copy inside your plugin's own `node_modules` would shadow it with a *second* instance of the host's
|
||||
contract, turning a sign-in redirect into a 500, so discovery refuses one there at boot.
|
||||
- **Plainpages is sized for a handful of plugins per deployment, about ten.** Each plugin carries
|
||||
its own dependency tree, so every one adds to disk use and to the memory of the `web` process;
|
||||
hundreds of plugins is not a goal.
|
||||
- **The host never upgrades or dedupes your dependencies.** Two plugins depending on the same package
|
||||
each get their own copy at their own version, so neither can break the other by upgrading — and
|
||||
keeping yours current, and audited, is yours to own. Renovate here watches every manifest in this
|
||||
|
||||
@@ -62,24 +62,3 @@ Inherited architect, decided by:
|
||||
|
||||
- Self-sufficiency, Overall: `public/css/styles.css:5` `Architecture` header: the false `light-dark()` bullet is gone, so the header no longer contradicts the TOKENS comment
|
||||
- Locality, Self-sufficiency, Overall: `public/css/styles.css:687` `@supports selector(:has(a))` in `@media (max-width: 860px)`: the guard and its comment put the dependency of the open state on `:has()` where an editor would change it
|
||||
|
||||
## 2026-10-31T01:19:49Z, PR #184 at af157c9, against bb8e6bc
|
||||
|
||||
Ruling: better
|
||||
|
||||
| Seat | Navigation | Locality | Shape | Self-sufficiency | Overall |
|
||||
|---|---|---|---|---|---|
|
||||
| Mid A | better | same | same | better | better |
|
||||
| Inherited architect | better | better | same | better | better |
|
||||
|
||||
Mid A, decided by:
|
||||
|
||||
- Self-sufficiency, Navigation, Overall: `public/css/styles.css:12` `TOKENS` header comment: says all three theme rules need `:has()`, names the engines without it and points to the `@supports not selector(:has(a))` block, where the old text called the swaps "rock-solid"
|
||||
- Navigation, Self-sufficiency, Overall: `README.md:214` `## Glossary`: defines host, plugin, manifest, chrome, nav fragment, permission name, system plugin and denylist and links each to its home section
|
||||
- Self-sufficiency: `README.md:815` `Plugin dependencies`: the scale bullet states the plugin count the per-plugin dependency design assumes
|
||||
|
||||
Inherited architect, decided by:
|
||||
|
||||
- Self-sufficiency, Locality, Overall: `public/css/styles.css:12` `1. TOKENS` comment: an editor of the theme mapping learns of the distant `:has()` fallback from the comment, where the old text said the swap could not fail
|
||||
- Navigation, Self-sufficiency, Overall: `README.md:214` `## Glossary`: a cold reader reaches "chrome" or "denylist" and their home sections in one hop
|
||||
- Self-sufficiency: `README.md:815` `### Plugin dependencies`: the scale bullet gives the plugin count the dependency design assumes
|
||||
|
||||
@@ -47,15 +47,3 @@ Read most (opens and searches):
|
||||
- `examples/plugins/scheduling/views/overview.ejs` 1
|
||||
- `examples/plugins/scheduling/views/shifts.ejs` 1
|
||||
- `src/http/app.test.ts` 1
|
||||
|
||||
## Reading cost, chunk 91d11dbe63f9, finished 2026-10-31T01:13:44Z
|
||||
|
||||
69010 new input tokens before the first commit.
|
||||
|
||||
Read most (opens and searches):
|
||||
|
||||
- `todo.md` 7
|
||||
- `README.md` 4
|
||||
- `public/css/styles.css` 4
|
||||
- `CHANGELOG.md` 1
|
||||
- `src/ui/stylesheet-floor.test.ts` 1
|
||||
|
||||
Generated
+4
-4
@@ -8,7 +8,7 @@
|
||||
"dependencies": {
|
||||
"@larvit/log": "2.3.0",
|
||||
"ejs": "6.0.1",
|
||||
"lucide-static": "1.64.0",
|
||||
"lucide-static": "1.65.0",
|
||||
"postgres": "3.4.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -399,9 +399,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-static": {
|
||||
"version": "1.64.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-static/-/lucide-static-1.64.0.tgz",
|
||||
"integrity": "sha512-UxWmMteOXbKHI5E4f2qVtxCAAsI9gsInYK1lNw0nrOA0w4FkJeOVZ+UOV6aGA2377FZe3qyiFYq3lqjBKZ6OuA==",
|
||||
"version": "1.65.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-static/-/lucide-static-1.65.0.tgz",
|
||||
"integrity": "sha512-Mbahsp1MHXzBK2qQ+j+OpdwvPwOFYFBOe2pD1O5eI+XLRpbqPY2euz6vemAddXGevRvJ5n8D83ilUCrtxP07RQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/postgres": {
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
"dependencies": {
|
||||
"@larvit/log": "2.3.0",
|
||||
"ejs": "6.0.1",
|
||||
"lucide-static": "1.64.0",
|
||||
"lucide-static": "1.65.0",
|
||||
"postgres": "3.4.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -9,17 +9,14 @@
|
||||
/* ---------- 1. TOKENS ----------------------------------------
|
||||
Two raw palettes (--l-* light, --d-* dark) are defined once.
|
||||
A small mapping (--bg: var(--l-bg) ...) picks the active set.
|
||||
Theme resolution (no JS); all three rules below need :has():
|
||||
Theme resolution (no JS):
|
||||
• default + @media(dark) → follows the OS (Auto)
|
||||
• html:has(#theme-light) → force light
|
||||
• html:has(#theme-dark) → force dark
|
||||
Engines without :has() (Chromium below 105, Firefox below 121,
|
||||
Safari below 15.4) ignore all three and stay light; see the @supports not
|
||||
selector(:has(a)) block further down.
|
||||
We avoid light-dark()/color-scheme for app colors because their
|
||||
resolution is unreliable across engines. color-scheme is still
|
||||
set as a hint for native widgets (date picker, scrollbars,
|
||||
selects).
|
||||
resolution is unreliable across engines; :has() variable swaps
|
||||
are rock-solid. color-scheme is still set as a hint for native
|
||||
widgets (date picker, scrollbars, selects).
|
||||
------------------------------------------------------------- */
|
||||
:root {
|
||||
/* Hex, never oklch: below Chromium 111 `var(--accent)` is invalid at computed-value time and the
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
|
||||
| ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 26 | 0.4.21 | | | **Add a domain glossary and the project's intended horizon to the README.** | 1 | 2 | 1 | 2 | 3 | 9 by 3, code health by 2 | 11.9 |
|
||||
| 8 | 0.4.21 | | | **Run the visual E2E specs at viewports from a large desktop down to a small phone.** | 2 | 1 | 1 | 2 | 4 | code health by 3 | 9.0 |
|
||||
| 9 | 0.4.21 | | | **Typecheck `e2e-tests/` in the gate.** | 1 | 1 | 1 | 1 | 3 | code health by 2 | 9.0 |
|
||||
| 24 | 0.4.21 | | | **Retire `src/ui/shell-context.ts` and render the dashboard from `ctx.chrome`.** | 2 | 1 | 2 | 3 | 4 | code health by 3 | 9.0 |
|
||||
| 17 | 0.4.21 | | | **Remove the icons no caller uses from `ICON_NAMES` and the sprite.** | 1 | 1 | 1 | 1 | 1 | 8 by 1, code health by 1 | 8.8 |
|
||||
| 21 | 0.4.21 | | | **Fail the gate on a function that exceeds a size or complexity limit.** | 3 | 1 | 1 | 3 | 5 | code health by 4 | 7.2 |
|
||||
| 38 | 0.4.21 | | | **Correct the TOKENS comment at the top of `public/css/styles.css` so it no longer claims `:has()` swaps are "rock-solid" or that the theme follows the OS on every engine.** | 1 | 2 | 1 | 2 | 1 | code health by 1 | 7.2 |
|
||||
| 32 | 0.4.21 | | | **Fail the gate on a stylesheet feature the browser floor lacks, unless it is guarded.** | 3 | 1 | 1 | 3 | 4 | code health by 2 | 6.0 |
|
||||
| 20 | 0.4.21 | | | **Redirect an anonymous hit on a session-needing flow page to sign-in, instead of a 500.** | 2 | 2 | 2 | 4 | 3 | 2 by 4 | 4.2 |
|
||||
| 4 | 0.4.21 | | | **Refuse a permission save whose baseline no longer matches Keto, so concurrent edits are not silently lost.** | 2 | 2 | 2 | 4 | 4 | 2 by 4 | 2.8 |
|
||||
@@ -37,6 +39,12 @@
|
||||
|
||||
## Details
|
||||
|
||||
### 26. Add a domain glossary and the project's intended horizon to the README.
|
||||
|
||||
Glossary: host, manifest, chrome, nav fragment, permission token, system plugin, denylist. Also the
|
||||
expected plugin count, since per-plugin dependency isolation prices N dependency trees on disk and
|
||||
in RSS.
|
||||
|
||||
### 24. Retire `src/ui/shell-context.ts` and render the dashboard from `ctx.chrome`.
|
||||
|
||||
`ShellModel`/`buildShellContext` has one consumer, the dashboard, and duplicates `PageChrome` on
|
||||
@@ -51,6 +59,14 @@ AGENTS.md → "`ICON_NAMES` is a host-owned registry" already makes this routine
|
||||
|
||||
Only `tsc --strict` runs today. A new tool is a new dependency, so justify it.
|
||||
|
||||
### 38. Correct the TOKENS comment at the top of `public/css/styles.css` so it no longer claims `:has()` swaps are "rock-solid" or that the theme follows the OS on every engine.
|
||||
|
||||
Section 1's TOKENS comment says the theme follows the OS by default and that `:has()` variable swaps
|
||||
are "rock-solid". On an engine without `:has()` (Chromium below 105, Firefox below 121) every theme
|
||||
rule is inert, the OS-dark rule included, so those engines stay light, as the
|
||||
`@supports not selector(:has(a))` block further down says. A reader trusting the comment expects dark
|
||||
mode there.
|
||||
|
||||
### 32. Fail the gate on a stylesheet feature the browser floor lacks, unless it is guarded.
|
||||
|
||||
Nothing checks README → "Two browser floors", and no E2E engine is old enough to show a break.
|
||||
|
||||
Reference in New Issue
Block a user