plutonium 0.66.0 → 0.68.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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium-dashboard/SKILL.md +17 -2
  3. data/.claude/skills/plutonium-resource/SKILL.md +17 -10
  4. data/.claude/skills/plutonium-ui/SKILL.md +13 -3
  5. data/CHANGELOG.md +35 -0
  6. data/SECURITY.md +1 -1
  7. data/app/assets/plutonium.css +1 -1
  8. data/app/assets/plutonium.js +56 -3
  9. data/app/assets/plutonium.js.map +4 -4
  10. data/app/assets/plutonium.min.js +32 -32
  11. data/app/assets/plutonium.min.js.map +4 -4
  12. data/config/brakeman.ignore +1 -1
  13. data/config/locales/en/dashboard.yml +1 -0
  14. data/config/locales/en/js.yml +4 -0
  15. data/config/locales/en/ui.yml +19 -0
  16. data/docs/reference/dashboard/dsl.md +54 -3
  17. data/docs/reference/resource/definition.md +14 -7
  18. data/docs/reference/resource/query.md +11 -1
  19. data/docs/reference/ui/displays.md +19 -2
  20. data/docs/reference/ui/forms.md +77 -4
  21. data/gemfiles/rails_7.gemfile +1 -0
  22. data/gemfiles/rails_7.gemfile.lock +13 -3
  23. data/gemfiles/rails_8.0.gemfile +1 -0
  24. data/gemfiles/rails_8.0.gemfile.lock +10 -1
  25. data/gemfiles/rails_8.1.gemfile +1 -0
  26. data/gemfiles/rails_8.1.gemfile.lock +10 -1
  27. data/lib/plutonium/auth/rodauth.rb +7 -0
  28. data/lib/plutonium/core/controller.rb +0 -2
  29. data/lib/plutonium/dashboard/base.rb +80 -1
  30. data/lib/plutonium/dashboard/card.rb +12 -13
  31. data/lib/plutonium/dashboard/controller.rb +28 -10
  32. data/lib/plutonium/dashboard/dsl.rb +59 -2
  33. data/lib/plutonium/dashboard/evaluation.rb +30 -0
  34. data/lib/plutonium/dashboard/filter.rb +114 -0
  35. data/lib/plutonium/dashboard/link.rb +35 -0
  36. data/lib/plutonium/dashboard/route_resolution.rb +6 -2
  37. data/lib/plutonium/helpers/content_helper.rb +0 -3
  38. data/lib/plutonium/query/filter.rb +2 -1
  39. data/lib/plutonium/query/filters/tags.rb +36 -0
  40. data/lib/plutonium/resource/query_object.rb +6 -0
  41. data/lib/plutonium/routing/dashboard_registration.rb +5 -0
  42. data/lib/plutonium/ui/avatar.rb +2 -2
  43. data/lib/plutonium/ui/component/resolves_tags.rb +13 -0
  44. data/lib/plutonium/ui/dashboard/board.rb +4 -1
  45. data/lib/plutonium/ui/dashboard/frame.rb +1 -1
  46. data/lib/plutonium/ui/dashboard/metric.rb +18 -5
  47. data/lib/plutonium/ui/dashboard/toolbar.rb +76 -0
  48. data/lib/plutonium/ui/display/base.rb +50 -2
  49. data/lib/plutonium/ui/display/components/badge.rb +1 -1
  50. data/lib/plutonium/ui/display/components/binary.rb +20 -0
  51. data/lib/plutonium/ui/display/components/boolean.rb +1 -1
  52. data/lib/plutonium/ui/display/components/code.rb +23 -0
  53. data/lib/plutonium/ui/display/components/color.rb +14 -6
  54. data/lib/plutonium/ui/display/components/duration.rb +31 -0
  55. data/lib/plutonium/ui/display/components/file_size.rb +22 -0
  56. data/lib/plutonium/ui/display/components/key_value.rb +37 -0
  57. data/lib/plutonium/ui/display/components/list.rb +33 -0
  58. data/lib/plutonium/ui/display/components/number.rb +32 -0
  59. data/lib/plutonium/ui/display/components/progress.rb +36 -0
  60. data/lib/plutonium/ui/display/components/rating.rb +36 -0
  61. data/lib/plutonium/ui/display/components/relative_time.rb +25 -0
  62. data/lib/plutonium/ui/display/components/rich_text.rb +23 -0
  63. data/lib/plutonium/ui/display/components/tags.rb +18 -0
  64. data/lib/plutonium/ui/display/options/inferred_types.rb +13 -0
  65. data/lib/plutonium/ui/display/theme.rb +24 -2
  66. data/lib/plutonium/ui/form/base.rb +50 -1
  67. data/lib/plutonium/ui/form/components/binary.rb +40 -0
  68. data/lib/plutonium/ui/form/components/key_value_store.rb +22 -71
  69. data/lib/plutonium/ui/form/components/list.rb +46 -0
  70. data/lib/plutonium/ui/form/components/rating.rb +51 -0
  71. data/lib/plutonium/ui/form/components/rich_text.rb +20 -0
  72. data/lib/plutonium/ui/form/components/secure_association.rb +31 -8
  73. data/lib/plutonium/ui/form/components/secure_association_checkboxes.rb +30 -0
  74. data/lib/plutonium/ui/form/components/secure_association_choices.rb +123 -0
  75. data/lib/plutonium/ui/form/components/secure_association_radio_buttons.rb +45 -0
  76. data/lib/plutonium/ui/form/components/secure_association_toggles.rb +18 -0
  77. data/lib/plutonium/ui/form/components/tags.rb +45 -0
  78. data/lib/plutonium/ui/form/components/tags_filter.rb +40 -0
  79. data/lib/plutonium/ui/form/components/uppy.rb +1 -1
  80. data/lib/plutonium/ui/form/options/inferred_types.rb +16 -15
  81. data/lib/plutonium/ui/form/theme.rb +34 -4
  82. data/lib/plutonium/ui/grid/card.rb +25 -5
  83. data/lib/plutonium/ui/layout/base.rb +10 -1
  84. data/lib/plutonium/ui/options/rich_text_field.rb +18 -0
  85. data/lib/plutonium/ui/options/secret_field.rb +32 -0
  86. data/lib/plutonium/ui/options/tags_field.rb +23 -0
  87. data/lib/plutonium/ui/page/dashboard.rb +1 -0
  88. data/lib/plutonium/ui/table/components/scopes_bar.rb +1 -1
  89. data/lib/plutonium/ui/table/components/scopes_pills.rb +1 -1
  90. data/lib/plutonium/ui/table/components/toolbar.rb +2 -2
  91. data/lib/plutonium/ui/table/components/view_switcher.rb +4 -2
  92. data/lib/plutonium/ui/table/display_theme.rb +30 -2
  93. data/lib/plutonium/version.rb +1 -1
  94. data/package.json +1 -1
  95. data/plutonium.gemspec +1 -1
  96. data/src/css/components.css +43 -0
  97. data/src/css/flatpickr.css +91 -0
  98. data/src/js/controllers/easymde_controller.js +1 -0
  99. data/src/js/controllers/register_controllers.js +2 -0
  100. data/src/js/controllers/slim_select_controller.js +32 -3
  101. data/src/js/controllers/timeago_controller.js +40 -0
  102. metadata +35 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b131f7d0e5b02982f4696e7d1db781f45b17f8b28f17da5fe5cc3b0a4480c7a0
4
- data.tar.gz: f700d3d65a3fc368e73315c436a7f8d2afb5244cd04b4b0be5618efbc738d26f
3
+ metadata.gz: a01423b0e39c2e985ec24ea8663cf53377817ff24814c95ce89270806f51b878
4
+ data.tar.gz: cbe95fbf1cd307e3b07cccf24cda7db7293dc15676c0ee288fe076d37102cb15
5
5
  SHA512:
6
- metadata.gz: 7dc926b3cf2fb69f5dfa4644d10c47e88a39e74f3359be2572a4ea6ea46ad216a5c0447959b0e1596f0ee6ae67190140f46acc53afa332a508e63f275440bdad
7
- data.tar.gz: 4d9f3fba2b94d24ef0db3d5b24b35b30d6c058acc46ac20107d37c1a507f804dac7d47b3a38556f02b16ac229ce9b2a4140e73216152c9e56ae7189a5c71bc00
6
+ metadata.gz: a2034d1af622ec4bfbc4c6ac00c46ed63f4a7ea6ed878ebc78b9666b109cf572dff20f60da0bc9f5e5adafdb992e76deb292c4ec08146684e3828732daf2ced7
7
+ data.tar.gz: '09f8b0ef419d7adf5df3057a271ed4659140e4308c4b7ea5ea5965554256e2c609314ecb6cd33893629773b9fab97f735b1e16872f08bb8784445cc80049a214'
@@ -77,9 +77,24 @@ end
77
77
 
78
78
  ## metric
79
79
 
80
- Return a value, or a hash: `value` (required), `previous` (computes % change), `change` (numeric percentage points or a verbatim string), `trend` (`:up`/`:down`/`:flat`, inferred), `change_label`.
80
+ Return a value, or a hash: `value` (required), `previous` (computes the change), `change` (a number or a verbatim string), `trend` (`:up`/`:down`/`:flat`, inferred), `change_label`.
81
81
 
82
- Options: `format:` (`:number` default, `:currency`, `:percentage`, `:human`, or `->(v) { ... }`), `precision:`, `unit:` (currency symbol), `prefix:`, `suffix:`, `positive:` (`:up` default, `:down` when falling is good), `change_label:`.
82
+ Options: `format:` (`:number` default, `:currency`, `:percentage`, `:human`, or `->(v) { ... }`), `precision:`, `unit:` (currency symbol), `prefix:`, `suffix:`, `positive:` (`:up` default, `:down` when falling is good), `change_label:`, `change_unit:` (`:percent` default; `:points` for a value that is itself a percentage, so 50% to 55.6% reads `+5.6 pts`).
83
+
84
+ ## Filters and toolbar links
85
+
86
+ ```ruby
87
+ filter :period, choices: {"7" => "Last 7 days", "30" => "Last 30 days"}, default: "30"
88
+ link :settings, icon: Phlex::TablerIcons::Settings, href: -> { resource_url_for(Setting) }
89
+
90
+ metric(:orders) { orders.where(created_at: filter_value(:period).to_i.days.ago..).count }
91
+ ```
92
+
93
+ - A filter's value comes from the query string; an unknown value reads as the default. Read it with `filter_value(:key)`, never `params[:key]` directly.
94
+ - Don't name a filter after a card-route param or `url_for` option (`card`, `format`, `anchor`, `host`, `use_route`, ...) or a path segment of the dashboard's route (entity param, `locale`); it raises.
95
+ - To filter by a resource's scopes, use `filter :status, scopes: Post` (choices = "All" + the definition's scopes) with `filtered(Post)` in card blocks and `href: -> { filtered_url(Post) }`. Do not hand-map filter values to `where` clauses or `q[scope]` URLs; `filtered` runs the definition's scope, so the count matches the index. One scope filter per resource. The scope block runs on the dashboard's controller, so it cannot call resource-controller helpers like `current_parent`.
96
+ - Every lazy card's frame URL carries the filter values, so lazy cards see the page's choice. Do not hand-roll query forwarding or a custom `DashboardsController` for this.
97
+ - Filters render as segmented links and links as buttons in a toolbar above the cards; neither needs a custom page class.
83
98
 
84
99
  ## chart
85
100
 
@@ -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`, `:tel`, `:password` |
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`, `:radio_buttons`, `:check_boxes` |
552
- | Files | `:file`, `:uppy`, `:attachment` |
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`, `:phone` |
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
- | `has_cents` decimal | currency (`:currency`) | delimited, 2 decimals, **no symbol** unless you add `unit:` |
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
@@ -618,8 +622,9 @@ end
618
622
 
619
623
  - `input :tag_ids` uses Rails' collection-ids setter. Permit `:tag_ids`; values are plain ids. Simplest for a scoped checkbox list.
620
624
  - `input :tags, as: :secure_association` is the native picker. It sets input_param `tag_sgids`, scopes via `choices:` (or the target policy), and validates submitted SGIDs by decoded model-id.
625
+ - `input :tags, as: :association_checkboxes` (or `:association_toggles`) is the same picker rendered as one checkbox/switch per record. `input :category, as: :association_radio_buttons` does the same for a belongs_to/has_one. They are capped by `choice_limit:` like the select but have no typeahead, so keep them to short lists.
621
626
 
622
- ⚠️ Do **not** hand-roll `collection_checkboxes_tag(choices: [[label, record.to_sgid.to_s]])`. SGIDs are non-deterministic (`to_sgid` embeds a timestamp), the component re-derives `choices` at submit and intersects the posted value against the freshly-minted SGIDs, so it never matches and the value is dropped. Use stable ids in custom checkboxes, or `secure_association` (which validates by model-id).
627
+ ⚠️ Do **not** hand-roll `collection_checkboxes_tag(choices: [[label, record.to_sgid.to_s]])`. SGIDs are non-deterministic (`to_sgid` embeds a timestamp), the component re-derives `choices` at submit and intersects the posted value against the freshly-minted SGIDs, so it never matches and the value is dropped. Use stable ids in custom checkboxes, or `as: :association_checkboxes` (which validates by model-id).
623
628
 
624
629
  ## Conditional Rendering
625
630
 
@@ -1112,7 +1117,7 @@ Only declare `index_views` explicitly if you want to **disable** one (e.g. `inde
1112
1117
  | `grid_layout :media` | `:compact` (image left) or `:media` (image on top). |
1113
1118
  | `grid_columns 3` | Override responsive column count. |
1114
1119
 
1115
- All grid slots are optional; slots pointing at unpermitted fields collapse silently.
1120
+ All grid slots are optional; slots pointing at unpermitted fields collapse silently. In the `:media` layout a record whose image does not resolve keeps the cover's space, with a neutral icon, so cards in a row stay one height. An image may be an attachment, an `http` or root-relative URL, or a `data:image/` URI.
1116
1121
 
1117
1122
  ---
1118
1123
 
@@ -1277,6 +1282,7 @@ end
1277
1282
  | Date Range | `:date_range` | `from`, `to` | `from_label:`, `to_label:` |
1278
1283
  | Select | `:select` | `value` | `choices:`, `multiple:` |
1279
1284
  | Association | `:association` | `value` | `class_name:`, `multiple:` |
1285
+ | Tags | `:tags` | `value` | `match:` (`:any` default, `:all`); no_fly_list contexts, options limited to the policy scope |
1280
1286
 
1281
1287
  **Text predicates:** `:eq`, `:not_eq`, `:contains`, `:not_contains`, `:starts_with`, `:ends_with`, `:matches`, `:not_matches`
1282
1288
  **Date predicates:** `:eq`, `:not_eq`, `:lt`, `:lteq`, `:gt`, `:gteq`
@@ -1291,6 +1297,7 @@ filter :category, with: :select, choices: -> { Category.pluck(:name) }
1291
1297
  filter :tags, with: :select, choices: %w[ruby rails js], multiple: true
1292
1298
  filter :category, with: :association
1293
1299
  filter :author, with: :association, class_name: User
1300
+ filter :labels, with: :tags # no_fly_list context
1294
1301
  ```
1295
1302
 
1296
1303
  **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`, `url_tag`, `tel_tag`, `hidden_tag` | standard HTML inputs |
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
 
@@ -324,7 +324,16 @@ render field(:title).wrapped(class: "col-span-full") { |f| f.input_tag }
324
324
  | `uppy_tag` / `file_tag` | Uppy file upload |
325
325
  | `secure_association_tag` | Association with policy-checked options |
326
326
  | `belongs_to_tag` / `has_many_tag` / `has_one_tag` | Association selects |
327
+ | `association_checkboxes_tag` / `association_toggles_tag` | has_many / habtm as checkboxes or switches (`as: :association_checkboxes` / `:association_toggles`); same policy scoping and SGID validation as the select, capped by `choice_limit:` like the select (selected records always render), no typeahead |
328
+ | `association_radio_buttons_tag` | belongs_to / has_one as a radio group (`as: :association_radio_buttons`); optional associations get a "None" choice |
327
329
  | `key_value_store_tag` | Key/value pairs editor |
330
+ | `currency_tag` | Money input (number field + unit prefix) |
331
+ | `rich_text_tag` | Action Text editor (Trix, or Lexxy once the app installs it); default for `has_rich_text` |
332
+ | `list_tag` | Chip input for array values (`suggestions:`, `limit:`, `addable: false`) |
333
+ | `tags_tag` | Chip input for a no_fly_list `has_tags` context; default for those contexts |
334
+ | `rating_tag` | Star picker (`max:`, default 5); a Clear option unless required |
335
+ | `binary_tag` | Plain file input whose bytes go into a `binary` column |
336
+ | `json_tag` | JSON editor; default for `json`/`jsonb` columns |
328
337
 
329
338
  ```ruby
330
339
  render field(:published_at).wrapped { |f| f.flatpickr_tag(min_date: Date.today, enable_time: true) }
@@ -335,11 +344,12 @@ render field(:avatar).wrapped { |f| f.uppy_tag(allowed_file_types: %w[.jpg
335
344
 
336
345
  `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
346
 
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.
347
+ 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
348
 
340
349
  ```ruby
341
350
  field :api_token, as: :string # opt OUT: show a readable value (token to copy, checksum)
342
351
  field :recovery_phrase, as: :password # opt IN: mask a secret the heuristic misses
352
+ field :pin, as: :secret # :secret is an alias of :password
343
353
  ```
344
354
 
345
355
  ## Submit buttons
@@ -1114,7 +1124,7 @@ end
1114
1124
 
1115
1125
  ### Display theme keys
1116
1126
 
1117
- `fields_wrapper`, `fields_inner`, `sections_wrapper`, `section_grid`, `label`, `description`, `string`, `text`, `link`, `email`, `phone`, `markdown`, `json`, `boolean`, `badge`, `currency`, `color`.
1127
+ `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
1128
 
1119
1129
  ⚠️ **`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
1130
 
data/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.68.0] - 2026-10-14
6
+
7
+ ### Bug Fixes
8
+
9
+ - Send card links to the whole page ([#183](https://github.com/radioactive-labs/plutonium-core/issues/183))
10
+ - Keep the grid card cover's space when a record has no image ([#184](https://github.com/radioactive-labs/plutonium-core/issues/184))
11
+
12
+ ### Features
13
+
14
+ - Filters carried to card frames, toolbar links, point changes ([#182](https://github.com/radioactive-labs/plutonium-core/issues/182))
15
+ - Checkbox, toggle and radio variants of the association input ([#185](https://github.com/radioactive-labs/plutonium-core/issues/185))
16
+ - Scope filters, plus fixes from the #182 review ([#186](https://github.com/radioactive-labs/plutonium-core/issues/186))
17
+
18
+ ## [0.67.0] - 2026-10-09
19
+
20
+ ### Bug Fixes
21
+
22
+ - Keep view switcher from clipping on narrow screens
23
+ - Restore CSRF verification for cookie sessions ([#175](https://github.com/radioactive-labs/plutonium-core/issues/175))
24
+ - Let slim-select dropdown overflow modal edge instead of clipping
25
+ - Load Font Awesome 4.7 from jsdelivr for EasyMDE toolbar icons
26
+ - Mask secret fields on every display surface
27
+ - Fixes from the component audit
28
+
29
+ ### Features
30
+
31
+ - Binary column input and display
32
+ - Key/value, list, rating and value display components
33
+ - No_fly_list tags support
34
+ - [**breaking**] Render has_rich_text through Action Text's editor
35
+
36
+ ### Testing
37
+
38
+ - Search slim-select typeaheads by email in system tests ([#177](https://github.com/radioactive-labs/plutonium-core/issues/177))
39
+
5
40
  ## [0.66.0] - 2026-10-08
6
41
 
7
42
  ### 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.66.x`) | :white_check_mark: |
15
+ | Latest release (`0.68.x`) | :white_check_mark: |
16
16
  | Older releases | :x: |
17
17
 
18
18
  ## Reporting a Vulnerability