plutonium 0.50.0 → 0.51.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/SKILL.md +85 -102
  3. data/.claude/skills/plutonium-app/SKILL.md +572 -0
  4. data/.claude/skills/plutonium-auth/SKILL.md +163 -300
  5. data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
  6. data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
  7. data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
  8. data/.claude/skills/plutonium-testing/SKILL.md +6 -5
  9. data/.claude/skills/plutonium-ui/SKILL.md +900 -0
  10. data/CHANGELOG.md +27 -2
  11. data/Rakefile +2 -1
  12. data/app/assets/plutonium.css +1 -11
  13. data/app/assets/plutonium.js +1009 -1214
  14. data/app/assets/plutonium.js.map +3 -3
  15. data/app/assets/plutonium.min.js +52 -51
  16. data/app/assets/plutonium.min.js.map +3 -3
  17. data/docs/.vitepress/config.ts +37 -27
  18. data/docs/getting-started/index.md +22 -29
  19. data/docs/getting-started/installation.md +37 -80
  20. data/docs/getting-started/tutorial/index.md +4 -5
  21. data/docs/guides/adding-resources.md +66 -377
  22. data/docs/guides/authentication.md +94 -463
  23. data/docs/guides/authorization.md +124 -370
  24. data/docs/guides/creating-packages.md +94 -296
  25. data/docs/guides/custom-actions.md +121 -441
  26. data/docs/guides/index.md +22 -42
  27. data/docs/guides/multi-tenancy.md +116 -187
  28. data/docs/guides/nested-resources.md +103 -431
  29. data/docs/guides/search-filtering.md +123 -240
  30. data/docs/guides/testing.md +5 -4
  31. data/docs/guides/theming.md +157 -407
  32. data/docs/guides/troubleshooting.md +5 -3
  33. data/docs/guides/user-invites.md +106 -425
  34. data/docs/guides/user-profile.md +76 -243
  35. data/docs/index.md +1 -1
  36. data/docs/reference/app/generators.md +517 -0
  37. data/docs/reference/app/index.md +158 -0
  38. data/docs/reference/app/packages.md +146 -0
  39. data/docs/reference/app/portals.md +377 -0
  40. data/docs/reference/auth/accounts.md +230 -0
  41. data/docs/reference/auth/index.md +88 -0
  42. data/docs/reference/auth/profile.md +185 -0
  43. data/docs/reference/behavior/controllers.md +395 -0
  44. data/docs/reference/behavior/index.md +22 -0
  45. data/docs/reference/behavior/interactions.md +341 -0
  46. data/docs/reference/behavior/policies.md +417 -0
  47. data/docs/reference/index.md +56 -49
  48. data/docs/reference/resource/actions.md +423 -0
  49. data/docs/reference/resource/definition.md +508 -0
  50. data/docs/reference/resource/index.md +50 -0
  51. data/docs/reference/resource/model.md +348 -0
  52. data/docs/reference/resource/query.md +305 -0
  53. data/docs/reference/tenancy/entity-scoping.md +361 -0
  54. data/docs/reference/tenancy/index.md +36 -0
  55. data/docs/reference/tenancy/invites.md +393 -0
  56. data/docs/reference/tenancy/nested-resources.md +267 -0
  57. data/docs/reference/testing/index.md +287 -0
  58. data/docs/reference/ui/assets.md +400 -0
  59. data/docs/reference/ui/components.md +165 -0
  60. data/docs/reference/ui/displays.md +104 -0
  61. data/docs/reference/ui/forms.md +284 -0
  62. data/docs/reference/ui/index.md +30 -0
  63. data/docs/reference/ui/layouts.md +106 -0
  64. data/docs/reference/ui/pages.md +189 -0
  65. data/docs/reference/ui/tables.md +117 -0
  66. data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
  67. data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
  68. data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
  69. data/gemfiles/rails_7.gemfile.lock +1 -1
  70. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  71. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  72. data/lib/generators/pu/core/update/update_generator.rb +0 -20
  73. data/lib/generators/pu/invites/install_generator.rb +1 -0
  74. data/lib/plutonium/definition/base.rb +1 -1
  75. data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
  76. data/lib/plutonium/helpers/turbo_helper.rb +11 -0
  77. data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
  78. data/lib/plutonium/resource/controller.rb +1 -0
  79. data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
  80. data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
  81. data/lib/plutonium/resource/policy.rb +7 -0
  82. data/lib/plutonium/routing/mapper_extensions.rb +15 -0
  83. data/lib/plutonium/ui/component/methods.rb +4 -0
  84. data/lib/plutonium/ui/form/base.rb +6 -2
  85. data/lib/plutonium/ui/form/components/json.rb +58 -0
  86. data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
  87. data/lib/plutonium/ui/form/components/secure_association.rb +98 -22
  88. data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
  89. data/lib/plutonium/ui/form/resource.rb +0 -4
  90. data/lib/plutonium/ui/grid/resource.rb +1 -1
  91. data/lib/plutonium/ui/layout/base.rb +1 -0
  92. data/lib/plutonium/ui/page/base.rb +0 -7
  93. data/lib/plutonium/ui/page/index.rb +4 -4
  94. data/lib/plutonium/ui/table/resource.rb +1 -1
  95. data/lib/plutonium/version.rb +1 -1
  96. data/lib/plutonium.rb +8 -0
  97. data/lib/tasks/release.rake +15 -1
  98. data/package.json +10 -10
  99. data/src/css/slim_select.css +4 -0
  100. data/src/js/controllers/slim_select_controller.js +61 -0
  101. data/src/js/turbo/turbo_actions.js +33 -0
  102. data/yarn.lock +553 -543
  103. metadata +44 -33
  104. data/.claude/skills/plutonium-assets/SKILL.md +0 -512
  105. data/.claude/skills/plutonium-controller/SKILL.md +0 -396
  106. data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
  107. data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
  108. data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
  109. data/.claude/skills/plutonium-forms/SKILL.md +0 -465
  110. data/.claude/skills/plutonium-installation/SKILL.md +0 -331
  111. data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
  112. data/.claude/skills/plutonium-invites/SKILL.md +0 -408
  113. data/.claude/skills/plutonium-model/SKILL.md +0 -440
  114. data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
  115. data/.claude/skills/plutonium-package/SKILL.md +0 -198
  116. data/.claude/skills/plutonium-policy/SKILL.md +0 -456
  117. data/.claude/skills/plutonium-portal/SKILL.md +0 -410
  118. data/.claude/skills/plutonium-views/SKILL.md +0 -651
  119. data/docs/reference/assets/index.md +0 -496
  120. data/docs/reference/controller/index.md +0 -412
  121. data/docs/reference/definition/actions.md +0 -462
  122. data/docs/reference/definition/fields.md +0 -383
  123. data/docs/reference/definition/index.md +0 -326
  124. data/docs/reference/definition/query.md +0 -351
  125. data/docs/reference/generators/index.md +0 -648
  126. data/docs/reference/interaction/index.md +0 -449
  127. data/docs/reference/model/features.md +0 -248
  128. data/docs/reference/model/index.md +0 -218
  129. data/docs/reference/policy/index.md +0 -456
  130. data/docs/reference/portal/index.md +0 -379
  131. data/docs/reference/views/forms.md +0 -411
  132. data/docs/reference/views/index.md +0 -544
@@ -1,465 +0,0 @@
1
- ---
2
- name: plutonium-forms
3
- description: Use BEFORE customizing a form template, field builder, or input component in Plutonium. Also when overriding Form in a definition.
4
- ---
5
-
6
- # Plutonium Forms
7
-
8
- ## 🚨 Critical (read first)
9
- - **Use `pu:field:input NAME` for custom input components.** Don't hand-write Phlexi field classes — the generator registers them correctly.
10
- - **Configure inputs in the definition, render them in the form.** `input :foo, as: :markdown` in the definition; `render_resource_field :foo` in the custom form template.
11
- - **Override via `class Form < Form` in the definition.** Don't replace the form root class.
12
- - **`render_actions` renders the submit buttons.** Always call it at the end of a custom `form_template` or the form won't submit.
13
- - **Related skills:** `plutonium-definition` (input configuration), `plutonium-views` (custom page classes), `plutonium-assets` (theming), `plutonium-interaction` (interaction forms).
14
-
15
- **Use generators for custom field types:**
16
- - `rails g pu:field:input NAME` creates a custom form input component
17
- - `rails g pu:field:renderer NAME` creates a custom display renderer
18
-
19
- Plutonium forms are built on [Phlexi::Form](https://github.com/radioactive-labs/phlexi-form), providing a Ruby-first approach to form building with Phlex components.
20
-
21
- ## Form Class Hierarchy
22
-
23
- ```
24
- Phlexi::Form::Base
25
- └── Plutonium::UI::Form::Base # Base with Plutonium components
26
- ├── Plutonium::UI::Form::Resource # Resource CRUD forms
27
- │ └── Plutonium::UI::Form::Interaction # Interactive action forms
28
- └── Plutonium::UI::Form::Query # Search/filter forms
29
- ```
30
-
31
- ## Customizing Resource Forms
32
-
33
- ### Override in Definition
34
-
35
- ```ruby
36
- class PostDefinition < ResourceDefinition
37
- class Form < Form
38
- def form_template
39
- # Your custom form layout
40
- render_fields
41
- render_actions
42
- end
43
- end
44
- end
45
- ```
46
-
47
- ### Form Template Methods
48
-
49
- | Method | Description |
50
- |--------|-------------|
51
- | `form_template` | Main template method to override |
52
- | `render_fields` | Render all permitted fields |
53
- | `render_resource_field(name)` | Render a single field by name |
54
- | `render_actions` | Render submit buttons |
55
- | `fields_wrapper { }` | Wrapper div for field grid |
56
- | `actions_wrapper { }` | Wrapper div for buttons |
57
-
58
- ### Form Attributes
59
-
60
- | Attribute | Description |
61
- |-----------|-------------|
62
- | `object` / `record` | The form object being edited |
63
- | `resource_fields` | Array of permitted field names |
64
- | `resource_definition` | The definition instance |
65
-
66
- ## Custom Form Layout
67
-
68
- ### Sectioned Form
69
-
70
- ```ruby
71
- class PostDefinition < ResourceDefinition
72
- class Form < Form
73
- def form_template
74
- section("Basic Information") {
75
- render_resource_field :title
76
- render_resource_field :slug
77
- }
78
-
79
- section("Content") {
80
- render_resource_field :content
81
- render_resource_field :excerpt
82
- }
83
-
84
- section("Publishing") {
85
- render_resource_field :published_at
86
- render_resource_field :category
87
- }
88
-
89
- render_actions
90
- end
91
-
92
- private
93
-
94
- def section(title, &)
95
- div(class: "mb-8") {
96
- h3(class: "text-lg font-semibold mb-4 text-gray-900 dark:text-white") { title }
97
- fields_wrapper(&)
98
- }
99
- end
100
- end
101
- end
102
- ```
103
-
104
- ### Two-Column Layout
105
-
106
- ```ruby
107
- class Form < Form
108
- def form_template
109
- div(class: "grid grid-cols-1 lg:grid-cols-3 gap-6") {
110
- # Main content - 2 columns
111
- div(class: "lg:col-span-2") {
112
- fields_wrapper {
113
- render_resource_field :title
114
- render_resource_field :content
115
- }
116
- }
117
-
118
- # Sidebar - 1 column
119
- div(class: "space-y-4") {
120
- Panel {
121
- h4(class: "font-medium mb-2") { "Settings" }
122
- render_resource_field :status
123
- render_resource_field :visibility
124
- }
125
- }
126
- }
127
-
128
- render_actions
129
- end
130
- end
131
- ```
132
-
133
- ## Field Builder
134
-
135
- When using `render_resource_field`, Plutonium uses the field builder. For custom rendering, use the `field` method directly.
136
-
137
- ### Basic Field Usage
138
-
139
- ```ruby
140
- def form_template
141
- # Using field builder directly
142
- render field(:title).wrapped {|f| f.input_tag }
143
- render field(:content).wrapped {|f| f.easymde_tag }
144
- render field(:published).wrapped {|f| f.checkbox_tag }
145
-
146
- render_actions
147
- end
148
- ```
149
-
150
- ### Field Builder Methods
151
-
152
- The field builder (`f`) provides these tag methods:
153
-
154
- | Method | Input Type |
155
- |--------|------------|
156
- | `f.input_tag` | Text input (auto-detects type) |
157
- | `f.string_tag` | Text input |
158
- | `f.text_tag` | Textarea |
159
- | `f.number_tag` | Number input |
160
- | `f.email_tag` | Email input |
161
- | `f.password_tag` | Password input |
162
- | `f.url_tag` | URL input |
163
- | `f.tel_tag` | Telephone input |
164
- | `f.hidden_tag` | Hidden input |
165
- | `f.checkbox_tag` | Checkbox |
166
- | `f.select_tag` | Select dropdown |
167
- | `f.radio_button_tag` | Radio buttons |
168
-
169
- ### Plutonium-Enhanced Tags
170
-
171
- | Method | Description |
172
- |--------|-------------|
173
- | `f.easymde_tag` / `f.markdown_tag` | Markdown editor (EasyMDE) |
174
- | `f.slim_select_tag` | Enhanced select (Slim Select) |
175
- | `f.flatpickr_tag` | Date/time picker (Flatpickr) |
176
- | `f.phone_tag` / `f.int_tel_input_tag` | International phone input |
177
- | `f.uppy_tag` / `f.file_tag` | File upload (Uppy) |
178
- | `f.secure_association_tag` | Association with authorization |
179
- | `f.belongs_to_tag` | Belongs-to association |
180
- | `f.has_many_tag` | Has-many association |
181
- | `f.has_one_tag` | Has-one association |
182
- | `f.key_value_store_tag` | Key-value pairs |
183
-
184
- ### Field with Options
185
-
186
- ```ruby
187
- # Select with choices
188
- render field(:status).wrapped { |f|
189
- f.select_tag(choices: %w[draft published archived])
190
- }
191
-
192
- # Date picker with options
193
- render field(:published_at).wrapped { |f|
194
- f.flatpickr_tag(min_date: Date.today, enable_time: true)
195
- }
196
-
197
- # File upload with restrictions
198
- render field(:avatar).wrapped { |f|
199
- f.uppy_tag(
200
- allowed_file_types: %w[.jpg .png .gif],
201
- max_file_size: 5.megabytes
202
- )
203
- }
204
- ```
205
-
206
- ### Wrapped vs Unwrapped
207
-
208
- ```ruby
209
- # Wrapped - includes label, hint, errors
210
- render field(:title).wrapped { |f| f.input_tag }
211
-
212
- # Unwrapped - just the input element
213
- render field(:title).input_tag
214
-
215
- # Custom wrapper options
216
- render field(:title).wrapped(class: "col-span-full") { |f|
217
- f.input_tag
218
- }
219
- ```
220
-
221
- ## Input Configuration in Definitions
222
-
223
- Define inputs in the definition, render them in the form:
224
-
225
- ```ruby
226
- class PostDefinition < ResourceDefinition
227
- # Configure inputs
228
- input :title, hint: "Be descriptive", placeholder: "Enter title"
229
- input :content, as: :markdown
230
- input :status, as: :select, choices: %w[draft published]
231
- input :published_at, as: :flatpickr
232
-
233
- # Custom input with block
234
- input :category do |f|
235
- choices = Category.active.pluck(:name, :id)
236
- f.select_tag(choices: choices)
237
- end
238
-
239
- class Form < Form
240
- def form_template
241
- # render_resource_field uses the input configuration
242
- render_resource_field :title
243
- render_resource_field :content
244
- render_resource_field :status
245
- render_resource_field :published_at
246
- render_resource_field :category
247
-
248
- render_actions
249
- end
250
- end
251
- end
252
- ```
253
-
254
- ## Dynamic Forms (pre_submit)
255
-
256
- Fields with `pre_submit: true` trigger form re-rendering on change:
257
-
258
- ```ruby
259
- class PostDefinition < ResourceDefinition
260
- input :post_type, as: :select,
261
- choices: %w[article video podcast],
262
- pre_submit: true
263
-
264
- input :video_url,
265
- condition: -> { object.post_type == "video" }
266
-
267
- input :podcast_url,
268
- condition: -> { object.post_type == "podcast" }
269
- end
270
- ```
271
-
272
- When `post_type` changes, the form re-renders via Turbo and shows/hides conditional fields.
273
-
274
- ## Nested Forms
275
-
276
- For `has_many` / `has_one` associations with `accepts_nested_attributes_for`:
277
-
278
- ### Model Setup
279
-
280
- ```ruby
281
- class Post < ResourceRecord
282
- has_many :comments
283
- accepts_nested_attributes_for :comments, allow_destroy: true
284
- end
285
- ```
286
-
287
- ### Definition Setup
288
-
289
- ```ruby
290
- class PostDefinition < ResourceDefinition
291
- nested_input :comments do |n|
292
- n.input :author_name
293
- n.input :body, as: :text
294
- end
295
-
296
- # Or reference another definition
297
- nested_input :comments, using: CommentDefinition, fields: %i[author_name body]
298
- end
299
- ```
300
-
301
- ### Custom Nested Rendering
302
-
303
- ```ruby
304
- class Form < Form
305
- def form_template
306
- render_resource_field :title
307
- render_resource_field :content
308
-
309
- # Nested fields are automatically handled
310
- render_resource_field :comments
311
-
312
- render_actions
313
- end
314
- end
315
- ```
316
-
317
- ## Interaction Forms
318
-
319
- Forms for interactive actions use `Plutonium::UI::Form::Interaction`:
320
-
321
- ```ruby
322
- class PublishPostInteraction < ResourceInteraction
323
- attribute :publish_date, :date
324
- attribute :notify_subscribers, :boolean, default: true
325
-
326
- # Custom form
327
- class Form < Form
328
- def form_template
329
- div(class: "space-y-4") {
330
- render_resource_field :publish_date
331
- render_resource_field :notify_subscribers
332
- }
333
- render_actions
334
- end
335
- end
336
- end
337
- ```
338
-
339
- ## Form Actions
340
-
341
- ### Submit and Continue Button
342
-
343
- Forms include a secondary button ("Create and add another" for new records, "Update and continue editing" for existing). Control this in your definition:
344
-
345
- ```ruby
346
- class PostDefinition < ResourceDefinition
347
- # nil (default) = auto-detect (hidden for singular resources, shown for plural)
348
- # true = always show
349
- # false = always hide
350
- submit_and_continue false
351
- end
352
- ```
353
-
354
- Singular resources (e.g., `resource :profile` routes or `has_one` nested) auto-hide this button.
355
-
356
- ### Default Actions
357
-
358
- ```ruby
359
- def render_actions
360
- actions_wrapper {
361
- # "Create and add another" / "Update and continue editing" button (if enabled)
362
- # Primary submit button
363
- render submit_button
364
- }
365
- end
366
- ```
367
-
368
- ### Custom Actions
369
-
370
- ```ruby
371
- def render_actions
372
- actions_wrapper {
373
- # Cancel link
374
- a(href: resource_url_for(resource_class), class: "btn btn-secondary") {
375
- "Cancel"
376
- }
377
-
378
- # Save as draft
379
- button(type: :submit, name: "draft", value: "1", class: "btn") {
380
- "Save Draft"
381
- }
382
-
383
- # Primary submit
384
- render submit_button
385
- }
386
- end
387
- ```
388
-
389
- ## Form Context
390
-
391
- Inside form templates, you have access to:
392
-
393
- ```ruby
394
- class Form < Form
395
- def form_template
396
- # Form object
397
- object # The record
398
- record # Alias for object
399
- object.new_record? # Check if creating
400
-
401
- # Request context
402
- current_user
403
- current_parent
404
- current_scoped_entity
405
- request
406
- params
407
-
408
- # Definition
409
- resource_definition
410
- resource_fields # Permitted fields
411
-
412
- # URL helpers
413
- resource_url_for(object)
414
- resource_url_for(Post, action: :new)
415
-
416
- # Rails helpers
417
- helpers.link_to(...)
418
- end
419
- end
420
- ```
421
-
422
- ## Theming
423
-
424
- Forms use a theme system for consistent styling:
425
-
426
- ```ruby
427
- class PostDefinition < ResourceDefinition
428
- class Form < Form
429
- class Theme < Plutonium::UI::Form::Theme
430
- def self.theme
431
- super.merge({
432
- fields_wrapper: "grid grid-cols-2 gap-6",
433
- actions_wrapper: "flex justify-between mt-8",
434
- input: "w-full p-3 border-2 rounded-lg",
435
- label: "block mb-1 font-bold text-gray-700"
436
- })
437
- end
438
- end
439
- end
440
- end
441
- ```
442
-
443
- ### Theme Keys
444
-
445
- | Key | Description |
446
- |-----|-------------|
447
- | `base` | Form container |
448
- | `fields_wrapper` | Grid wrapper for fields |
449
- | `actions_wrapper` | Wrapper for buttons |
450
- | `wrapper` | Individual field wrapper |
451
- | `label` | Label styling |
452
- | `input` | Input styling |
453
- | `hint` | Hint text styling |
454
- | `error` | Error message styling |
455
- | `button` | Submit button styling |
456
- | `checkbox` | Checkbox styling |
457
- | `select` | Select styling |
458
-
459
- ## Related Skills
460
-
461
- - `plutonium-definition` - Input configuration (as:, hint:, condition:)
462
- - `plutonium-views` - Custom page classes
463
- - `plutonium-assets` - TailwindCSS and component theming
464
- - `plutonium-interaction` - Interactive action forms
465
- - `plutonium-nested-resources` - Parent/child forms