plutonium 0.51.0 → 0.52.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.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium-app/SKILL.md +2 -0
- data/.claude/skills/plutonium-auth/SKILL.md +6 -4
- data/.claude/skills/plutonium-behavior/SKILL.md +1 -1
- data/.claude/skills/plutonium-tenancy/SKILL.md +25 -6
- data/.claude/skills/plutonium-testing/SKILL.md +3 -1
- data/.claude/skills/plutonium-ui/SKILL.md +3 -3
- data/CHANGELOG.md +17 -0
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +1 -0
- data/app/assets/plutonium.js.map +3 -3
- data/app/assets/plutonium.min.js +1 -1
- data/app/assets/plutonium.min.js.map +3 -3
- data/docs/.vitepress/config.ts +1 -2
- data/docs/.vitepress/theme/components/HomeAudienceSplit.vue +53 -0
- data/docs/.vitepress/theme/components/HomeCta.vue +108 -0
- data/docs/.vitepress/theme/components/HomeHero.vue +70 -0
- data/docs/.vitepress/theme/components/HomeInTheBox.vue +74 -0
- data/docs/.vitepress/theme/components/HomePillars.vue +42 -0
- data/docs/.vitepress/theme/components/HomeStopWriting.vue +49 -0
- data/docs/.vitepress/theme/components/HomeWalkthrough.vue +111 -0
- data/docs/.vitepress/theme/components/SectionLanding.vue +115 -0
- data/docs/.vitepress/theme/custom.css +144 -0
- data/docs/.vitepress/theme/index.ts +58 -1
- data/docs/getting-started/index.md +33 -50
- data/docs/getting-started/tutorial/02-first-resource.md +17 -8
- data/docs/getting-started/tutorial/03-authentication.md +31 -23
- data/docs/getting-started/tutorial/05-custom-actions.md +9 -4
- data/docs/getting-started/tutorial/06-nested-resources.md +7 -1
- data/docs/getting-started/tutorial/07-author-portal.md +8 -0
- data/docs/getting-started/tutorial/08-customizing-ui.md +4 -0
- data/docs/guides/authentication.md +10 -5
- data/docs/guides/authorization.md +3 -3
- data/docs/guides/creating-packages.md +8 -11
- data/docs/guides/custom-actions.md +6 -1
- data/docs/guides/customizing-ui.md +258 -0
- data/docs/guides/index.md +49 -32
- data/docs/guides/multi-tenancy.md +10 -2
- data/docs/guides/nested-resources.md +69 -0
- data/docs/guides/search-filtering.md +6 -0
- data/docs/guides/testing.md +5 -1
- data/docs/guides/theming.md +13 -0
- data/docs/guides/user-invites.md +10 -4
- data/docs/guides/user-profile.md +8 -0
- data/docs/index.md +10 -219
- data/docs/public/asciinema/home-scaffold.cast +305 -0
- data/docs/public/images/guides/custom-actions-bulk.png +0 -0
- data/docs/public/images/guides/multi-tenancy-dashboard.png +0 -0
- data/docs/public/images/guides/multi-tenancy-welcome.png +0 -0
- data/docs/public/images/guides/nested-inputs.png +0 -0
- data/docs/public/images/guides/nested-resources-tab.png +0 -0
- data/docs/public/images/guides/search-filtering-index.png +0 -0
- data/docs/public/images/guides/search-filtering-panel.png +0 -0
- data/docs/public/images/guides/theming-after.png +0 -0
- data/docs/public/images/guides/theming-before.png +0 -0
- data/docs/public/images/guides/user-invites-landing.png +0 -0
- data/docs/public/images/guides/user-profile-edit.png +0 -0
- data/docs/public/images/guides/user-profile-show.png +0 -0
- data/docs/public/images/home-index.png +0 -0
- data/docs/public/images/home-new.png +0 -0
- data/docs/public/images/home-show.png +0 -0
- data/docs/public/images/tutorial/02-empty-index.png +0 -0
- data/docs/public/images/tutorial/02-index-with-posts.png +0 -0
- data/docs/public/images/tutorial/02-new-form-modal.png +0 -0
- data/docs/public/images/tutorial/02-new-form.png +0 -0
- data/docs/public/images/tutorial/03-create-account.png +0 -0
- data/docs/public/images/tutorial/03-login.png +0 -0
- data/docs/public/images/tutorial/04-admin-index.png +0 -0
- data/docs/public/images/tutorial/05-actions-menu.png +0 -0
- data/docs/public/images/tutorial/05-row-actions.png +0 -0
- data/docs/public/images/tutorial/06-comments-tab.png +0 -0
- data/docs/public/images/tutorial/06-post-with-comments.png +0 -0
- data/docs/public/images/tutorial/07-author-dashboard.png +0 -0
- data/docs/public/images/tutorial/07-author-portal.png +0 -0
- data/docs/public/images/tutorial/08-customized-index.png +0 -0
- data/docs/reference/app/generators.md +4 -4
- data/docs/reference/auth/accounts.md +6 -7
- data/docs/reference/auth/index.md +1 -1
- data/docs/reference/behavior/policies.md +1 -1
- data/docs/reference/index.md +67 -55
- data/docs/reference/resource/definition.md +1 -1
- data/docs/reference/tenancy/entity-scoping.md +8 -1
- data/docs/reference/tenancy/index.md +1 -1
- data/docs/reference/tenancy/invites.md +12 -5
- data/docs/reference/ui/tables.md +8 -4
- data/docs/superpowers/plans/2026-05-15-public-pages-overhaul.md +1648 -0
- data/docs/superpowers/plans/2026-05-15-public-pages-overhaul.md.tasks.json +109 -0
- data/docs/superpowers/specs/2026-05-15-public-pages-overhaul-design.md +263 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/assets/assets_generator.rb +10 -0
- data/lib/generators/pu/invites/install_generator.rb +44 -0
- data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +1 -0
- data/lib/generators/pu/profile/conn_generator.rb +2 -2
- data/lib/generators/pu/res/conn/conn_generator.rb +33 -6
- data/lib/generators/pu/res/model/templates/model.rb.tt +4 -0
- data/lib/generators/pu/rodauth/account_generator.rb +2 -1
- data/lib/generators/pu/rodauth/admin_generator.rb +0 -2
- data/lib/generators/pu/rodauth/migration_generator.rb +0 -2
- data/lib/generators/pu/rodauth/views_generator.rb +0 -2
- data/lib/generators/pu/saas/membership/USAGE +4 -1
- data/lib/generators/pu/saas/setup_generator.rb +16 -4
- data/lib/generators/pu/saas/welcome/templates/app/controllers/welcome_controller.rb.tt +1 -1
- data/lib/plutonium/helpers/turbo_helper.rb +19 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +4 -4
- data/lib/plutonium/resource/controllers/interactive_actions.rb +3 -3
- data/lib/plutonium/ui/component/methods.rb +1 -0
- data/lib/plutonium/ui/form/base.rb +17 -1
- data/lib/plutonium/ui/form/components/secure_association.rb +11 -6
- data/lib/plutonium/ui/form/interaction.rb +1 -1
- data/lib/plutonium/ui/form/theme.rb +1 -1
- data/lib/plutonium/ui/page/edit.rb +1 -1
- data/lib/plutonium/ui/page/new.rb +1 -1
- data/lib/plutonium/ui/table/components/filter_form.rb +12 -4
- data/lib/plutonium/version.rb +1 -1
- data/package.json +4 -1
- data/src/js/controllers/form_controller.js +5 -4
- data/yarn.lock +108 -1
- metadata +45 -3
|
@@ -16,6 +16,9 @@ module Pu
|
|
|
16
16
|
class_option :entity, type: :string, required: true,
|
|
17
17
|
desc: "The entity model name (e.g., Organization)"
|
|
18
18
|
|
|
19
|
+
class_option :dest, type: :string, default: "main_app",
|
|
20
|
+
desc: "Destination feature/package for entity, membership, and api_client (default: main_app)"
|
|
21
|
+
|
|
19
22
|
class_option :allow_signup, type: :boolean, default: true,
|
|
20
23
|
desc: "Whether to allow users to sign up to the platform"
|
|
21
24
|
|
|
@@ -52,6 +55,9 @@ module Pu
|
|
|
52
55
|
class_option :profile, type: :boolean, default: true,
|
|
53
56
|
desc: "Generate user profile resource"
|
|
54
57
|
|
|
58
|
+
class_option :profile_attributes, type: :array, default: %w[name:string],
|
|
59
|
+
desc: "Additional attributes for the user profile model (default: name:string)"
|
|
60
|
+
|
|
55
61
|
class_option :api_client, type: :string, default: nil,
|
|
56
62
|
desc: "Generate an API client model (e.g., ApiClient)"
|
|
57
63
|
|
|
@@ -130,9 +136,15 @@ module Pu
|
|
|
130
136
|
end
|
|
131
137
|
|
|
132
138
|
def generate_profile
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
139
|
+
klass = Rails::Generators.find_by_namespace("pu:profile:setup")
|
|
140
|
+
profile_options = {
|
|
141
|
+
user_model: options[:user],
|
|
142
|
+
dest: options[:dest],
|
|
143
|
+
force: options[:force],
|
|
144
|
+
skip: options[:skip]
|
|
145
|
+
}
|
|
146
|
+
profile_options[:portal] = portal_package if options[:portal]
|
|
147
|
+
klass.new([nil, *options[:profile_attributes]], profile_options).invoke_all
|
|
136
148
|
end
|
|
137
149
|
|
|
138
150
|
def generate_welcome
|
|
@@ -145,7 +157,7 @@ module Pu
|
|
|
145
157
|
|
|
146
158
|
def generate_invites
|
|
147
159
|
generate "pu:invites:install",
|
|
148
|
-
"--entity-model=#{options[:entity]} --user-model=#{options[:user]} --dest
|
|
160
|
+
"--entity-model=#{options[:entity]} --user-model=#{options[:user]} --dest=#{options[:dest]}" \
|
|
149
161
|
" --rodauth=#{rodauth_config}"
|
|
150
162
|
end
|
|
151
163
|
|
|
@@ -63,7 +63,7 @@ class WelcomeController < AuthenticatedController
|
|
|
63
63
|
<% end -%>
|
|
64
64
|
|
|
65
65
|
def portal_root_path(<%= entity_table %>)
|
|
66
|
-
<%= portal_engine %>::Engine.routes.url_helpers.<%= entity_table %>
|
|
66
|
+
<%= portal_engine %>::Engine.routes.url_helpers.<%= entity_table %>_scoped_root_path(<%= entity_table %>_scoped: <%= entity_table %>)
|
|
67
67
|
end
|
|
68
68
|
helper_method :portal_root_path
|
|
69
69
|
end
|
|
@@ -19,6 +19,25 @@ module Plutonium
|
|
|
19
19
|
def remote_modal_frame_tag(&)
|
|
20
20
|
turbo_frame_tag(Plutonium::REMOTE_MODAL_FRAME, &)
|
|
21
21
|
end
|
|
22
|
+
|
|
23
|
+
# Returns a turbo-frame-scoped element id. Two identically-named forms
|
|
24
|
+
# can be on the page simultaneously (e.g. a primary modal opens a
|
|
25
|
+
# secondary modal, each rendering an `id="resource-form"`). When the
|
|
26
|
+
# server later replies with `turbo_stream.replace("resource-form", ...)`,
|
|
27
|
+
# Turbo would pick the FIRST element matching the id — which is rarely
|
|
28
|
+
# the one the user actually submitted. Append a frame suffix so each
|
|
29
|
+
# frame's form has a unique id and the controller can target precisely.
|
|
30
|
+
#
|
|
31
|
+
# @param base [String, Symbol] the base id
|
|
32
|
+
# @return [String] the scoped id (no suffix outside any modal frame)
|
|
33
|
+
def turbo_scoped_dom_id(base)
|
|
34
|
+
base = base.to_s
|
|
35
|
+
case current_turbo_frame
|
|
36
|
+
when Plutonium::REMOTE_MODAL_FRAME then "#{base}-primary"
|
|
37
|
+
when Plutonium::REMOTE_MODAL_SECONDARY_FRAME then "#{base}-secondary"
|
|
38
|
+
else base
|
|
39
|
+
end
|
|
40
|
+
end
|
|
22
41
|
end
|
|
23
42
|
end
|
|
24
43
|
end
|
|
@@ -53,7 +53,7 @@ module Plutonium
|
|
|
53
53
|
|
|
54
54
|
respond_to do |format|
|
|
55
55
|
if params[:pre_submit]
|
|
56
|
-
format.turbo_stream { render turbo_stream: turbo_stream.replace("resource-form", view_context.render(build_form(action: :new))) }
|
|
56
|
+
format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.turbo_scoped_dom_id("resource-form"), view_context.render(build_form(action: :new))) }
|
|
57
57
|
format.html { render :new, status: :unprocessable_content }
|
|
58
58
|
elsif resource_record!.save
|
|
59
59
|
format.turbo_stream do
|
|
@@ -71,7 +71,7 @@ module Plutonium
|
|
|
71
71
|
location: redirect_url_after_submit
|
|
72
72
|
end
|
|
73
73
|
else
|
|
74
|
-
format.turbo_stream { render turbo_stream: turbo_stream.replace("resource-form", view_context.render(build_form(action: :new))), status: :unprocessable_content }
|
|
74
|
+
format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.turbo_scoped_dom_id("resource-form"), view_context.render(build_form(action: :new))), status: :unprocessable_content }
|
|
75
75
|
format.html { render :new, status: :unprocessable_content }
|
|
76
76
|
format.any do
|
|
77
77
|
@errors = resource_record!.errors
|
|
@@ -100,7 +100,7 @@ module Plutonium
|
|
|
100
100
|
|
|
101
101
|
respond_to do |format|
|
|
102
102
|
if params[:pre_submit]
|
|
103
|
-
format.turbo_stream { render turbo_stream: turbo_stream.replace("resource-form", view_context.render(build_form(action: :edit))) }
|
|
103
|
+
format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.turbo_scoped_dom_id("resource-form"), view_context.render(build_form(action: :edit))) }
|
|
104
104
|
format.html { render :edit, status: :unprocessable_content }
|
|
105
105
|
elsif resource_record!.save
|
|
106
106
|
format.turbo_stream do
|
|
@@ -116,7 +116,7 @@ module Plutonium
|
|
|
116
116
|
render :show, status: :ok, location: redirect_url_after_submit
|
|
117
117
|
end
|
|
118
118
|
else
|
|
119
|
-
format.turbo_stream { render turbo_stream: turbo_stream.replace("resource-form", view_context.render(build_form(action: :edit))), status: :unprocessable_content }
|
|
119
|
+
format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.turbo_scoped_dom_id("resource-form"), view_context.render(build_form(action: :edit))), status: :unprocessable_content }
|
|
120
120
|
format.html { render :edit, status: :unprocessable_content }
|
|
121
121
|
format.any do
|
|
122
122
|
@errors = resource_record!.errors
|
|
@@ -38,7 +38,7 @@ module Plutonium
|
|
|
38
38
|
|
|
39
39
|
if params[:pre_submit]
|
|
40
40
|
respond_to do |format|
|
|
41
|
-
format.turbo_stream { render turbo_stream: turbo_stream.replace("interaction-form", view_context.render(@interaction.build_form)) }
|
|
41
|
+
format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.turbo_scoped_dom_id("interaction-form"), view_context.render(@interaction.build_form)) }
|
|
42
42
|
format.html { render :interactive_record_action, formats: [:html], status: :unprocessable_content }
|
|
43
43
|
end
|
|
44
44
|
return
|
|
@@ -87,7 +87,7 @@ module Plutonium
|
|
|
87
87
|
|
|
88
88
|
if params[:pre_submit]
|
|
89
89
|
respond_to do |format|
|
|
90
|
-
format.turbo_stream { render turbo_stream: turbo_stream.replace("interaction-form", view_context.render(@interaction.build_form)) }
|
|
90
|
+
format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.turbo_scoped_dom_id("interaction-form"), view_context.render(@interaction.build_form)) }
|
|
91
91
|
format.html { render :interactive_resource_action, status: :unprocessable_content }
|
|
92
92
|
end
|
|
93
93
|
return
|
|
@@ -134,7 +134,7 @@ module Plutonium
|
|
|
134
134
|
|
|
135
135
|
if params[:pre_submit]
|
|
136
136
|
respond_to do |format|
|
|
137
|
-
format.turbo_stream { render turbo_stream: turbo_stream.replace("interaction-form", view_context.render(@interaction.build_form)) }
|
|
137
|
+
format.turbo_stream { render turbo_stream: turbo_stream.replace(helpers.turbo_scoped_dom_id("interaction-form"), view_context.render(@interaction.build_form)) }
|
|
138
138
|
format.html { render :interactive_bulk_action, formats: [:html], status: :unprocessable_content }
|
|
139
139
|
end
|
|
140
140
|
return
|
|
@@ -149,9 +149,25 @@ module Plutonium
|
|
|
149
149
|
def initialize_attributes
|
|
150
150
|
super
|
|
151
151
|
|
|
152
|
-
attributes[:id] ||=
|
|
152
|
+
attributes[:id] ||= "resource-form"
|
|
153
153
|
attributes["data-controller"] = "form"
|
|
154
154
|
end
|
|
155
|
+
|
|
156
|
+
# Scope the form id to the current turbo frame at render time (we
|
|
157
|
+
# can't do this in `initialize_attributes` — Phlex hasn't started
|
|
158
|
+
# rendering yet, so `view_context` and the request headers aren't
|
|
159
|
+
# accessible). Primary and secondary modals can each host a form
|
|
160
|
+
# without colliding on document-level turbo-stream `replace target=`
|
|
161
|
+
# lookups. See Helpers::TurboHelper#turbo_scoped_dom_id.
|
|
162
|
+
#
|
|
163
|
+
# Also force-replace the id (Phlexi's `mix` would otherwise prepend
|
|
164
|
+
# `@namespace.dom_id`, producing space-separated ids like
|
|
165
|
+
# "q filter-form" which break document.getElementById lookups).
|
|
166
|
+
def form_attributes
|
|
167
|
+
attrs = super
|
|
168
|
+
attrs[:id] = turbo_scoped_dom_id(attributes[:id]) if attributes[:id]
|
|
169
|
+
attrs
|
|
170
|
+
end
|
|
155
171
|
end
|
|
156
172
|
end
|
|
157
173
|
end
|
|
@@ -24,15 +24,20 @@ module Plutonium
|
|
|
24
24
|
def render_add_button
|
|
25
25
|
return if @add_action == false
|
|
26
26
|
|
|
27
|
+
# Two stacking levels are supported (primary + secondary modal).
|
|
28
|
+
# Hide the "+" entirely once we're already inside the secondary —
|
|
29
|
+
# there's no tertiary frame to escalate to.
|
|
30
|
+
return if in_secondary_modal?
|
|
31
|
+
|
|
27
32
|
url, turbo_frame = add_url_and_frame
|
|
28
33
|
return unless url
|
|
29
34
|
|
|
30
|
-
# When the parent form is already inside
|
|
31
|
-
# "+" to the secondary frame so the stacked dialog
|
|
32
|
-
# top of the original form rather than replacing it.
|
|
33
|
-
# crud controller mirrors this on success — closing the
|
|
34
|
-
# secondary modal and reloading the primary so the
|
|
35
|
-
#
|
|
35
|
+
# When the parent form is already inside the primary modal,
|
|
36
|
+
# route the "+" to the secondary frame so the stacked dialog
|
|
37
|
+
# opens on top of the original form rather than replacing it.
|
|
38
|
+
# The crud controller mirrors this on success — closing the
|
|
39
|
+
# secondary modal and reloading the primary so the association
|
|
40
|
+
# select picks up the new record.
|
|
36
41
|
if turbo_frame == Plutonium::REMOTE_MODAL_FRAME && in_modal?
|
|
37
42
|
turbo_frame = Plutonium::REMOTE_MODAL_SECONDARY_FRAME
|
|
38
43
|
end
|
|
@@ -14,7 +14,7 @@ module Plutonium
|
|
|
14
14
|
inner_wrapper: "w-full",
|
|
15
15
|
|
|
16
16
|
# Form errors
|
|
17
|
-
form_errors_wrapper: "flex items-start gap-3
|
|
17
|
+
form_errors_wrapper: "flex items-start gap-3 m-4 p-4 text-base text-danger-800 rounded-[var(--pu-radius-lg)] bg-danger-50 border border-danger-200 dark:bg-danger-950/30 dark:border-danger-800 dark:text-danger-300",
|
|
18
18
|
form_errors_message: "font-semibold",
|
|
19
19
|
form_errors_list: "mt-2 list-disc list-inside text-sm",
|
|
20
20
|
|
|
@@ -14,10 +14,7 @@ module Plutonium
|
|
|
14
14
|
def initialize(*, query_object:, search_url:, search_param: :q, search_value: nil, attributes: {}, **opts, &)
|
|
15
15
|
opts[:as] = :q
|
|
16
16
|
opts[:method] = :get
|
|
17
|
-
attributes = attributes.deep_merge(
|
|
18
|
-
id: "filter-form",
|
|
19
|
-
data: {turbo_frame: nil}
|
|
20
|
-
)
|
|
17
|
+
attributes = attributes.deep_merge(data: {turbo_frame: nil})
|
|
21
18
|
super(*, attributes:, **opts, &)
|
|
22
19
|
@query_object = query_object
|
|
23
20
|
@search_url = search_url
|
|
@@ -25,6 +22,17 @@ module Plutonium
|
|
|
25
22
|
@search_value = search_value
|
|
26
23
|
end
|
|
27
24
|
|
|
25
|
+
# The Filters slideover renders on every index page (off-screen
|
|
26
|
+
# until opened) — same DOM as any CRUD modal that might appear.
|
|
27
|
+
# Base defaults the form id to "resource-form"; without this
|
|
28
|
+
# override, document-level `turbo_stream.replace("resource-form", …)`
|
|
29
|
+
# from a CRUD submit would clobber the wrong form. Pick a distinct
|
|
30
|
+
# id so the two never collide.
|
|
31
|
+
def initialize_attributes
|
|
32
|
+
super
|
|
33
|
+
attributes[:id] = "filter-form"
|
|
34
|
+
end
|
|
35
|
+
|
|
28
36
|
def form_class
|
|
29
37
|
"flex-1 flex flex-col min-h-0"
|
|
30
38
|
end
|
data/lib/plutonium/version.rb
CHANGED
data/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radioactive-labs/plutonium",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.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",
|
|
@@ -30,15 +30,18 @@
|
|
|
30
30
|
"marked": "^15.0.3"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
+
"@tabler/icons-vue": "^3.44.0",
|
|
33
34
|
"@tailwindcss/forms": "^0.5.10",
|
|
34
35
|
"@tailwindcss/postcss": "^4.3.0",
|
|
35
36
|
"@tailwindcss/typography": "^0.5.16",
|
|
37
|
+
"asciinema-player": "^3.15.1",
|
|
36
38
|
"chokidar-cli": "^3.0.0",
|
|
37
39
|
"concurrently": "^8.2.2",
|
|
38
40
|
"cssnano": "^7.0.2",
|
|
39
41
|
"esbuild": "^0.28.0",
|
|
40
42
|
"esbuild-plugin-manifest": "^1.0.3",
|
|
41
43
|
"flowbite-typography": "^1.0.5",
|
|
44
|
+
"medium-zoom": "^1.1.0",
|
|
42
45
|
"mermaid": "^11.15.0",
|
|
43
46
|
"postcss": "^8.5.14",
|
|
44
47
|
"postcss-cli": "^11.0.1",
|
|
@@ -6,19 +6,20 @@ export default class extends Controller {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
preSubmit() {
|
|
9
|
-
//
|
|
9
|
+
// Some widgets (e.g. slim-select) dispatch their own change event on top
|
|
10
|
+
// of the native one, so this can fire twice per user action. Remove any
|
|
11
|
+
// prior hidden field before appending a fresh one to avoid duplicates.
|
|
12
|
+
this.element.querySelectorAll('input[name="pre_submit"]').forEach(n => n.remove());
|
|
13
|
+
|
|
10
14
|
const hiddenField = document.createElement('input');
|
|
11
15
|
hiddenField.type = 'hidden';
|
|
12
16
|
hiddenField.name = 'pre_submit';
|
|
13
17
|
hiddenField.value = 'true';
|
|
14
|
-
|
|
15
|
-
// Append it to the form
|
|
16
18
|
this.element.appendChild(hiddenField);
|
|
17
19
|
|
|
18
20
|
// Skip validation by setting novalidate attribute
|
|
19
21
|
this.element.setAttribute('novalidate', '');
|
|
20
22
|
|
|
21
|
-
// Submit the form
|
|
22
23
|
this.submit();
|
|
23
24
|
}
|
|
24
25
|
|
data/yarn.lock
CHANGED
|
@@ -876,6 +876,7 @@ __metadata:
|
|
|
876
876
|
"@hotwired/stimulus": "npm:^3.2.2"
|
|
877
877
|
"@hotwired/turbo": "npm:^8.0.4"
|
|
878
878
|
"@popperjs/core": "npm:^2.11.8"
|
|
879
|
+
"@tabler/icons-vue": "npm:^3.44.0"
|
|
879
880
|
"@tailwindcss/forms": "npm:^0.5.10"
|
|
880
881
|
"@tailwindcss/postcss": "npm:^4.3.0"
|
|
881
882
|
"@tailwindcss/typography": "npm:^0.5.16"
|
|
@@ -883,6 +884,7 @@ __metadata:
|
|
|
883
884
|
"@uppy/dashboard": "npm:^4.1.3"
|
|
884
885
|
"@uppy/image-editor": "npm:^3.2.1"
|
|
885
886
|
"@uppy/xhr-upload": "npm:^4.2.3"
|
|
887
|
+
asciinema-player: "npm:^3.15.1"
|
|
886
888
|
chokidar-cli: "npm:^3.0.0"
|
|
887
889
|
concurrently: "npm:^8.2.2"
|
|
888
890
|
cssnano: "npm:^7.0.2"
|
|
@@ -892,6 +894,7 @@ __metadata:
|
|
|
892
894
|
flowbite-typography: "npm:^1.0.5"
|
|
893
895
|
lodash.debounce: "npm:^4.0.8"
|
|
894
896
|
marked: "npm:^15.0.3"
|
|
897
|
+
medium-zoom: "npm:^1.1.0"
|
|
895
898
|
mermaid: "npm:^11.15.0"
|
|
896
899
|
postcss: "npm:^8.5.14"
|
|
897
900
|
postcss-cli: "npm:^11.0.1"
|
|
@@ -1158,6 +1161,53 @@ __metadata:
|
|
|
1158
1161
|
languageName: node
|
|
1159
1162
|
linkType: hard
|
|
1160
1163
|
|
|
1164
|
+
"@solid-primitives/refs@npm:^1.0.5":
|
|
1165
|
+
version: 1.1.3
|
|
1166
|
+
resolution: "@solid-primitives/refs@npm:1.1.3"
|
|
1167
|
+
dependencies:
|
|
1168
|
+
"@solid-primitives/utils": "npm:^6.4.0"
|
|
1169
|
+
peerDependencies:
|
|
1170
|
+
solid-js: ^1.6.12
|
|
1171
|
+
checksum: 10c0/af1e27b5b38f639e5a3125a982ff8f9c58fe2aea4609718c9383d88d1fc5a13e490fb40c262eb183c695b3efacd89b5328876a615814c0edb54981b58b3804fa
|
|
1172
|
+
languageName: node
|
|
1173
|
+
linkType: hard
|
|
1174
|
+
|
|
1175
|
+
"@solid-primitives/transition-group@npm:^1.0.2":
|
|
1176
|
+
version: 1.1.2
|
|
1177
|
+
resolution: "@solid-primitives/transition-group@npm:1.1.2"
|
|
1178
|
+
peerDependencies:
|
|
1179
|
+
solid-js: ^1.6.12
|
|
1180
|
+
checksum: 10c0/f676cefc38bab6aad7c1214ef2ea145d598ec2e873eca663a79a3a8e46b9ee8f6ea57f3e11c0acf5156ffa63ba727e9676c46c6fd0cf9130e034da79ead12402
|
|
1181
|
+
languageName: node
|
|
1182
|
+
linkType: hard
|
|
1183
|
+
|
|
1184
|
+
"@solid-primitives/utils@npm:^6.4.0":
|
|
1185
|
+
version: 6.4.0
|
|
1186
|
+
resolution: "@solid-primitives/utils@npm:6.4.0"
|
|
1187
|
+
peerDependencies:
|
|
1188
|
+
solid-js: ^1.6.12
|
|
1189
|
+
checksum: 10c0/fdac336c74be180251ac40df280571534d427c773b207e19a51aa01f013e16864f15c5c829f53a8e7d0033543bef07a6410c2dbaf364410dc29783966e14fcac
|
|
1190
|
+
languageName: node
|
|
1191
|
+
linkType: hard
|
|
1192
|
+
|
|
1193
|
+
"@tabler/icons-vue@npm:^3.44.0":
|
|
1194
|
+
version: 3.44.0
|
|
1195
|
+
resolution: "@tabler/icons-vue@npm:3.44.0"
|
|
1196
|
+
dependencies:
|
|
1197
|
+
"@tabler/icons": "npm:3.44.0"
|
|
1198
|
+
peerDependencies:
|
|
1199
|
+
vue: ">=3.0.1"
|
|
1200
|
+
checksum: 10c0/7730f3cd00056584ad322ab9d7b740d0f62632e5480a89dfd458f916af278010a940bfb62bcd1067b32177bf8ff5c101534aa898666501e47dc0ddaf3f0f9c9f
|
|
1201
|
+
languageName: node
|
|
1202
|
+
linkType: hard
|
|
1203
|
+
|
|
1204
|
+
"@tabler/icons@npm:3.44.0":
|
|
1205
|
+
version: 3.44.0
|
|
1206
|
+
resolution: "@tabler/icons@npm:3.44.0"
|
|
1207
|
+
checksum: 10c0/0d5c1f9d6e68aa04c4a661e96035190e0884e0bc0022d31922efad3cfba210a25b2f013c0b6c0ab6aeb3d5a402a9b85a168966bd11a30ba6087f614f8521ccf3
|
|
1208
|
+
languageName: node
|
|
1209
|
+
linkType: hard
|
|
1210
|
+
|
|
1161
1211
|
"@tailwindcss/forms@npm:^0.5.10":
|
|
1162
1212
|
version: 0.5.10
|
|
1163
1213
|
resolution: "@tailwindcss/forms@npm:0.5.10"
|
|
@@ -2184,6 +2234,17 @@ __metadata:
|
|
|
2184
2234
|
languageName: node
|
|
2185
2235
|
linkType: hard
|
|
2186
2236
|
|
|
2237
|
+
"asciinema-player@npm:^3.15.1":
|
|
2238
|
+
version: 3.15.1
|
|
2239
|
+
resolution: "asciinema-player@npm:3.15.1"
|
|
2240
|
+
dependencies:
|
|
2241
|
+
"@babel/runtime": "npm:^7.21.0"
|
|
2242
|
+
solid-js: "npm:^1.3.0"
|
|
2243
|
+
solid-transition-group: "npm:^0.2.3"
|
|
2244
|
+
checksum: 10c0/36638e9804a94866d6c6ae25cdbf867313873e68838b2630eed73229819170bc67667098865fe38b061012c3fa78e8235322725c5e1bda258ec82787d470f8a4
|
|
2245
|
+
languageName: node
|
|
2246
|
+
linkType: hard
|
|
2247
|
+
|
|
2187
2248
|
"balanced-match@npm:^4.0.2":
|
|
2188
2249
|
version: 4.0.4
|
|
2189
2250
|
resolution: "balanced-match@npm:4.0.4"
|
|
@@ -2647,7 +2708,7 @@ __metadata:
|
|
|
2647
2708
|
languageName: node
|
|
2648
2709
|
linkType: hard
|
|
2649
2710
|
|
|
2650
|
-
"csstype@npm:^3.2.3":
|
|
2711
|
+
"csstype@npm:^3.1.0, csstype@npm:^3.2.3":
|
|
2651
2712
|
version: 3.2.3
|
|
2652
2713
|
resolution: "csstype@npm:3.2.3"
|
|
2653
2714
|
checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce
|
|
@@ -4153,6 +4214,13 @@ __metadata:
|
|
|
4153
4214
|
languageName: node
|
|
4154
4215
|
linkType: hard
|
|
4155
4216
|
|
|
4217
|
+
"medium-zoom@npm:^1.1.0":
|
|
4218
|
+
version: 1.1.0
|
|
4219
|
+
resolution: "medium-zoom@npm:1.1.0"
|
|
4220
|
+
checksum: 10c0/7d1f05e8eab045c33d7c04d4ee7bf04f5246cf7a720d7b5f5a51c36ab23666e363bcbb6bffae50b5948d5eb19361914cb0e26a1fce5c1fff7a266bc0217893f3
|
|
4221
|
+
languageName: node
|
|
4222
|
+
linkType: hard
|
|
4223
|
+
|
|
4156
4224
|
"mermaid@npm:^11.15.0":
|
|
4157
4225
|
version: 11.15.0
|
|
4158
4226
|
resolution: "mermaid@npm:11.15.0"
|
|
@@ -5355,6 +5423,22 @@ __metadata:
|
|
|
5355
5423
|
languageName: node
|
|
5356
5424
|
linkType: hard
|
|
5357
5425
|
|
|
5426
|
+
"seroval-plugins@npm:~1.5.0":
|
|
5427
|
+
version: 1.5.4
|
|
5428
|
+
resolution: "seroval-plugins@npm:1.5.4"
|
|
5429
|
+
peerDependencies:
|
|
5430
|
+
seroval: ^1.0
|
|
5431
|
+
checksum: 10c0/f8843ff12c2fcbf0d1124d02addcffc1a727f55b500ac24218a528e95e540c42052e2e6f6b3dfaad2aa8105fd0985dff722c3ae774723b9899e0fafe7d4698be
|
|
5432
|
+
languageName: node
|
|
5433
|
+
linkType: hard
|
|
5434
|
+
|
|
5435
|
+
"seroval@npm:~1.5.0":
|
|
5436
|
+
version: 1.5.4
|
|
5437
|
+
resolution: "seroval@npm:1.5.4"
|
|
5438
|
+
checksum: 10c0/6191e27f21000f7693ab923fde69c47a3ce5fbb86e585e5a8fc072d70db52ebc3c4dab83c3b2ab67311ec646b2064df089a3a155c49b21846438aaf510d4b964
|
|
5439
|
+
languageName: node
|
|
5440
|
+
linkType: hard
|
|
5441
|
+
|
|
5358
5442
|
"set-blocking@npm:^2.0.0":
|
|
5359
5443
|
version: 2.0.0
|
|
5360
5444
|
resolution: "set-blocking@npm:2.0.0"
|
|
@@ -5434,6 +5518,29 @@ __metadata:
|
|
|
5434
5518
|
languageName: node
|
|
5435
5519
|
linkType: hard
|
|
5436
5520
|
|
|
5521
|
+
"solid-js@npm:^1.3.0":
|
|
5522
|
+
version: 1.9.13
|
|
5523
|
+
resolution: "solid-js@npm:1.9.13"
|
|
5524
|
+
dependencies:
|
|
5525
|
+
csstype: "npm:^3.1.0"
|
|
5526
|
+
seroval: "npm:~1.5.0"
|
|
5527
|
+
seroval-plugins: "npm:~1.5.0"
|
|
5528
|
+
checksum: 10c0/1c407da820435771ec6fd65e605fd804fc1faf74ee84af2d3dce2bc5c223563017a9e15746eb86d27237e6d0d6ac8660685c560eb1f1decdc6f3c7b913927928
|
|
5529
|
+
languageName: node
|
|
5530
|
+
linkType: hard
|
|
5531
|
+
|
|
5532
|
+
"solid-transition-group@npm:^0.2.3":
|
|
5533
|
+
version: 0.2.3
|
|
5534
|
+
resolution: "solid-transition-group@npm:0.2.3"
|
|
5535
|
+
dependencies:
|
|
5536
|
+
"@solid-primitives/refs": "npm:^1.0.5"
|
|
5537
|
+
"@solid-primitives/transition-group": "npm:^1.0.2"
|
|
5538
|
+
peerDependencies:
|
|
5539
|
+
solid-js: ^1.6.12
|
|
5540
|
+
checksum: 10c0/584656bedefb03fd91801d858c9abf0de5afc175e9a7bea2023000faa5ed3af4c6e4b8b99dd7ed1069595d362b002d4ae8ca08d030e422b65dc23742fb2ac681
|
|
5541
|
+
languageName: node
|
|
5542
|
+
linkType: hard
|
|
5543
|
+
|
|
5437
5544
|
"source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.1":
|
|
5438
5545
|
version: 1.2.1
|
|
5439
5546
|
resolution: "source-map-js@npm:1.2.1"
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: plutonium
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.52.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Froelich
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-05-
|
|
10
|
+
date: 2026-05-21 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: zeitwerk
|
|
@@ -532,6 +532,14 @@ files:
|
|
|
532
532
|
- config/initializers/rabl.rb
|
|
533
533
|
- config/initializers/sqlite_alias.rb
|
|
534
534
|
- docs/.vitepress/config.ts
|
|
535
|
+
- docs/.vitepress/theme/components/HomeAudienceSplit.vue
|
|
536
|
+
- docs/.vitepress/theme/components/HomeCta.vue
|
|
537
|
+
- docs/.vitepress/theme/components/HomeHero.vue
|
|
538
|
+
- docs/.vitepress/theme/components/HomeInTheBox.vue
|
|
539
|
+
- docs/.vitepress/theme/components/HomePillars.vue
|
|
540
|
+
- docs/.vitepress/theme/components/HomeStopWriting.vue
|
|
541
|
+
- docs/.vitepress/theme/components/HomeWalkthrough.vue
|
|
542
|
+
- docs/.vitepress/theme/components/SectionLanding.vue
|
|
535
543
|
- docs/.vitepress/theme/custom.css
|
|
536
544
|
- docs/.vitepress/theme/index.ts
|
|
537
545
|
- docs/getting-started/index.md
|
|
@@ -550,6 +558,7 @@ files:
|
|
|
550
558
|
- docs/guides/authorization.md
|
|
551
559
|
- docs/guides/creating-packages.md
|
|
552
560
|
- docs/guides/custom-actions.md
|
|
561
|
+
- docs/guides/customizing-ui.md
|
|
553
562
|
- docs/guides/index.md
|
|
554
563
|
- docs/guides/multi-tenancy.md
|
|
555
564
|
- docs/guides/nested-resources.md
|
|
@@ -564,9 +573,39 @@ files:
|
|
|
564
573
|
- docs/public/android-chrome-192x192.png
|
|
565
574
|
- docs/public/android-chrome-512x512.png
|
|
566
575
|
- docs/public/apple-touch-icon.png
|
|
576
|
+
- docs/public/asciinema/home-scaffold.cast
|
|
567
577
|
- docs/public/favicon-16x16.png
|
|
568
578
|
- docs/public/favicon-32x32.png
|
|
569
579
|
- docs/public/favicon.ico
|
|
580
|
+
- docs/public/images/guides/custom-actions-bulk.png
|
|
581
|
+
- docs/public/images/guides/multi-tenancy-dashboard.png
|
|
582
|
+
- docs/public/images/guides/multi-tenancy-welcome.png
|
|
583
|
+
- docs/public/images/guides/nested-inputs.png
|
|
584
|
+
- docs/public/images/guides/nested-resources-tab.png
|
|
585
|
+
- docs/public/images/guides/search-filtering-index.png
|
|
586
|
+
- docs/public/images/guides/search-filtering-panel.png
|
|
587
|
+
- docs/public/images/guides/theming-after.png
|
|
588
|
+
- docs/public/images/guides/theming-before.png
|
|
589
|
+
- docs/public/images/guides/user-invites-landing.png
|
|
590
|
+
- docs/public/images/guides/user-profile-edit.png
|
|
591
|
+
- docs/public/images/guides/user-profile-show.png
|
|
592
|
+
- docs/public/images/home-index.png
|
|
593
|
+
- docs/public/images/home-new.png
|
|
594
|
+
- docs/public/images/home-show.png
|
|
595
|
+
- docs/public/images/tutorial/02-empty-index.png
|
|
596
|
+
- docs/public/images/tutorial/02-index-with-posts.png
|
|
597
|
+
- docs/public/images/tutorial/02-new-form-modal.png
|
|
598
|
+
- docs/public/images/tutorial/02-new-form.png
|
|
599
|
+
- docs/public/images/tutorial/03-create-account.png
|
|
600
|
+
- docs/public/images/tutorial/03-login.png
|
|
601
|
+
- docs/public/images/tutorial/04-admin-index.png
|
|
602
|
+
- docs/public/images/tutorial/05-actions-menu.png
|
|
603
|
+
- docs/public/images/tutorial/05-row-actions.png
|
|
604
|
+
- docs/public/images/tutorial/06-comments-tab.png
|
|
605
|
+
- docs/public/images/tutorial/06-post-with-comments.png
|
|
606
|
+
- docs/public/images/tutorial/07-author-dashboard.png
|
|
607
|
+
- docs/public/images/tutorial/07-author-portal.png
|
|
608
|
+
- docs/public/images/tutorial/08-customized-index.png
|
|
570
609
|
- docs/public/og-image.png
|
|
571
610
|
- docs/public/plutonium.png
|
|
572
611
|
- docs/public/site.webmanifest
|
|
@@ -620,12 +659,15 @@ files:
|
|
|
620
659
|
- docs/superpowers/plans/2026-05-06-multi-invite-model-support.md.tasks.json
|
|
621
660
|
- docs/superpowers/plans/2026-05-07-ui-layout-overhaul.md
|
|
622
661
|
- docs/superpowers/plans/2026-05-07-ui-layout-overhaul.md.tasks.json
|
|
662
|
+
- docs/superpowers/plans/2026-05-15-public-pages-overhaul.md
|
|
663
|
+
- docs/superpowers/plans/2026-05-15-public-pages-overhaul.md.tasks.json
|
|
623
664
|
- docs/superpowers/specs/2026-04-08-plutonium-skills-overhaul-design.md
|
|
624
665
|
- docs/superpowers/specs/2026-04-14-plutonium-testing-design.md
|
|
625
666
|
- docs/superpowers/specs/2026-05-07-ui-layout-overhaul-design.md
|
|
626
667
|
- docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md
|
|
627
668
|
- docs/superpowers/specs/2026-05-12-skill-compaction-design.md
|
|
628
669
|
- docs/superpowers/specs/2026-05-13-docs-restructure-design.md
|
|
670
|
+
- docs/superpowers/specs/2026-05-15-public-pages-overhaul-design.md
|
|
629
671
|
- esbuild.config.js
|
|
630
672
|
- exe/pug
|
|
631
673
|
- gemfiles/rails_7.gemfile
|
|
@@ -1183,7 +1225,7 @@ metadata:
|
|
|
1183
1225
|
homepage_uri: https://radioactive-labs.github.io/plutonium-core/
|
|
1184
1226
|
source_code_uri: https://github.com/radioactive-labs/plutonium-core
|
|
1185
1227
|
post_install_message: |
|
|
1186
|
-
⚠️ Plutonium 0.
|
|
1228
|
+
⚠️ Plutonium 0.52.0 — breaking change
|
|
1187
1229
|
|
|
1188
1230
|
Entity-scoped URL helpers and path params have been renamed from
|
|
1189
1231
|
`<entity>_scope_*` to `<entity>_scoped_*`.
|