plutonium 0.68.0 → 0.70.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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium-async-interactions/SKILL.md +41 -2
  3. data/.claude/skills/plutonium-resource/SKILL.md +13 -0
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +37 -0
  6. data/CLAUDE.md +11 -0
  7. data/SECURITY.md +1 -1
  8. data/app/assets/plutonium.css +3 -1
  9. data/app/assets/plutonium.png +0 -0
  10. data/config/locales/en/async.yml +11 -1
  11. data/config/locales/en/ui.yml +3 -0
  12. data/docs/blog/introducing-plutonium-dashboards.md +4 -4
  13. data/docs/blog/the-slowest-query-is-the-count.md +109 -0
  14. data/docs/guides/performance.md +14 -0
  15. data/docs/public/favicon-16x16.png +0 -0
  16. data/docs/public/favicon-32x32.png +0 -0
  17. data/docs/public/images/blog/i18n-es-admin.png +0 -0
  18. data/docs/public/images/blog/pagination-capped.png +0 -0
  19. data/docs/public/images/blog/pagination-countless.png +0 -0
  20. data/docs/public/images/components/avatar.png +0 -0
  21. data/docs/public/images/guides/custom-actions-bulk.png +0 -0
  22. data/docs/public/images/guides/dashboard-card-error.png +0 -0
  23. data/docs/public/images/guides/dashboard-loading.png +0 -0
  24. data/docs/public/images/guides/dashboard-overview.png +0 -0
  25. data/docs/public/images/guides/dashboard-sidebar.png +0 -0
  26. data/docs/public/images/guides/kanban-after-move.png +0 -0
  27. data/docs/public/images/guides/kanban-board-light.png +0 -0
  28. data/docs/public/images/guides/kanban-board.png +0 -0
  29. data/docs/public/images/guides/kanban-show-centered-modal.png +0 -0
  30. data/docs/public/images/guides/kanban-wip-toast.png +0 -0
  31. data/docs/public/images/guides/multi-tenancy-dashboard.png +0 -0
  32. data/docs/public/images/guides/multi-tenancy-welcome.png +0 -0
  33. data/docs/public/images/guides/nested-inputs.png +0 -0
  34. data/docs/public/images/guides/nested-resources-tab.png +0 -0
  35. data/docs/public/images/guides/search-filtering-index.png +0 -0
  36. data/docs/public/images/guides/search-filtering-panel.png +0 -0
  37. data/docs/public/images/guides/theming-after.png +0 -0
  38. data/docs/public/images/guides/theming-before.png +0 -0
  39. data/docs/public/images/guides/user-invites-landing.png +0 -0
  40. data/docs/public/images/guides/user-profile-edit.png +0 -0
  41. data/docs/public/images/guides/user-profile-show.png +0 -0
  42. data/docs/public/images/guides/wizards-chooser.png +0 -0
  43. data/docs/public/images/guides/wizards-completed.png +0 -0
  44. data/docs/public/images/guides/wizards-index-action.png +0 -0
  45. data/docs/public/images/guides/wizards-repeater.png +0 -0
  46. data/docs/public/images/guides/wizards-review.png +0 -0
  47. data/docs/public/images/guides/wizards-step.png +0 -0
  48. data/docs/public/images/home/tour-actions.png +0 -0
  49. data/docs/public/images/home/tour-async.png +0 -0
  50. data/docs/public/images/home/tour-kanban.png +0 -0
  51. data/docs/public/images/home/tour-tenancy.png +0 -0
  52. data/docs/public/images/home/tour-wizard.png +0 -0
  53. data/docs/public/images/home-index.png +0 -0
  54. data/docs/public/images/home-new.png +0 -0
  55. data/docs/public/images/home-show.png +0 -0
  56. data/docs/public/images/reference/async-progress-page.png +0 -0
  57. data/docs/public/images/reference/async-running-banner.png +0 -0
  58. data/docs/public/images/reference/structured-inputs-removed.png +0 -0
  59. data/docs/public/images/reference/structured-inputs.png +0 -0
  60. data/docs/public/images/tutorial/02-empty-index.png +0 -0
  61. data/docs/public/images/tutorial/02-index-with-posts.png +0 -0
  62. data/docs/public/images/tutorial/02-new-form-modal.png +0 -0
  63. data/docs/public/images/tutorial/02-new-form.png +0 -0
  64. data/docs/public/images/tutorial/03-create-account.png +0 -0
  65. data/docs/public/images/tutorial/03-login.png +0 -0
  66. data/docs/public/images/tutorial/04-admin-index.png +0 -0
  67. data/docs/public/images/tutorial/05-actions-menu.png +0 -0
  68. data/docs/public/images/tutorial/05-row-actions.png +0 -0
  69. data/docs/public/images/tutorial/06-comments-tab.png +0 -0
  70. data/docs/public/images/tutorial/06-post-with-comments.png +0 -0
  71. data/docs/public/images/tutorial/07-author-dashboard.png +0 -0
  72. data/docs/public/images/tutorial/07-author-portal.png +0 -0
  73. data/docs/public/images/tutorial/08-customized-index.png +0 -0
  74. data/docs/public/og-image.png +0 -0
  75. data/docs/public/plutonium.png +0 -0
  76. data/docs/public/tutorial/plutonium-association-panel.png +0 -0
  77. data/docs/public/tutorial/plutonium-dashboard.png +0 -0
  78. data/docs/public/tutorial/plutonium-login-page.png +0 -0
  79. data/docs/public/tutorial/plutonium-nested-form.png +0 -0
  80. data/docs/public/tutorial/plutonium-posts-dashboard-customized.png +0 -0
  81. data/docs/public/tutorial/plutonium-posts-dashboard.png +0 -0
  82. data/docs/public/tutorial/plutonium-posts-detail-customized.png +0 -0
  83. data/docs/public/tutorial/plutonium-posts-detail.png +0 -0
  84. data/docs/public/tutorial/plutonium-publish-post.png +0 -0
  85. data/docs/reference/behavior/async-interactions.md +62 -5
  86. data/docs/reference/configuration.md +1 -0
  87. data/docs/reference/generators/lite.md +30 -6
  88. data/docs/reference/i18n.md +2 -2
  89. data/docs/reference/resource/definition.md +35 -0
  90. data/docs/reference/ui/layouts.md +13 -0
  91. data/docs/superpowers/plans/2026-10-15-async-run-pre-check.md +1056 -0
  92. data/docs/superpowers/plans/2026-10-15-async-run-pre-check.md.tasks.json +49 -0
  93. data/docs/superpowers/specs/2026-10-15-async-run-pre-check-design.md +208 -0
  94. data/gemfiles/rails_7.gemfile.lock +5 -5
  95. data/gemfiles/rails_8.0.gemfile.lock +3 -3
  96. data/gemfiles/rails_8.1.gemfile.lock +3 -3
  97. data/lib/generators/pu/lite/maintenance/maintenance_generator.rb +20 -1
  98. data/lib/generators/pu/lite/maintenance/templates/app/jobs/sqlite_maintenance_job.rb.tt +18 -1
  99. data/lib/plutonium/configuration.rb +17 -0
  100. data/lib/plutonium/definition/base.rb +22 -0
  101. data/lib/plutonium/definition/presentable.rb +4 -1
  102. data/lib/plutonium/interaction/async/configuration.rb +15 -0
  103. data/lib/plutonium/interaction/async/context.rb +12 -2
  104. data/lib/plutonium/interaction/async/executor.rb +54 -0
  105. data/lib/plutonium/interaction/async/run.rb +128 -14
  106. data/lib/plutonium/interaction/async/run_definition.rb +5 -4
  107. data/lib/plutonium/interaction/async/run_policy.rb +5 -5
  108. data/lib/plutonium/interaction/response/redirect.rb +19 -7
  109. data/lib/plutonium/pagination.rb +35 -0
  110. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +27 -1
  111. data/lib/plutonium/ui/breadcrumbs.rb +26 -8
  112. data/lib/plutonium/ui/grid/card.rb +11 -2
  113. data/lib/plutonium/ui/grid/resource.rb +2 -2
  114. data/lib/plutonium/ui/interaction/async/run_progress.rb +25 -11
  115. data/lib/plutonium/ui/interaction/async/running_banner.rb +21 -5
  116. data/lib/plutonium/ui/layout/base.rb +0 -9
  117. data/lib/plutonium/ui/layout/icon_rail.rb +42 -4
  118. data/lib/plutonium/ui/page/index.rb +23 -4
  119. data/lib/plutonium/ui/table/components/filter_pills.rb +8 -2
  120. data/lib/plutonium/ui/table/components/pagy_info.rb +30 -10
  121. data/lib/plutonium/ui/table/resource.rb +2 -2
  122. data/lib/plutonium/version.rb +1 -1
  123. data/package.json +1 -1
  124. data/plutonium.gemspec +3 -1
  125. data/src/css/components.css +12 -1
  126. data/src/css/font_awesome.css +2328 -0
  127. data/src/css/plutonium.css +1 -0
  128. metadata +11 -7
  129. data/docs/public/images/blog/dashboards-card-error.png +0 -0
  130. data/docs/public/images/blog/dashboards-loading.png +0 -0
  131. data/docs/public/images/blog/dashboards-overview.png +0 -0
  132. data/docs/public/images/blog/dashboards-sidebar.png +0 -0
@@ -855,6 +855,41 @@ All three inherit to subclasses, so a portal-specific definition keeps its paren
855
855
  - **Interactions** (`Plutonium::Interaction::Base`) support the same settings, for interactive actions rendered as standalone pages.
856
856
  - **Wizards are configured separately**, via `Plutonium.configuration.wizards.width`. It defaults to `:md` independently of `default_page_width`, so widening resource pages leaves wizard steps where they are. Set both if you want them to match.
857
857
 
858
+ ## Pagination count
859
+
860
+ The index page shows its total ("Displaying Posts 1-20 of 13,742 in total") and links every page, which takes a `COUNT(*)` on each load. On a table with millions of rows that count can take seconds. `pagination` sets how much counting a resource does:
861
+
862
+ ```ruby
863
+ # config/initializers/plutonium.rb
864
+ Plutonium.configure { |config| config.pagination = {count: :exact} } # the default
865
+
866
+ class TransactionDefinition < ResourceDefinition
867
+ pagination count: 10_000
868
+ end
869
+ ```
870
+
871
+ | `count:` | Query | Total shown | Page links |
872
+ |---|---|---|---|
873
+ | `:exact` | `SELECT COUNT(*) FROM ...` | the exact total | every page |
874
+ | an Integer | `SELECT COUNT(*) FROM (SELECT 1 FROM ... LIMIT 10001)` | exact up to the cap, then "10000+" | up to the page holding row 10,000 |
875
+ | `false` | none: fetches one extra row to tell whether a next page exists | the page's range only, without "of N" | previous/next, plus the pages visited so far |
876
+
877
+ A cap makes the count cheap when many rows match, because the subquery stops after `cap + 1` of them. It does not help a filter that matches few rows: the database still scans until it has found them all, so give that filter's column an index.
878
+
879
+ Any other value raises `ArgumentError` at declaration. The setting inherits to subclasses, so a portal-specific definition keeps its parent's choice unless it sets its own.
880
+
881
+ To choose per request, for example no count only while no filter is applied, override `pagination_count` in the controller. It returns the same values:
882
+
883
+ ```ruby
884
+ class TransactionsController < ResourceController
885
+ private
886
+
887
+ def pagination_count
888
+ current_query_object.active_filter_descriptions.empty? ? false : 10_000
889
+ end
890
+ end
891
+ ```
892
+
858
893
  ## File uploads
859
894
 
860
895
  ```ruby
@@ -109,6 +109,19 @@ The sidebar/icon-rail navigation is built with `Phlexi::Menu::Builder` in the ej
109
109
  ) %>
110
110
  ```
111
111
 
112
+ ### Links pinned above the footer
113
+
114
+ `footer_menu:` takes a second `Phlexi::Menu::Builder` whose items render between the scrolling nav and the pin button, so they stay in view however long the nav gets. Items render the same way as the main menu's:
115
+
116
+ ```erb
117
+ <%= render Plutonium::UI::Layout::IconRail.new(
118
+ menu: main_menu,
119
+ footer_menu: Phlexi::Menu::Builder.new do |m|
120
+ m.item "Settings", url: settings_path, icon: Phlex::TablerIcons::Settings
121
+ end
122
+ ) %>
123
+ ```
124
+
112
125
  ### Per-item link attributes
113
126
 
114
127
  Any extra options you pass to `item` are spread straight onto the rendered `<a>`, so a menu entry can opt into `target`, `rel`, `data-*`, `aria-*`, etc. Useful for items that open in their own tab or drive a Stimulus/Turbo behavior: