plutonium 0.60.0 → 0.60.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium-auth/SKILL.md +1 -1
- data/.claude/skills/plutonium-ui/SKILL.md +12 -0
- data/CHANGELOG.md +9 -0
- data/app/assets/plutonium.css +1 -1
- data/docs/reference/configuration.md +1 -1
- data/docs/reference/resource/definition.md +2 -2
- data/docs/reference/ui/layouts.md +37 -1
- data/docs/superpowers/plans/2026-06-14-form-sectioning.md +11 -2
- data/docs/superpowers/plans/2026-06-14-railless-portal.md +761 -0
- data/docs/superpowers/plans/2026-06-14-railless-portal.md.tasks.json +51 -0
- data/docs/superpowers/specs/2026-06-14-form-sectioning-design.md +15 -5
- data/docs/superpowers/specs/2026-06-14-railless-portal-design.md +275 -0
- data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +1 -0
- data/lib/plutonium/auth/rodauth.rb +2 -1
- data/lib/plutonium/configuration.rb +2 -1
- data/lib/plutonium/core/controller.rb +19 -0
- data/lib/plutonium/definition/form_layout.rb +5 -6
- data/lib/plutonium/ui/form/components/sticky_footer.rb +1 -1
- data/lib/plutonium/ui/layout/base.rb +5 -0
- data/lib/plutonium/ui/layout/resource_layout.rb +22 -6
- data/lib/plutonium/ui/layout/topbar.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/src/css/components.css +9 -0
- metadata +5 -2
|
@@ -35,7 +35,7 @@ Loads the baseline defaults for a given framework version. Call this first; late
|
|
|
35
35
|
| `development` | `ENV["PLUTONIUM_DEV"]` | Development mode for the framework itself (local assets, hot reload, verbose errors). Query with `config.development?`. You rarely set this in an app — see [Development mode](#development-mode). |
|
|
36
36
|
| `cache_discovery` | `true` outside `development` env | Cache resource/route discovery. Disable to pick up new resources without a reboot. |
|
|
37
37
|
| `enable_hotreload` | `true` in `development` env | Hot-reload Plutonium components on change. |
|
|
38
|
-
| `shell` | `:modern` | Chrome style: `:modern` (topbar + icon rail) or `:classic` (legacy header + sidebar, only for upgrades). See [Layouts](./ui/layouts). |
|
|
38
|
+
| `shell` | `:modern` | Chrome style: `:modern` (topbar + icon rail), `:plain` (topbar, no icon rail), or `:classic` (legacy header + sidebar, only for upgrades). See [Layouts](./ui/layouts). |
|
|
39
39
|
| `navii_host_url` | `"https://api.navii.dev"` | Host of the [Navii](https://navii.dev) avatar service used by [`Avatar`](./ui/components#avatar). The component appends `/avatar/:seed`. Repoint to self-host or proxy. |
|
|
40
40
|
| `assets.logo` | `"plutonium.png"` | Brand logo asset. See [Assets](./ui/assets). |
|
|
41
41
|
| `assets.favicon` | `"plutonium.ico"` | Favicon asset. |
|
|
@@ -562,9 +562,9 @@ class ArticleDefinition < ResourceDefinition
|
|
|
562
562
|
end
|
|
563
563
|
```
|
|
564
564
|
|
|
565
|
-
###
|
|
565
|
+
### Fields not in the permitted set are skipped
|
|
566
566
|
|
|
567
|
-
A `section` that lists a field
|
|
567
|
+
A `section` only renders the fields that are actually in the form's permitted set for the current request. A key it lists that isn't there — a typo, or a field excluded by policy, per-action `permitted_attributes`, entity scoping, or nesting — is **silently dropped**, never an error. This lets a single `form_layout` reference conditionally-permitted fields without crashing the form in the contexts where they're filtered out.
|
|
568
568
|
|
|
569
569
|
### On interactions
|
|
570
570
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# Layouts
|
|
2
2
|
|
|
3
|
-
The overall page chrome — topbar, sidebar, footer, body wrapping. Plutonium ships
|
|
3
|
+
The overall page chrome — topbar, sidebar, footer, body wrapping. Plutonium ships three shells; you can eject the templates or write a custom `ResourceLayout` for total control.
|
|
4
4
|
|
|
5
5
|
## Shell
|
|
6
6
|
|
|
7
7
|
```ruby
|
|
8
8
|
Plutonium.configure do |config|
|
|
9
9
|
config.shell = :modern # default — topbar + icon rail
|
|
10
|
+
# config.shell = :plain # topbar, no icon rail (rail-less app)
|
|
10
11
|
# config.shell = :classic # legacy header + sidebar (only when upgrading)
|
|
11
12
|
end
|
|
12
13
|
```
|
|
@@ -15,6 +16,41 @@ end
|
|
|
15
16
|
If you're starting fresh, use `:modern`. `:classic` exists so apps upgrading from pre-`:modern` versions can preserve their chrome while migrating.
|
|
16
17
|
:::
|
|
17
18
|
|
|
19
|
+
## Shell variants & the icon rail
|
|
20
|
+
|
|
21
|
+
`config.shell` selects the chrome for the whole app:
|
|
22
|
+
|
|
23
|
+
- `:modern` (default) — Topbar plus the desktop icon rail.
|
|
24
|
+
- `:plain` — Topbar but **no** icon rail. The Topbar is kept; only the rail is removed, so the whole app is rail-less.
|
|
25
|
+
- `:classic` — legacy Header/Sidebar (upgrade paths only).
|
|
26
|
+
|
|
27
|
+
### Per-controller / per-portal override
|
|
28
|
+
|
|
29
|
+
Any Plutonium resource controller exposes a class-level `rail` DSL that overrides the shell default. It's a `class_attribute`, so it's inherited: a portal opts its entire surface in or out by calling `rail false` (or `rail true`) once in its controller concern.
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
module CustomerPortal
|
|
33
|
+
module Concerns
|
|
34
|
+
module Controller
|
|
35
|
+
extend ActiveSupport::Concern
|
|
36
|
+
included { rail false } # entire portal rail-less
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`rail nil` (the default) inherits the shell default — the rail shows when `config.shell == :modern`. Read the resolved value with the `rail?` predicate.
|
|
43
|
+
|
|
44
|
+
### Stable CSS hooks
|
|
45
|
+
|
|
46
|
+
Rail-less rendering exposes a few stable hooks for custom overrides:
|
|
47
|
+
|
|
48
|
+
- `pu-topbar` — class on the Topbar nav.
|
|
49
|
+
- `pu-sticky-footer` — class on the form sticky-footer div.
|
|
50
|
+
- `html.pu-no-rail` — root class present whenever the current page is rail-less.
|
|
51
|
+
|
|
52
|
+
A built-in rule cancels the desktop rail inset on `.pu-topbar` and `.pu-sticky-footer` under `html.pu-no-rail`; target these hooks to layer your own CSS.
|
|
53
|
+
|
|
18
54
|
## Eject the chrome for per-portal customization
|
|
19
55
|
|
|
20
56
|
```bash
|
|
@@ -290,7 +290,7 @@ git commit -m "feat(forms): add form_layout/section/ungrouped DSL registry"
|
|
|
290
290
|
|
|
291
291
|
### Task 2: Resolve a field list into ordered sections
|
|
292
292
|
|
|
293
|
-
**Goal:** Add `resolve_form_sections(resource_fields)` to `FormLayout` — assign permitted fields to sections (in declared order), collect leftovers into `ungrouped` (default **last**, else at its declared position),
|
|
293
|
+
**Goal:** Add `resolve_form_sections(resource_fields)` to `FormLayout` — assign permitted fields to sections (in declared order), collect leftovers into `ungrouped` (default **last**, else at its declared position), skip section keys not in the permitted set, and keep empty sections (no hiding). _(Amended: default was "first", and unknown keys originally raised — see Amendments at the end.)_
|
|
294
294
|
|
|
295
295
|
**Files:**
|
|
296
296
|
- Modify: `lib/plutonium/definition/form_layout.rb` (add instance method `resolve_form_sections`)
|
|
@@ -302,7 +302,7 @@ git commit -m "feat(forms): add form_layout/section/ungrouped DSL registry"
|
|
|
302
302
|
- [ ] `ungrouped` collects all unclaimed permitted fields, in `resource_fields` order.
|
|
303
303
|
- [ ] Without an `ungrouped` macro, leftovers render in a heading-less section placed **last** (appended after all declared sections). _(Amended — was "first".)_
|
|
304
304
|
- [ ] With an `ungrouped` macro, leftovers render at the macro's declared position with its options.
|
|
305
|
-
- [ ] A section referencing a field
|
|
305
|
+
- [ ] A section referencing a field not in `resource_fields` (a typo, or a field filtered by policy/scoping/per-action) is **silently skipped**, not raised. _(Amended — originally raised `ArgumentError`.)_
|
|
306
306
|
- [ ] Empty sections are returned (not hidden).
|
|
307
307
|
|
|
308
308
|
> **Known-vs-permitted note:** `resolve_form_sections` only sees the policy-filtered `resource_fields`. To distinguish "typo" from "filtered out", it raises only when the field is not present in `resource_fields` AND the caller passes it as not-an-attribute. For unit purposes we treat any field not in `resource_fields` as unknown → raises. The form passes the *full* attribute set check at render via existing machinery; here we validate against `resource_fields`. Keep it strict: unknown key → raise.
|
|
@@ -898,6 +898,15 @@ steps/snippets are left as-built; these supersede them where they conflict.
|
|
|
898
898
|
render — visibility + option evaluation in one pass — and `render_form_section`
|
|
899
899
|
is pure presentation. Example: `collapsed: -> { object.persisted? }`.
|
|
900
900
|
|
|
901
|
+
- **Unknown / filtered section keys skipped, not raised** (`form_layout.rb`,
|
|
902
|
+
`resolve_form_sections`). A key not in the form's permitted set
|
|
903
|
+
(`submittable_attributes_for(action)`) is dropped silently. The original raise
|
|
904
|
+
crashed forms whenever a layout referenced a field excluded by per-action
|
|
905
|
+
`permitted_attributes`, entity scoping, nesting, or per-user policy — the
|
|
906
|
+
resolver only ever saw the filtered list, so it couldn't tell a typo from a
|
|
907
|
+
legitimately-filtered field. Test renamed
|
|
908
|
+
`test_unknown_field_raises` → `test_field_not_in_permitted_set_is_skipped_not_raised`.
|
|
909
|
+
|
|
901
910
|
- **Interactions exercised** — `ReconfigureKitchenSink` (record action on
|
|
902
911
|
`KitchenSink`) + `test/integration/admin_portal/form_layout_interaction_test.rb`
|
|
903
912
|
cover form_layout (incl. a dynamic `collapsed:`) in an interaction form, where
|