plutonium 0.41.0 → 0.42.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium-create-resource/SKILL.md +13 -0
  3. data/CHANGELOG.md +30 -0
  4. data/app/assets/plutonium.css +2 -2
  5. data/app/assets/plutonium.js +46 -1
  6. data/app/assets/plutonium.js.map +4 -4
  7. data/app/assets/plutonium.min.js +32 -32
  8. data/app/assets/plutonium.min.js.map +4 -4
  9. data/docs/guides/user-invites.md +1 -1
  10. data/docs/reference/generators/index.md +57 -1
  11. data/gemfiles/rails_7.gemfile.lock +1 -1
  12. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  13. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  14. data/lib/generators/pu/invites/templates/app/interactions/invite_user_interaction.rb.tt +2 -0
  15. data/lib/generators/pu/invites/templates/app/interactions/user_invite_user_interaction.rb.tt +3 -1
  16. data/lib/generators/pu/invites/templates/invitable/invite_user_interaction.rb.tt +3 -1
  17. data/lib/generators/pu/lib/plutonium_generators/concerns/package_selector.rb +1 -1
  18. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +84 -15
  19. data/lib/generators/pu/rodauth/account_generator.rb +20 -5
  20. data/lib/generators/pu/rodauth/admin_generator.rb +1 -1
  21. data/lib/generators/pu/rodauth/concerns/configuration.rb +1 -0
  22. data/lib/generators/pu/rodauth/concerns/gem_helpers.rb +19 -0
  23. data/lib/generators/pu/rodauth/install_generator.rb +7 -3
  24. data/lib/generators/pu/rodauth/migration/active_record/base.erb +4 -4
  25. data/lib/generators/pu/rodauth/migration_generator.rb +7 -0
  26. data/lib/generators/pu/rodauth/templates/app/models/account.rb.tt +1 -1
  27. data/lib/generators/pu/saas/USAGE +10 -1
  28. data/lib/generators/pu/saas/api_client/USAGE +32 -0
  29. data/lib/generators/pu/saas/api_client/templates/app/interactions/create_interaction.rb.tt +80 -0
  30. data/lib/generators/pu/saas/api_client/templates/app/interactions/disable_interaction.rb.tt +15 -0
  31. data/lib/generators/pu/saas/api_client/templates/lib/tasks/api_client.rake.tt +48 -0
  32. data/lib/generators/pu/saas/api_client_generator.rb +254 -0
  33. data/lib/generators/pu/saas/entity_generator.rb +5 -3
  34. data/lib/generators/pu/saas/membership_generator.rb +21 -9
  35. data/lib/generators/pu/saas/setup_generator.rb +23 -0
  36. data/lib/plutonium/api_client/concerns/create_api_client.rb +256 -0
  37. data/lib/plutonium/api_client/concerns/disable_api_client.rb +64 -0
  38. data/lib/plutonium/api_client.rb +21 -0
  39. data/lib/plutonium/interaction/concerns/scoping.rb +68 -0
  40. data/lib/plutonium/interaction/response/render.rb +16 -1
  41. data/lib/plutonium/invites/concerns/invite_user.rb +1 -1
  42. data/lib/plutonium/version.rb +1 -1
  43. data/package.json +1 -1
  44. data/src/js/controllers/clipboard_controller.js +37 -0
  45. data/src/js/controllers/register_controllers.js +2 -0
  46. data/src/js/controllers/remote_modal_controller.js +18 -4
  47. metadata +13 -2
@@ -0,0 +1,256 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module ApiClient
5
+ module Concerns
6
+ # CreateApiClient provides the core logic for creating API client accounts.
7
+ #
8
+ # Include this in your CreateInteraction and implement the required methods.
9
+ #
10
+ # @example Basic usage
11
+ # class ApiClient::CreateInteraction < Plutonium::Resource::Interaction
12
+ # include Plutonium::ApiClient::Concerns::CreateApiClient
13
+ #
14
+ # input :role, as: :select, choices: OrganizationApiClient.roles.keys
15
+ #
16
+ # def membership_class
17
+ # OrganizationApiClient
18
+ # end
19
+ #
20
+ # def role
21
+ # attributes[:role] || "read_only"
22
+ # end
23
+ # end
24
+ #
25
+ module CreateApiClient
26
+ extend ActiveSupport::Concern
27
+ include Plutonium::Interaction::Concerns::Scoping
28
+
29
+ included do
30
+ presents label: "Create API Client", icon: Phlex::TablerIcons::Key
31
+
32
+ attribute :login, :string
33
+
34
+ validates :login, presence: true
35
+ end
36
+
37
+ def execute
38
+ password = generate_secure_password
39
+
40
+ rodauth_instance.create_account(
41
+ login: login,
42
+ password: password
43
+ )
44
+
45
+ # Rodauth internal_request returns nil, so we need to find the account
46
+ api_client = api_client_class.find_by!(login: login)
47
+
48
+ create_membership!(api_client) if entity_scoped_api_client?
49
+
50
+ succeed(api_client).with_render_response(
51
+ credentials_page_class.new(
52
+ login: api_client.login,
53
+ password: password,
54
+ parent: scoped_parent
55
+ )
56
+ )
57
+ rescue ActiveRecord::RecordNotFound => e
58
+ failed(login: "Failed to create account: #{e.message}")
59
+ rescue => e
60
+ failed(login: e.message)
61
+ end
62
+
63
+ private
64
+
65
+ # Override to specify the Rodauth configuration name
66
+ # @return [Symbol]
67
+ def rodauth_name
68
+ raise NotImplementedError, "#{self.class}#rodauth_name must return the Rodauth configuration name (e.g., :api_client)"
69
+ end
70
+
71
+ # Override to specify the API client model class
72
+ # @return [Class]
73
+ def api_client_class
74
+ raise NotImplementedError, "#{self.class}#api_client_class must return the API client model class"
75
+ end
76
+
77
+ # Override to specify the entity model class for scoping
78
+ # @return [Class, nil]
79
+ def entity_class
80
+ nil
81
+ end
82
+
83
+ # Override to specify the membership model class
84
+ # @return [Class, nil]
85
+ def membership_class
86
+ nil
87
+ end
88
+
89
+ # Override to specify the role to assign
90
+ # @return [String, Symbol, nil]
91
+ def role
92
+ nil
93
+ end
94
+
95
+ # Override to add additional attributes when creating the membership
96
+ # @return [Hash]
97
+ def additional_membership_attributes
98
+ {}
99
+ end
100
+
101
+ # Override to customize the credentials page class
102
+ # @return [Class]
103
+ def credentials_page_class
104
+ CredentialsPage
105
+ end
106
+
107
+ # Override to customize password generation
108
+ # @return [String]
109
+ def generate_secure_password
110
+ SecureRandom.base64(32)
111
+ end
112
+
113
+ def rodauth_instance
114
+ RodauthApp.rodauth(rodauth_name)
115
+ end
116
+
117
+ def entity_scoped_api_client?
118
+ entity_class.present? && membership_class.present? && scoped_entity_id.present?
119
+ end
120
+
121
+ def scoped_entity
122
+ return unless entity_class
123
+
124
+ scoped_record_of_type(entity_class)
125
+ end
126
+
127
+ def scoped_entity_id
128
+ scoped_entity&.id
129
+ end
130
+
131
+ def create_membership!(api_client)
132
+ attrs = {
133
+ entity_foreign_key => scoped_entity_id,
134
+ api_client_foreign_key => api_client.id,
135
+ **additional_membership_attributes
136
+ }
137
+ attrs[:role] = role if role.present?
138
+
139
+ membership_class.create!(attrs)
140
+ end
141
+
142
+ def entity_foreign_key
143
+ :"#{entity_class.model_name.singular}_id"
144
+ end
145
+
146
+ def api_client_foreign_key
147
+ :"#{api_client_class.model_name.singular}_id"
148
+ end
149
+
150
+ # Default credentials page - can be overridden
151
+ class CredentialsPage < Plutonium::UI::Page::Base
152
+ def initialize(login:, password:, parent: nil)
153
+ @login = login
154
+ @password = password
155
+ @parent = parent
156
+ end
157
+
158
+ def view_template
159
+ div(class: "max-w-2xl mx-auto py-8") do
160
+ render_success_banner
161
+ render_credentials_card
162
+ render_action_buttons
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def render_success_banner
169
+ div(class: "bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-6 mb-6") do
170
+ div(class: "flex items-center gap-3 mb-4") do
171
+ render_check_icon
172
+ h2(class: "text-xl font-semibold text-green-800 dark:text-green-200") { success_title }
173
+ end
174
+
175
+ p(class: "text-green-700 dark:text-green-300") do
176
+ strong { "Important: " }
177
+ plain "Save these credentials now. The password cannot be retrieved later."
178
+ end
179
+ end
180
+ end
181
+
182
+ def render_credentials_card
183
+ div(class: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-6 space-y-4") do
184
+ render_credential_field("Login", @login)
185
+ render_credential_field("Password", @password)
186
+ end
187
+ end
188
+
189
+ def render_credential_field(label, value)
190
+ div(class: "space-y-1", data: {controller: "clipboard"}) do
191
+ label(class: "block text-sm font-medium text-gray-700 dark:text-gray-300") { label }
192
+ div(class: "flex items-center gap-2") do
193
+ input(
194
+ type: "text",
195
+ value: value,
196
+ readonly: true,
197
+ data: {clipboard_target: "source"},
198
+ class: "flex-1 px-3 py-2 bg-gray-50 dark:bg-gray-900 border border-gray-300 dark:border-gray-600 rounded-md font-mono text-sm select-all focus:ring-2 focus:ring-primary-500"
199
+ )
200
+ button(
201
+ type: "button",
202
+ data: {action: "clipboard#copy"},
203
+ class: "px-3 py-2 text-sm bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-md transition-colors"
204
+ ) { "Copy" }
205
+ end
206
+ end
207
+ end
208
+
209
+ def render_action_buttons
210
+ credentials_text = "Login: #{@login}\nPassword: #{@password}"
211
+
212
+ div(class: "mt-6 flex gap-4", data: {controller: "clipboard"}) do
213
+ input(type: "hidden", value: credentials_text, data: {clipboard_target: "source"})
214
+ button(
215
+ type: "button",
216
+ data: {action: "clipboard#copy"},
217
+ class: "px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-md transition-colors"
218
+ ) { "Copy All" }
219
+
220
+ a(
221
+ href: done_url,
222
+ class: "px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white rounded-md transition-colors"
223
+ ) { "Done" }
224
+ end
225
+ end
226
+
227
+ def render_check_icon
228
+ svg(
229
+ class: "w-8 h-8 text-green-600 dark:text-green-400",
230
+ fill: "none",
231
+ stroke: "currentColor",
232
+ viewBox: "0 0 24 24"
233
+ ) do |s|
234
+ s.path(
235
+ stroke_linecap: "round",
236
+ stroke_linejoin: "round",
237
+ stroke_width: "2",
238
+ d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
239
+ )
240
+ end
241
+ end
242
+
243
+ # Override in subclass to customize
244
+ def success_title
245
+ "API Client Created Successfully"
246
+ end
247
+
248
+ # Override in subclass to customize the done URL
249
+ def done_url
250
+ helpers.url_for(action: :index)
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module ApiClient
5
+ module Concerns
6
+ # DisableApiClient provides the core logic for disabling API client accounts.
7
+ #
8
+ # Include this in your DisableInteraction and implement the required methods.
9
+ #
10
+ # @example Basic usage
11
+ # class ApiClient::DisableInteraction < Plutonium::Resource::Interaction
12
+ # include Plutonium::ApiClient::Concerns::DisableApiClient
13
+ #
14
+ # def rodauth_name
15
+ # :api_client
16
+ # end
17
+ # end
18
+ #
19
+ module DisableApiClient
20
+ extend ActiveSupport::Concern
21
+
22
+ included do
23
+ presents label: "Disable",
24
+ description: "Disable this API client (cannot be undone)",
25
+ icon: Phlex::TablerIcons::Ban,
26
+ color: :danger
27
+
28
+ attribute :resource
29
+
30
+ validates :resource, presence: true
31
+ end
32
+
33
+ def execute
34
+ login = resource.login
35
+
36
+ rodauth_instance.close_account(account_login: login)
37
+
38
+ succeed(resource).with_message(success_message(login))
39
+ rescue => e
40
+ failed(base: e.message)
41
+ end
42
+
43
+ private
44
+
45
+ # Override to specify the Rodauth configuration name
46
+ # @return [Symbol]
47
+ def rodauth_name
48
+ raise NotImplementedError, "#{self.class}#rodauth_name must return the Rodauth configuration name (e.g., :api_client)"
49
+ end
50
+
51
+ # Override to customize success message
52
+ # @param login [String] the login of the disabled API client
53
+ # @return [String]
54
+ def success_message(login)
55
+ "API client '#{login}' has been disabled"
56
+ end
57
+
58
+ def rodauth_instance
59
+ RodauthApp.rodauth(rodauth_name)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ # ApiClient module provides concerns for building API client account interactions.
5
+ #
6
+ # @example Creating an API client interaction
7
+ # class ApiClient::CreateInteraction < Plutonium::Resource::Interaction
8
+ # include Plutonium::ApiClient::Concerns::CreateApiClient
9
+ #
10
+ # def rodauth_name
11
+ # :api_client
12
+ # end
13
+ #
14
+ # def api_client_class
15
+ # ApiClient
16
+ # end
17
+ # end
18
+ #
19
+ module ApiClient
20
+ end
21
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ module Interaction
5
+ module Concerns
6
+ # Scoping concern provides access to scoped records from the controller context.
7
+ #
8
+ # This handles both:
9
+ # - Entity scoping: Portal-level multi-tenancy via `scope_to_entity` (accessed via `current_scoped_entity`)
10
+ # - Parent scoping: Nested routes (accessed via `current_parent`)
11
+ #
12
+ # The `scoped_record_of_type` method checks both contexts and ensures type safety.
13
+ #
14
+ # @example Using in an interaction
15
+ # class MyInteraction < Plutonium::Resource::Interaction
16
+ # include Plutonium::Interaction::Concerns::Scoping
17
+ #
18
+ # def execute
19
+ # organization = scoped_record_of_type(Organization)
20
+ # # Returns the Organization from either entity or parent scope
21
+ # end
22
+ # end
23
+ #
24
+ module Scoping
25
+ extend ActiveSupport::Concern
26
+
27
+ private
28
+
29
+ # Returns a scoped record that matches the expected type.
30
+ #
31
+ # Checks both entity scoping (`current_scoped_entity`) and parent scoping (`current_parent`),
32
+ # returning the first match that is an instance of the specified class.
33
+ #
34
+ # @param klass [Class] the expected model class
35
+ # @return [Object, nil] the scoped record if found and type matches, nil otherwise
36
+ def scoped_record_of_type(klass)
37
+ [current_scoped_entity, current_parent].find { |record| record.is_a?(klass) }
38
+ end
39
+
40
+ # Returns the parent record from the controller (nested routes).
41
+ #
42
+ # @return [Object, nil] the current parent or nil
43
+ def current_parent
44
+ view_context.controller.current_parent
45
+ rescue NoMethodError
46
+ nil
47
+ end
48
+
49
+ # Returns the entity record from the controller (portal multi-tenancy).
50
+ #
51
+ # @return [Object, nil] the current scoped entity or nil
52
+ def current_scoped_entity
53
+ view_context.controller.current_scoped_entity
54
+ rescue NoMethodError
55
+ nil
56
+ end
57
+
58
+ # Returns the appropriate parent for URL generation.
59
+ # Prefers entity scope over parent scope.
60
+ #
61
+ # @return [Object, nil] the entity or parent, whichever is available
62
+ def scoped_parent
63
+ current_scoped_entity || current_parent
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -12,7 +12,22 @@ module Plutonium
12
12
  # @param controller [ActionController::Base] The controller instance.
13
13
  # @return [void]
14
14
  def execute(controller)
15
- controller.render(*@args, @options)
15
+ render_args = @args
16
+ render_options = @options
17
+
18
+ controller.instance_eval do
19
+ respond_to do |format|
20
+ format.turbo_stream do
21
+ # For Turbo requests, replace the form with the rendered content
22
+ render turbo_stream: turbo_stream.replace(
23
+ "interaction-form",
24
+ view_context.render(*render_args, **render_options)
25
+ )
26
+ end
27
+
28
+ format.any { render(*render_args, **render_options) }
29
+ end
30
+ end
16
31
  end
17
32
  end
18
33
  end
@@ -24,7 +24,7 @@ module Plutonium
24
24
  extend ActiveSupport::Concern
25
25
 
26
26
  included do
27
- presents label: "Invite User", icon: Phlex::TablerIcons::Mail
27
+ include Plutonium::Interaction::Concerns::Scoping
28
28
 
29
29
  attribute :resource
30
30
  attribute :email
@@ -1,5 +1,5 @@
1
1
  module Plutonium
2
- VERSION = "0.41.0"
2
+ VERSION = "0.42.0"
3
3
  NEXT_MAJOR_VERSION = VERSION.split(".").tap { |v|
4
4
  v[1] = v[1].to_i + 1
5
5
  v[2] = 0
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radioactive-labs/plutonium",
3
- "version": "0.41.0",
3
+ "version": "0.42.0",
4
4
  "description": "Build production-ready Rails apps in minutes, not days. Convention-driven, fully customizable, AI-ready.",
5
5
  "type": "module",
6
6
  "main": "src/js/core.js",
@@ -0,0 +1,37 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = ["source"]
5
+
6
+ copy(event) {
7
+ const text = this.sourceTarget.value || this.sourceTarget.textContent
8
+ const button = event.currentTarget
9
+ const originalText = button.textContent
10
+
11
+ navigator.clipboard.writeText(text).then(() => {
12
+ button.textContent = "Copied!"
13
+ setTimeout(() => {
14
+ button.textContent = originalText
15
+ }, 2000)
16
+ }).catch((err) => {
17
+ // Fallback for browsers that don't support clipboard API
18
+ console.warn("Clipboard API failed, using fallback:", err)
19
+ this.fallbackCopy(text)
20
+ button.textContent = "Copied!"
21
+ setTimeout(() => {
22
+ button.textContent = originalText
23
+ }, 2000)
24
+ })
25
+ }
26
+
27
+ fallbackCopy(text) {
28
+ const textarea = document.createElement("textarea")
29
+ textarea.value = text
30
+ textarea.style.position = "fixed"
31
+ textarea.style.opacity = "0"
32
+ document.body.appendChild(textarea)
33
+ textarea.select()
34
+ document.execCommand("copy")
35
+ document.body.removeChild(textarea)
36
+ }
37
+ }
@@ -23,6 +23,7 @@ import KeyValueStoreController from "./key_value_store_controller.js"
23
23
  import BulkActionsController from "./bulk_actions_controller.js"
24
24
  import FilterPanelController from "./filter_panel_controller.js"
25
25
  import TextareaAutogrowController from "./textarea_autogrow_controller.js"
26
+ import ClipboardController from "./clipboard_controller.js"
26
27
 
27
28
  export default function (application) {
28
29
  // Register controllers here
@@ -50,4 +51,5 @@ export default function (application) {
50
51
  application.register("bulk-actions", BulkActionsController)
51
52
  application.register("filter-panel", FilterPanelController)
52
53
  application.register("textarea-autogrow", TextareaAutogrowController)
54
+ application.register("clipboard", ClipboardController)
53
55
  }
@@ -3,8 +3,13 @@ import { Controller } from "@hotwired/stimulus";
3
3
  // Connects to data-controller="remote-modal"
4
4
  export default class extends Controller {
5
5
  connect() {
6
- // Store original scroll position
6
+ // Store original scroll position and body overflow
7
7
  this.originalScrollPosition = window.scrollY;
8
+ this.originalOverflow = document.body.style.overflow;
9
+ this.bodyStateRestored = false;
10
+
11
+ // Lock body scroll
12
+ document.body.style.overflow = "hidden";
8
13
 
9
14
  // Show the modal
10
15
  this.element.showModal();
@@ -15,17 +20,26 @@ export default class extends Controller {
15
20
  close() {
16
21
  // Close the modal
17
22
  this.element.close();
18
- // Restore the original scroll position
19
- window.scrollTo(0, this.originalScrollPosition);
23
+ this.restoreBodyState();
20
24
  }
21
25
 
22
26
  disconnect() {
23
27
  // Clean up event listener when controller is disconnected
24
28
  this.element.removeEventListener("close", this.handleClose);
29
+ this.restoreBodyState();
25
30
  }
26
31
 
27
32
  handleClose() {
28
- // Restore the original scroll position after dialog closes
33
+ this.restoreBodyState();
34
+ }
35
+
36
+ restoreBodyState() {
37
+ if (this.bodyStateRestored) return;
38
+ this.bodyStateRestored = true;
39
+
40
+ // Restore body overflow
41
+ document.body.style.overflow = this.originalOverflow || "";
42
+ // Restore the original scroll position
29
43
  window.scrollTo(0, this.originalScrollPosition);
30
44
  }
31
45
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plutonium
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.41.0
4
+ version: 0.42.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Froelich
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-09 00:00:00.000000000 Z
11
+ date: 2026-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -745,6 +745,7 @@ files:
745
745
  - lib/generators/pu/rodauth/concerns/account_selector.rb
746
746
  - lib/generators/pu/rodauth/concerns/configuration.rb
747
747
  - lib/generators/pu/rodauth/concerns/feature_selector.rb
748
+ - lib/generators/pu/rodauth/concerns/gem_helpers.rb
748
749
  - lib/generators/pu/rodauth/install_generator.rb
749
750
  - lib/generators/pu/rodauth/migration/active_record/account_expiration.erb
750
751
  - lib/generators/pu/rodauth/migration/active_record/active_sessions.erb
@@ -799,6 +800,11 @@ files:
799
800
  - lib/generators/pu/rodauth/templates/lib/tasks/rodauth_admin.rake.tt
800
801
  - lib/generators/pu/rodauth/views_generator.rb
801
802
  - lib/generators/pu/saas/USAGE
803
+ - lib/generators/pu/saas/api_client/USAGE
804
+ - lib/generators/pu/saas/api_client/templates/app/interactions/create_interaction.rb.tt
805
+ - lib/generators/pu/saas/api_client/templates/app/interactions/disable_interaction.rb.tt
806
+ - lib/generators/pu/saas/api_client/templates/lib/tasks/api_client.rake.tt
807
+ - lib/generators/pu/saas/api_client_generator.rb
802
808
  - lib/generators/pu/saas/entity/USAGE
803
809
  - lib/generators/pu/saas/entity_generator.rb
804
810
  - lib/generators/pu/saas/membership/USAGE
@@ -825,6 +831,9 @@ files:
825
831
  - lib/plutonium/action/route_options.rb
826
832
  - lib/plutonium/action/simple.rb
827
833
  - lib/plutonium/action_policy/sti_policy_lookup.rb
834
+ - lib/plutonium/api_client.rb
835
+ - lib/plutonium/api_client/concerns/create_api_client.rb
836
+ - lib/plutonium/api_client/concerns/disable_api_client.rb
828
837
  - lib/plutonium/auth.rb
829
838
  - lib/plutonium/auth/public.rb
830
839
  - lib/plutonium/auth/rodauth.rb
@@ -859,6 +868,7 @@ files:
859
868
  - lib/plutonium/helpers/turbo_stream_actions_helper.rb
860
869
  - lib/plutonium/interaction/README.md
861
870
  - lib/plutonium/interaction/base.rb
871
+ - lib/plutonium/interaction/concerns/scoping.rb
862
872
  - lib/plutonium/interaction/concerns/workflow_dsl.rb
863
873
  - lib/plutonium/interaction/nested_attributes.rb
864
874
  - lib/plutonium/interaction/outcome.rb
@@ -1023,6 +1033,7 @@ files:
1023
1033
  - src/js/controllers/attachment_preview_container_controller.js
1024
1034
  - src/js/controllers/attachment_preview_controller.js
1025
1035
  - src/js/controllers/bulk_actions_controller.js
1036
+ - src/js/controllers/clipboard_controller.js
1026
1037
  - src/js/controllers/color_mode_controller.js
1027
1038
  - src/js/controllers/easymde_controller.js
1028
1039
  - src/js/controllers/filter_panel_controller.js