plutonium 0.49.1 → 0.50.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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium-definition/SKILL.md +87 -2
  3. data/.claude/skills/plutonium-installation/SKILL.md +6 -0
  4. data/.claude/skills/plutonium-views/SKILL.md +59 -0
  5. data/CHANGELOG.md +12 -0
  6. data/app/assets/plutonium.css +2 -2
  7. data/app/assets/plutonium.js +369 -25
  8. data/app/assets/plutonium.js.map +4 -4
  9. data/app/assets/plutonium.min.js +45 -45
  10. data/app/assets/plutonium.min.js.map +4 -4
  11. data/app/views/plutonium/_resource_header.html.erb +4 -4
  12. data/app/views/plutonium/_resource_sidebar.html.erb +9 -9
  13. data/app/views/resource/_resource_grid.html.erb +1 -0
  14. data/config/brakeman.ignore +25 -2
  15. data/docs/reference/definition/actions.md +14 -1
  16. data/docs/reference/definition/index.md +58 -0
  17. data/docs/reference/views/index.md +43 -0
  18. data/docs/superpowers/plans/2026-05-07-ui-layout-overhaul.md +841 -0
  19. data/docs/superpowers/plans/2026-05-07-ui-layout-overhaul.md.tasks.json +103 -0
  20. data/docs/superpowers/specs/2026-05-07-ui-layout-overhaul-design.md +270 -0
  21. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  22. data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +1 -0
  23. data/lib/generators/pu/core/update/update_generator.rb +20 -0
  24. data/lib/generators/pu/lite/rails_pulse/rails_pulse_generator.rb +54 -5
  25. data/lib/plutonium/action/base.rb +44 -1
  26. data/lib/plutonium/action/interactive.rb +1 -1
  27. data/lib/plutonium/configuration.rb +4 -0
  28. data/lib/plutonium/definition/actions.rb +3 -0
  29. data/lib/plutonium/definition/base.rb +8 -0
  30. data/lib/plutonium/definition/metadata.rb +40 -0
  31. data/lib/plutonium/definition/views.rb +94 -0
  32. data/lib/plutonium/helpers/turbo_helper.rb +1 -1
  33. data/lib/plutonium/interaction/response/redirect.rb +1 -1
  34. data/lib/plutonium/query/base.rb +8 -0
  35. data/lib/plutonium/query/filters/association.rb +30 -8
  36. data/lib/plutonium/query/filters/boolean.rb +5 -0
  37. data/lib/plutonium/resource/controllers/presentable.rb +11 -2
  38. data/lib/plutonium/resource/definition.rb +42 -0
  39. data/lib/plutonium/resource/query_object.rb +64 -6
  40. data/lib/plutonium/testing/resource_definition.rb +2 -2
  41. data/lib/plutonium/ui/action_button.rb +4 -2
  42. data/lib/plutonium/ui/component/kit.rb +12 -0
  43. data/lib/plutonium/ui/display/base.rb +3 -1
  44. data/lib/plutonium/ui/display/resource.rb +109 -25
  45. data/lib/plutonium/ui/display/theme.rb +2 -1
  46. data/lib/plutonium/ui/dyna_frame/content.rb +8 -14
  47. data/lib/plutonium/ui/empty_card.rb +1 -1
  48. data/lib/plutonium/ui/form/base.rb +29 -1
  49. data/lib/plutonium/ui/form/components/hidden_wrapper.rb +25 -0
  50. data/lib/plutonium/ui/form/components/resource_select.rb +79 -1
  51. data/lib/plutonium/ui/form/components/secure_association.rb +7 -2
  52. data/lib/plutonium/ui/form/components/sticky_footer.rb +17 -0
  53. data/lib/plutonium/ui/form/resource.rb +48 -9
  54. data/lib/plutonium/ui/form/theme.rb +1 -1
  55. data/lib/plutonium/ui/frame_navigator_panel.rb +7 -4
  56. data/lib/plutonium/ui/grid/card.rb +235 -0
  57. data/lib/plutonium/ui/grid/resource.rb +149 -0
  58. data/lib/plutonium/ui/layout/base.rb +37 -1
  59. data/lib/plutonium/ui/layout/header.rb +1 -2
  60. data/lib/plutonium/ui/layout/icon_rail.rb +212 -0
  61. data/lib/plutonium/ui/layout/resource_layout.rb +10 -3
  62. data/lib/plutonium/ui/layout/sidebar.rb +12 -24
  63. data/lib/plutonium/ui/layout/topbar.rb +100 -0
  64. data/lib/plutonium/ui/modal/base.rb +109 -0
  65. data/lib/plutonium/ui/modal/centered.rb +21 -0
  66. data/lib/plutonium/ui/modal/slideover.rb +26 -0
  67. data/lib/plutonium/ui/page/base.rb +25 -6
  68. data/lib/plutonium/ui/page/edit.rb +13 -1
  69. data/lib/plutonium/ui/page/index.rb +40 -1
  70. data/lib/plutonium/ui/page/interactive_action.rb +8 -39
  71. data/lib/plutonium/ui/page/new.rb +13 -1
  72. data/lib/plutonium/ui/page/show.rb +8 -1
  73. data/lib/plutonium/ui/page_header.rb +8 -13
  74. data/lib/plutonium/ui/panel.rb +10 -19
  75. data/lib/plutonium/ui/sidebar_menu.rb +2 -25
  76. data/lib/plutonium/ui/tab_list.rb +29 -7
  77. data/lib/plutonium/ui/table/base.rb +106 -0
  78. data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +12 -4
  79. data/lib/plutonium/ui/table/components/filter_form.rb +171 -0
  80. data/lib/plutonium/ui/table/components/filter_pills.rb +89 -0
  81. data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +13 -12
  82. data/lib/plutonium/ui/table/components/scopes_pills.rb +67 -0
  83. data/lib/plutonium/ui/table/components/selection_column.rb +2 -11
  84. data/lib/plutonium/ui/table/components/toolbar.rb +104 -0
  85. data/lib/plutonium/ui/table/components/view_switcher.rb +81 -0
  86. data/lib/plutonium/ui/table/resource.rb +158 -89
  87. data/lib/plutonium/ui/table/theme.rb +14 -5
  88. data/lib/plutonium/version.rb +1 -1
  89. data/lib/plutonium.rb +6 -0
  90. data/package.json +1 -1
  91. data/src/css/components.css +304 -131
  92. data/src/css/tokens.css +101 -85
  93. data/src/js/controllers/autosubmit_controller.js +24 -0
  94. data/src/js/controllers/bulk_actions_controller.js +15 -16
  95. data/src/js/controllers/capture_url_controller.js +14 -0
  96. data/src/js/controllers/filter_panel_controller.js +77 -19
  97. data/src/js/controllers/frame_navigator_controller.js +34 -6
  98. data/src/js/controllers/icon_rail_controller.js +22 -0
  99. data/src/js/controllers/icon_rail_flyout_controller.js +128 -0
  100. data/src/js/controllers/register_controllers.js +16 -0
  101. data/src/js/controllers/resource_tab_list_controller.js +56 -3
  102. data/src/js/controllers/row_click_controller.js +21 -0
  103. data/src/js/controllers/table_column_menu_controller.js +43 -0
  104. data/src/js/controllers/table_header_controller.js +16 -0
  105. data/src/js/controllers/view_switcher_controller.js +29 -0
  106. metadata +31 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5016bbce0d2ef39825a923b820797c304a848396853f334d94757f22a90a99e9
4
- data.tar.gz: aa95cd0b1d994c093dd0e51587d5c95c3b0a527b0ca5a8f771be1a6f21fd9daa
3
+ metadata.gz: 8f93a96f45defbcd651a0c13ce62ca190dfb8fb83b7d0bca50d450f28c8ee6c0
4
+ data.tar.gz: ac996ab0486ae36f7cfda6f6a25d625c71560f4841e828c443f1cd906cd1af2f
5
5
  SHA512:
6
- metadata.gz: 6aeedc9109ad0ac05e2343981de71497ba08be96017e5af800bde70eb5035a96a027477ec2280ad78a1e9449c530e2c67290663efc28a2cc41f97c492056e1e9
7
- data.tar.gz: 5b697b2d753c9f80443210c2169f2abe42642f0d36bb1f0aa909965da6317cc1e4c90406560d9bf0156457e5a01b9a009b38250c0aed134b09328d50cb47484f
6
+ metadata.gz: 8f06360e34c7b43c797877a2555bbae11222f870e2bf82e4ec0a1ce8c0a4baf0a078cff1b65baf6159f8095345068cd1216fdcdf4d152354d46069a4d0480ad6
7
+ data.tar.gz: 663af09cc688359ef9fb41c3b4a76aa3061c9dc68b701586f03e6b09bce7b2d853b0afed60202d3b5ffa497e7b6c413b7e3c86207ac6bed9e26e1c4969400e76
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: plutonium-definition
3
- description: Use BEFORE editing a resource definition — adding fields, inputs, displays, columns, search, filters, scopes, custom actions, or bulk actions.
3
+ description: Use BEFORE editing a resource definition — adding fields, inputs, displays, columns, metadata, index views (table/grid), search, filters, scopes, custom actions, modal/slideover behavior, or bulk actions.
4
4
  ---
5
5
 
6
6
  # Plutonium Resource Definitions
@@ -582,9 +582,19 @@ class PostDefinition < ResourceDefinition
582
582
  # true = always show
583
583
  # false = always hide
584
584
  submit_and_continue false
585
+
586
+ # How `:new` / `:edit` render. Default is :slideover.
587
+ # :slideover — slide-in panel from the right (default)
588
+ # :centered — centered dialog
589
+ # false — full standalone pages (no modal)
590
+ modal :centered
585
591
  end
586
592
  ```
587
593
 
594
+ The `modal` setting only affects the framework-provided `:new` / `:edit`
595
+ actions. Custom actions render in their own dialog, controlled by the
596
+ per-action `modal:` option (`:centered` default, or `:slideover`).
597
+
588
598
  ## Page Customization
589
599
 
590
600
  ```ruby
@@ -617,6 +627,74 @@ class PostDefinition < ResourceDefinition
617
627
  end
618
628
  ```
619
629
 
630
+ ## Metadata Panel (Show Page)
631
+
632
+ The `metadata` DSL declares a list of fields rendered in the show page's
633
+ right-side aside as label/value rows. The main details card and the
634
+ metadata aside share the same field-rendering machinery, so labels and
635
+ formatting come from your existing `field` / `display` declarations.
636
+
637
+ ```ruby
638
+ class PostDefinition < ResourceDefinition
639
+ metadata :author, :state, :created_at, :updated_at
640
+ end
641
+ ```
642
+
643
+ Behavior:
644
+
645
+ - **Opt-in.** No `metadata` call → the show page renders full-width with
646
+ no aside.
647
+ - **Policy-aware.** Metadata fields are intersected with the policy's
648
+ permitted attributes. Fields the user can't see disappear from the
649
+ panel; the panel auto-hides when nothing is permitted.
650
+ - **Deduplicated.** Fields listed in `metadata` are removed from the main
651
+ details card so the same value never appears twice.
652
+ - **Responsive.** Side-by-side at `lg+`, stacked single-column below.
653
+
654
+ Use it for chrome that's not the focus of the record — timestamps,
655
+ ownership, system flags — keeping the main card focused on the record's
656
+ substance.
657
+
658
+ ## Index Views (Table & Grid)
659
+
660
+ Resources can opt into a card-based **Grid** view alongside the default
661
+ **Table** view. Users can switch between the two and the choice is
662
+ persisted per-resource via cookie.
663
+
664
+ ```ruby
665
+ class UserDefinition < ResourceDefinition
666
+ views :table, :grid # enable both; user can switch
667
+ default_view :grid # initial view if no cookie
668
+
669
+ grid_fields(
670
+ image: :avatar, # ActiveStorage attachment, Shrine, or URL
671
+ header: :name, # falls back to record.to_label
672
+ subheader: :email,
673
+ body: :bio,
674
+ meta: [:role, :status], # rendered as small pills
675
+ footer: :last_seen_at # falls back to :created_at
676
+ )
677
+
678
+ grid_layout :media # :compact (default) or :media
679
+ grid_columns 3 # pin to 3 cols on lg+; default is 1/2/3/4 responsive
680
+ end
681
+ ```
682
+
683
+ DSL surface:
684
+
685
+ | Method | Purpose |
686
+ |--------|---------|
687
+ | `views :table, :grid` | Which views are available. Default `[:table]`. |
688
+ | `default_view :grid` | Initial view when no cookie. Falls back to first view in `views`. |
689
+ | `grid_fields(...)` | Maps card slots to fields. **Implicitly enables `:grid`** if not already in `views`. |
690
+ | `grid_layout :media` | `:compact` (image left of content) or `:media` (full-width image on top). |
691
+ | `grid_columns 3` | Override responsive column count on lg+. |
692
+
693
+ Grid slots are all optional — `:image`, `:header`, `:subheader`, `:body`,
694
+ `:meta`, `:footer`. `:meta` accepts an array; the rest are single
695
+ fields. Slots that point at fields not permitted by the user's policy
696
+ collapse silently.
697
+
620
698
  ## Context in Blocks
621
699
 
622
700
  Inside `condition` procs and `input` blocks:
@@ -941,9 +1019,16 @@ action :name,
941
1019
  # Behavior
942
1020
  confirmation: "Are you sure?",
943
1021
  turbo_frame: "_top",
944
- route_options: {action: :foo}
1022
+ route_options: {action: :foo},
1023
+ modal: :slideover # :centered (default) or :slideover —
1024
+ # how the action's interaction form renders
945
1025
  ```
946
1026
 
1027
+ **`Action#with(...)`** — actions are frozen value objects. To derive a
1028
+ variant (typically inside `customize_actions`) call
1029
+ `existing_action.with(turbo_frame: nil)` for a new copy with the
1030
+ overrides applied.
1031
+
947
1032
  ### Creating an Interaction
948
1033
 
949
1034
  #### Basic Structure
@@ -211,6 +211,10 @@ rails generate pu:res:conn Post --dest=admin_portal
211
211
  Plutonium.configure do |config|
212
212
  config.load_defaults 1.0
213
213
 
214
+ # Page chrome. Default :modern (topbar + icon rail). Set :classic
215
+ # only when upgrading and you want to keep the legacy header + sidebar.
216
+ # config.shell = :classic
217
+
214
218
  # Custom assets (optional)
215
219
  # config.assets.stylesheet = "custom_stylesheet"
216
220
  # config.assets.script = "custom_script"
@@ -311,6 +315,8 @@ For models that already exist in your app:
311
315
  | `pu:res:scaffold NAME` | Create resource (model, policy, definition, controller) |
312
316
  | `pu:res:conn NAME` | Connect resource to portal |
313
317
  | `pu:eject:layout` | Eject layout files for customization |
318
+ | `pu:eject:shell` | Eject the topbar/sidebar partials so you can customize chrome per-portal |
319
+ | `pu:core:update` | Update the plutonium gem + npm package |
314
320
  | `pu:skills:sync` | Sync Claude Code skills to project |
315
321
 
316
322
  ## Related Skills
@@ -425,6 +425,65 @@ class PostDefinition < ResourceDefinition
425
425
  end
426
426
  ```
427
427
 
428
+ ## Page Chrome (Shell)
429
+
430
+ `Plutonium.configuration.shell` controls the layout shipped above the
431
+ resource pages. Default is **`:modern`** (topbar + icon rail) — only
432
+ override to **`:classic`** (legacy header + sidebar) if you're upgrading
433
+ from a pre-`:modern` version and want to preserve the old chrome:
434
+
435
+ ```ruby
436
+ Plutonium.configure do |config|
437
+ config.shell = :classic
438
+ end
439
+ ```
440
+
441
+ To customize the shipped chrome per-portal, eject the templates:
442
+
443
+ ```bash
444
+ rails generate pu:eject:shell --dest=admin_portal
445
+ ```
446
+
447
+ This copies `_resource_header.html.erb` and `_resource_sidebar.html.erb`
448
+ into the portal's `app/views/plutonium/`. The eject is independent of
449
+ `shell` — you can run it on either.
450
+
451
+ ## Modal & Slideover Forms
452
+
453
+ The framework's `:new` / `:edit` actions render inline inside a modal.
454
+ Choose the chrome per-resource via the `modal` DSL on the definition:
455
+
456
+ ```ruby
457
+ class PostDefinition < ResourceDefinition
458
+ modal :slideover # default — slide-in panel from the right
459
+ # modal :centered # centered dialog
460
+ # modal false # full standalone page (no modal)
461
+ end
462
+ ```
463
+
464
+ Custom interactive actions render in their own dialog with their own
465
+ `modal:` option (`:centered` default, or `:slideover`).
466
+
467
+ ### Detecting render context in components
468
+
469
+ Custom pages / forms can branch on render context:
470
+
471
+ | Helper | True when |
472
+ |--------|-----------|
473
+ | `in_frame?` | Request is targeting a turbo-frame |
474
+ | `in_modal?` | Request is rendering inside a modal/slideover |
475
+
476
+ Use them to pin action strips, omit nav chrome, or swap layouts.
477
+
478
+ ## Tabs & URL Hash
479
+
480
+ Show pages with associations render the **Details** tab first followed
481
+ by one tab per permitted association. The active tab is reflected in
482
+ the URL hash (`#products`, `#refund-requests`) so the page deep-links and
483
+ the active state survives reloads / back navigation.
484
+
485
+ Tab rows scroll horizontally on narrow viewports — they don't wrap.
486
+
428
487
  ## Layout Customization
429
488
 
430
489
  ### Eject Layout
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.50.0] - 2026-05-11
2
+
3
+ ### 🚀 Features
4
+
5
+ - *(ui)* UI layout overhaul: modern shell, slideovers, filters, Grid view (#52)
6
+ - *(css)* Style WebKit autofill on .pu-input variants
7
+
8
+ ### 🐛 Bug Fixes
9
+
10
+ - *(ui/form)* Suppress label and chrome for hidden fields
11
+ - *(generators/rails_pulse)* Inject recurring tasks under env blocks
12
+ - *(bulk_actions)* Selection cells survive table re-renders
1
13
  ## [0.49.1] - 2026-05-06
2
14
 
3
15
  ### 🚀 Features