plutonium 0.66.0 → 0.67.0
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-resource/SKILL.md +14 -8
- data/.claude/skills/plutonium-ui/SKILL.md +11 -3
- data/CHANGELOG.md +22 -0
- data/SECURITY.md +1 -1
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +56 -3
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +32 -32
- data/app/assets/plutonium.min.js.map +4 -4
- data/config/brakeman.ignore +1 -1
- data/config/locales/en/js.yml +4 -0
- data/config/locales/en/ui.yml +16 -0
- data/docs/reference/resource/definition.md +12 -7
- data/docs/reference/resource/query.md +11 -1
- data/docs/reference/ui/displays.md +19 -2
- data/docs/reference/ui/forms.md +54 -4
- data/gemfiles/rails_7.gemfile +1 -0
- data/gemfiles/rails_7.gemfile.lock +13 -3
- data/gemfiles/rails_8.0.gemfile +1 -0
- data/gemfiles/rails_8.0.gemfile.lock +10 -1
- data/gemfiles/rails_8.1.gemfile +1 -0
- data/gemfiles/rails_8.1.gemfile.lock +10 -1
- data/lib/plutonium/auth/rodauth.rb +7 -0
- data/lib/plutonium/core/controller.rb +0 -2
- data/lib/plutonium/helpers/content_helper.rb +0 -3
- data/lib/plutonium/query/filter.rb +2 -1
- data/lib/plutonium/query/filters/tags.rb +36 -0
- data/lib/plutonium/ui/component/resolves_tags.rb +13 -0
- data/lib/plutonium/ui/display/base.rb +50 -2
- data/lib/plutonium/ui/display/components/badge.rb +1 -1
- data/lib/plutonium/ui/display/components/binary.rb +20 -0
- data/lib/plutonium/ui/display/components/boolean.rb +1 -1
- data/lib/plutonium/ui/display/components/code.rb +23 -0
- data/lib/plutonium/ui/display/components/color.rb +14 -6
- data/lib/plutonium/ui/display/components/duration.rb +31 -0
- data/lib/plutonium/ui/display/components/file_size.rb +22 -0
- data/lib/plutonium/ui/display/components/key_value.rb +37 -0
- data/lib/plutonium/ui/display/components/list.rb +33 -0
- data/lib/plutonium/ui/display/components/number.rb +32 -0
- data/lib/plutonium/ui/display/components/progress.rb +36 -0
- data/lib/plutonium/ui/display/components/rating.rb +36 -0
- data/lib/plutonium/ui/display/components/relative_time.rb +25 -0
- data/lib/plutonium/ui/display/components/rich_text.rb +23 -0
- data/lib/plutonium/ui/display/components/tags.rb +18 -0
- data/lib/plutonium/ui/display/options/inferred_types.rb +13 -0
- data/lib/plutonium/ui/display/theme.rb +24 -2
- data/lib/plutonium/ui/form/base.rb +30 -1
- data/lib/plutonium/ui/form/components/binary.rb +40 -0
- data/lib/plutonium/ui/form/components/key_value_store.rb +22 -71
- data/lib/plutonium/ui/form/components/list.rb +46 -0
- data/lib/plutonium/ui/form/components/rating.rb +51 -0
- data/lib/plutonium/ui/form/components/rich_text.rb +20 -0
- data/lib/plutonium/ui/form/components/tags.rb +45 -0
- data/lib/plutonium/ui/form/components/tags_filter.rb +40 -0
- data/lib/plutonium/ui/form/components/uppy.rb +1 -1
- data/lib/plutonium/ui/form/options/inferred_types.rb +16 -15
- data/lib/plutonium/ui/form/theme.rb +20 -4
- data/lib/plutonium/ui/grid/card.rb +13 -1
- data/lib/plutonium/ui/layout/base.rb +10 -1
- data/lib/plutonium/ui/options/rich_text_field.rb +18 -0
- data/lib/plutonium/ui/options/secret_field.rb +32 -0
- data/lib/plutonium/ui/options/tags_field.rb +23 -0
- data/lib/plutonium/ui/table/components/toolbar.rb +2 -2
- data/lib/plutonium/ui/table/components/view_switcher.rb +4 -2
- data/lib/plutonium/ui/table/display_theme.rb +30 -2
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/src/css/components.css +43 -0
- data/src/css/flatpickr.css +91 -0
- data/src/js/controllers/easymde_controller.js +1 -0
- data/src/js/controllers/register_controllers.js +2 -0
- data/src/js/controllers/slim_select_controller.js +32 -3
- data/src/js/controllers/timeago_controller.js +40 -0
- metadata +25 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7285167839fdb5e60e89da076028dc08b719ec60125cb61faaeb26cd151b9140
|
|
4
|
+
data.tar.gz: 87fe956c9ab3cdcc92e7083a3c5c965c144661f530e4ce0cb609d3b0a8adfb06
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3cdc13417311bd847b8b721ae46da0b59b12ecb1fafe95be087e556d71ac4d053d236169f2d48fa08edf831f9d49cdfdce27c3e28229b1bf5155dafdfb30cad5
|
|
7
|
+
data.tar.gz: 539932b5d8700edf4878fde1eb3c33ed0cd518d6ebafbbcf83df92de5ff098061dabcc1c1ca1a859cdfcf1e68a3f3e6ceaa55c44499b4132f132192db5cfcd18
|
|
@@ -543,19 +543,20 @@ The field-level help keys (`:label`, `:description`, `:hint`, `:placeholder`) ar
|
|
|
543
543
|
|
|
544
544
|
| Category | Types |
|
|
545
545
|
|----------|-------|
|
|
546
|
-
| Text | `:string`, `:text`, `:email`, `:url`, `:
|
|
547
|
-
| Rich Text | `:markdown` (EasyMDE) |
|
|
548
|
-
| Numeric | `:number`, `:integer`, `:decimal`, `:range` |
|
|
546
|
+
| Text | `:string`, `:text`, `:email`, `:url`, `:phone`, `:password` (alias `:secret`) |
|
|
547
|
+
| Rich Text | `:rich_text` (Action Text editor: Trix, or Lexxy once installed; **default** for `has_rich_text`), `:markdown` (EasyMDE) |
|
|
548
|
+
| Numeric | `:number`, `:integer`, `:decimal`, `:range`, `:rating` (star picker, `max:`) |
|
|
549
549
|
| Boolean | `:toggle` / `:switch` (switch, **default** for boolean columns), `:boolean` (plain checkbox) |
|
|
550
550
|
| Date/Time | `:date`, `:time`, `:datetime` |
|
|
551
|
-
| Selection | `:select`, `:slim_select`, `:
|
|
552
|
-
|
|
|
551
|
+
| Selection | `:select`, `:slim_select`, `:collection_radio_buttons`, `:collection_checkboxes` |
|
|
552
|
+
| Lists | `:list` (chip input for array values; `suggestions:`, `addable:`, `limit:`), `:tags` (no_fly_list `has_tags` contexts, **auto-detected**) |
|
|
553
|
+
| Files | `:file`, `:uppy`, `:attachment` (Active Storage), `:binary` (bytes in a `binary` column) |
|
|
553
554
|
| Associations | `:association`, `:secure_association`, `:belongs_to`, `:has_many`, `:has_one` |
|
|
554
|
-
| Special | `:hidden`, `:color`, `:
|
|
555
|
+
| Special | `:hidden`, `:color`, `:json`, `:key_value` |
|
|
555
556
|
|
|
556
557
|
### Display Types (show / index)
|
|
557
558
|
|
|
558
|
-
`:string`, `:text`, `:email`, `:url`, `:phone`, `:markdown`, `:number`, `:integer`, `:decimal`, `:boolean`, `:badge`, `:currency`, `:color`, `:date`, `:time`, `:datetime`, `:association`, `:attachment`
|
|
559
|
+
`:string`, `:text`, `:email`, `:url`, `:phone`, `:markdown`, `:number`, `:integer`, `:decimal`, `:boolean`, `:badge`, `:currency`, `:color`, `:date`, `:time`, `:datetime`, `:association`, `:attachment`, `:key_value`, `:binary`, `:list`, `:tags`, `:rich_text`, `:relative_time`, `:code`, `:duration`, `:file_size`, `:progress`, `:rating`
|
|
559
560
|
|
|
560
561
|
#### Auto-inferred display formatting
|
|
561
562
|
|
|
@@ -565,7 +566,10 @@ These render automatically: declare an `as:` only to override or pass options:
|
|
|
565
566
|
|--------|-----------|-------|
|
|
566
567
|
| `boolean` | Yes/No pill (`:boolean`) | green "Yes" / neutral "No". Override labels: `true_label:`, `false_label:` |
|
|
567
568
|
| `enum` | colored status badge (`:badge`) | known statuses (active, pending, failed…) auto-colored; unknown values get a stable decorative color |
|
|
568
|
-
| `
|
|
569
|
+
| `binary` | size (`:binary`) | `Binary data (12 KB)`, never the bytes |
|
|
570
|
+
| `has_rich_text` | HTML (`:rich_text`) | rendered and sanitized by Action Text |
|
|
571
|
+
| no_fly_list `has_tags` | chips (`:tags`) | declare by context name (`:labels`), not `labels_list` |
|
|
572
|
+
| `has_cents` decimal | currency (`:currency`) | delimited, 2 decimals; symbol from `unit:` (display or `has_cents`), else `config.default_currency_unit` / i18n. `unit: false` drops it |
|
|
569
573
|
|
|
570
574
|
```ruby
|
|
571
575
|
display :status, as: :badge, colors: {archived: :neutral, vip: :accent} # override per-value color
|
|
@@ -1277,6 +1281,7 @@ end
|
|
|
1277
1281
|
| Date Range | `:date_range` | `from`, `to` | `from_label:`, `to_label:` |
|
|
1278
1282
|
| Select | `:select` | `value` | `choices:`, `multiple:` |
|
|
1279
1283
|
| Association | `:association` | `value` | `class_name:`, `multiple:` |
|
|
1284
|
+
| Tags | `:tags` | `value` | `match:` (`:any` default, `:all`); no_fly_list contexts, options limited to the policy scope |
|
|
1280
1285
|
|
|
1281
1286
|
**Text predicates:** `:eq`, `:not_eq`, `:contains`, `:not_contains`, `:starts_with`, `:ends_with`, `:matches`, `:not_matches`
|
|
1282
1287
|
**Date predicates:** `:eq`, `:not_eq`, `:lt`, `:lteq`, `:gt`, `:gteq`
|
|
@@ -1291,6 +1296,7 @@ filter :category, with: :select, choices: -> { Category.pluck(:name) }
|
|
|
1291
1296
|
filter :tags, with: :select, choices: %w[ruby rails js], multiple: true
|
|
1292
1297
|
filter :category, with: :association
|
|
1293
1298
|
filter :author, with: :association, class_name: User
|
|
1299
|
+
filter :labels, with: :tags # no_fly_list context
|
|
1294
1300
|
```
|
|
1295
1301
|
|
|
1296
1302
|
**Custom filter class:**
|
|
@@ -309,7 +309,7 @@ render field(:title).wrapped(class: "col-span-full") { |f| f.input_tag }
|
|
|
309
309
|
| Tag | Input |
|
|
310
310
|
|---|---|
|
|
311
311
|
| `input_tag` | text (auto-detected type) |
|
|
312
|
-
| `string_tag`, `text_tag`, `number_tag`, `email_tag`, `password_tag
|
|
312
|
+
| `string_tag`, `text_tag`, `number_tag`, `email_tag`, `password_tag` / `secret_tag`, `url_tag`, `hidden_tag` | standard HTML inputs |
|
|
313
313
|
| `checkbox_tag`, `select_tag`, `radio_button_tag` | standard |
|
|
314
314
|
| `toggle_tag` / `switch_tag` | switch-styled boolean (`as: :toggle` / `:switch`), default for boolean columns; `as: :boolean` for a plain checkbox |
|
|
315
315
|
|
|
@@ -325,6 +325,13 @@ render field(:title).wrapped(class: "col-span-full") { |f| f.input_tag }
|
|
|
325
325
|
| `secure_association_tag` | Association with policy-checked options |
|
|
326
326
|
| `belongs_to_tag` / `has_many_tag` / `has_one_tag` | Association selects |
|
|
327
327
|
| `key_value_store_tag` | Key/value pairs editor |
|
|
328
|
+
| `currency_tag` | Money input (number field + unit prefix) |
|
|
329
|
+
| `rich_text_tag` | Action Text editor (Trix, or Lexxy once the app installs it); default for `has_rich_text` |
|
|
330
|
+
| `list_tag` | Chip input for array values (`suggestions:`, `limit:`, `addable: false`) |
|
|
331
|
+
| `tags_tag` | Chip input for a no_fly_list `has_tags` context; default for those contexts |
|
|
332
|
+
| `rating_tag` | Star picker (`max:`, default 5); a Clear option unless required |
|
|
333
|
+
| `binary_tag` | Plain file input whose bytes go into a `binary` column |
|
|
334
|
+
| `json_tag` | JSON editor; default for `json`/`jsonb` columns |
|
|
328
335
|
|
|
329
336
|
```ruby
|
|
330
337
|
render field(:published_at).wrapped { |f| f.flatpickr_tag(min_date: Date.today, enable_time: true) }
|
|
@@ -335,11 +342,12 @@ render field(:avatar).wrapped { |f| f.uppy_tag(allowed_file_types: %w[.jpg
|
|
|
335
342
|
|
|
336
343
|
`password_tag` masks the stored value and **never emits the secret into the DOM**. A stored secret renders a sentinel; an untouched submit keeps it, an edit-to-new-value then failed re-render comes back blank + `required` (re-type; secrets are never echoed back), a *cleared* field comes back blank but **not** `required` (the clear may be intentional), a deliberately emptied field clears it (clear-by-blank), a typed value sets it. The sentinel is guarded by the `password-sentinel` Stimulus controller: the first edit (incl. **backspace**) wipes the whole field so a partial edit can't corrupt it.
|
|
337
344
|
|
|
338
|
-
Auto-detected by name: `password`/`token`/`salt`, `encrypted_*`, `*_password`/`*_digest`/`*_hash`/`*_token`/`*_key`/`*_salt`, or any name containing `secret`. A convenience, **not** a guarantee: odd-named secrets (`recovery_phrase`, `pin`) still leak unless masked explicitly.
|
|
345
|
+
Auto-detected by name: `password`/`token`/`salt`, `encrypted_*`, `*_password`/`*_digest`/`*_hash`/`*_token`/`*_key`/`*_salt`, or any name containing `secret`. The same names render `••••••••` on the show page, table, wizard summary and grid/kanban cards; `field`/`display` `as:` overrides it there too. A convenience, **not** a guarantee: odd-named secrets (`recovery_phrase`, `pin`) still leak unless masked explicitly.
|
|
339
346
|
|
|
340
347
|
```ruby
|
|
341
348
|
field :api_token, as: :string # opt OUT: show a readable value (token to copy, checksum)
|
|
342
349
|
field :recovery_phrase, as: :password # opt IN: mask a secret the heuristic misses
|
|
350
|
+
field :pin, as: :secret # :secret is an alias of :password
|
|
343
351
|
```
|
|
344
352
|
|
|
345
353
|
## Submit buttons
|
|
@@ -1114,7 +1122,7 @@ end
|
|
|
1114
1122
|
|
|
1115
1123
|
### Display theme keys
|
|
1116
1124
|
|
|
1117
|
-
`fields_wrapper`, `fields_inner`, `sections_wrapper`, `section_grid`, `label`, `description`, `string`, `text`, `link`, `email`, `phone`, `markdown`, `json`, `boolean`, `badge`, `currency`, `color`.
|
|
1125
|
+
`fields_wrapper`, `fields_inner`, `sections_wrapper`, `section_grid`, `label`, `description`, `string`, `text`, `link`, `email`, `phone`, `markdown`, `rich_text`, `json`, `key_value`, `list` / `list_item`, `boolean`, `badge`, `currency`, `color`, `binary`, `relative_time`, `code`, `duration`, `file_size`, `progress`, `rating`. Table cells use `Plutonium::UI::Table::DisplayTheme`, which has its own (more compact) entry for each.
|
|
1118
1126
|
|
|
1119
1127
|
⚠️ **`fields_wrapper` is the CARD, `fields_inner` is the grid.** `fields_wrapper` is merged into a `Plutonium::UI::Block` (which supplies `pu-card` itself), so putting grid classes there styles the card, not the fields. Override `fields_inner` to change the unsectioned grid, and `section_grid` to change the grid inside a `display_layout` section.
|
|
1120
1128
|
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.67.0] - 2026-10-09
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Keep view switcher from clipping on narrow screens
|
|
10
|
+
- Restore CSRF verification for cookie sessions ([#175](https://github.com/radioactive-labs/plutonium-core/issues/175))
|
|
11
|
+
- Let slim-select dropdown overflow modal edge instead of clipping
|
|
12
|
+
- Load Font Awesome 4.7 from jsdelivr for EasyMDE toolbar icons
|
|
13
|
+
- Mask secret fields on every display surface
|
|
14
|
+
- Fixes from the component audit
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
- Binary column input and display
|
|
19
|
+
- Key/value, list, rating and value display components
|
|
20
|
+
- No_fly_list tags support
|
|
21
|
+
- [**breaking**] Render has_rich_text through Action Text's editor
|
|
22
|
+
|
|
23
|
+
### Testing
|
|
24
|
+
|
|
25
|
+
- Search slim-select typeaheads by email in system tests ([#177](https://github.com/radioactive-labs/plutonium-core/issues/177))
|
|
26
|
+
|
|
5
27
|
## [0.66.0] - 2026-10-08
|
|
6
28
|
|
|
7
29
|
### Bug Fixes
|
data/SECURITY.md
CHANGED
|
@@ -12,7 +12,7 @@ please upgrade before reporting an issue to confirm it still reproduces.
|
|
|
12
12
|
|
|
13
13
|
| Version | Supported |
|
|
14
14
|
| ------- | ------------------ |
|
|
15
|
-
| Latest release (`0.
|
|
15
|
+
| Latest release (`0.67.x`) | :white_check_mark: |
|
|
16
16
|
| Older releases | :x: |
|
|
17
17
|
|
|
18
18
|
## Reporting a Vulnerability
|