plutonium 0.48.0 → 0.49.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/CHANGELOG.md +23 -0
- data/app/assets/plutonium.js +38 -25
- data/app/assets/plutonium.js.map +2 -2
- data/app/assets/plutonium.min.js +29 -29
- data/app/assets/plutonium.min.js.map +3 -3
- data/config/initializers/pagy.rb +1 -1
- data/docs/public/templates/plutonium.rb +3 -0
- data/gemfiles/rails_7.gemfile.lock +27 -1
- data/gemfiles/rails_8.0.gemfile.lock +27 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/gem/actual_db_schema/actual_db_schema_generator.rb +24 -0
- data/lib/generators/pu/lib/plutonium_generators/concerns/configures_sqlite.rb +9 -3
- data/lib/generators/pu/lite/rails_pulse/rails_pulse_generator.rb +6 -3
- data/lib/generators/pu/lite/rails_pulse/templates/config/initializers/rails_pulse.rb.tt +18 -0
- data/lib/plutonium/core/controller.rb +10 -3
- data/lib/plutonium/engine.rb +1 -1
- data/lib/plutonium/rodauth/controller_methods.rb +5 -1
- data/lib/plutonium/ui/color_mode_selector.rb +7 -18
- data/lib/plutonium/ui/layout/rodauth_layout.rb +6 -0
- data/lib/plutonium/ui/table/components/pagy_info.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/plutonium.gemspec +16 -0
- data/src/js/controllers/color_mode_controller.js +41 -34
- metadata +17 -2
data/config/initializers/pagy.rb
CHANGED
|
@@ -23,6 +23,9 @@ after_bundle do
|
|
|
23
23
|
generate "pu:gem:letter_opener"
|
|
24
24
|
git(add: ".") && git(commit: %( -m 'add letter_opener' ))
|
|
25
25
|
|
|
26
|
+
generate "pu:gem:actual_db_schema"
|
|
27
|
+
git(add: ".") && git(commit: %( -m 'add actual_db_schema' ))
|
|
28
|
+
|
|
26
29
|
generate "pu:core:assets"
|
|
27
30
|
git(add: ".") && git(commit: %( -m 'integrate assets' ))
|
|
28
31
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
plutonium (0.
|
|
4
|
+
plutonium (0.48.0)
|
|
5
5
|
action_policy (~> 0.7.0)
|
|
6
6
|
listen (~> 3.8)
|
|
7
7
|
pagy (~> 43.0)
|
|
@@ -100,6 +100,8 @@ GEM
|
|
|
100
100
|
minitest (>= 5.1)
|
|
101
101
|
securerandom (>= 0.3)
|
|
102
102
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
103
|
+
addressable (2.9.0)
|
|
104
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
103
105
|
ansi (1.5.0)
|
|
104
106
|
appraisal (2.5.0)
|
|
105
107
|
bundler
|
|
@@ -118,6 +120,15 @@ GEM
|
|
|
118
120
|
bundler-audit (0.9.3)
|
|
119
121
|
bundler (>= 1.2.0)
|
|
120
122
|
thor (~> 1.0)
|
|
123
|
+
capybara (3.40.0)
|
|
124
|
+
addressable
|
|
125
|
+
matrix
|
|
126
|
+
mini_mime (>= 0.1.3)
|
|
127
|
+
nokogiri (~> 1.11)
|
|
128
|
+
rack (>= 1.6.0)
|
|
129
|
+
rack-test (>= 0.6.3)
|
|
130
|
+
regexp_parser (>= 1.5, < 3.0)
|
|
131
|
+
xpath (~> 3.2)
|
|
121
132
|
cgi (0.5.1)
|
|
122
133
|
chunky_png (1.4.0)
|
|
123
134
|
combustion (1.5.0)
|
|
@@ -174,6 +185,7 @@ GEM
|
|
|
174
185
|
net-pop
|
|
175
186
|
net-smtp
|
|
176
187
|
marcel (1.1.0)
|
|
188
|
+
matrix (0.4.3)
|
|
177
189
|
mini_mime (1.1.5)
|
|
178
190
|
minitest (6.0.2)
|
|
179
191
|
drb (~> 2.0)
|
|
@@ -265,6 +277,7 @@ GEM
|
|
|
265
277
|
psych (5.3.1)
|
|
266
278
|
date
|
|
267
279
|
stringio
|
|
280
|
+
public_suffix (7.0.5)
|
|
268
281
|
puma (7.2.0)
|
|
269
282
|
nio4r (~> 2.0)
|
|
270
283
|
rabl (0.17.0)
|
|
@@ -325,6 +338,7 @@ GEM
|
|
|
325
338
|
regexp_parser (2.11.3)
|
|
326
339
|
reline (0.6.3)
|
|
327
340
|
io-console (~> 0.5)
|
|
341
|
+
rexml (3.4.4)
|
|
328
342
|
roda (3.102.0)
|
|
329
343
|
rack
|
|
330
344
|
rodauth (2.42.0)
|
|
@@ -362,7 +376,14 @@ GEM
|
|
|
362
376
|
rubocop-ast (>= 1.47.1, < 2.0)
|
|
363
377
|
ruby-next-core (1.2.0)
|
|
364
378
|
ruby-progressbar (1.13.0)
|
|
379
|
+
rubyzip (3.2.2)
|
|
365
380
|
securerandom (0.4.1)
|
|
381
|
+
selenium-webdriver (4.43.0)
|
|
382
|
+
base64 (~> 0.2)
|
|
383
|
+
logger (~> 1.4)
|
|
384
|
+
rexml (~> 3.2, >= 3.2.5)
|
|
385
|
+
rubyzip (>= 1.2.2, < 4.0)
|
|
386
|
+
websocket (~> 1.0)
|
|
366
387
|
semantic_range (3.1.1)
|
|
367
388
|
sequel (5.102.0)
|
|
368
389
|
bigdecimal
|
|
@@ -419,11 +440,14 @@ GEM
|
|
|
419
440
|
unicode-emoji (4.2.0)
|
|
420
441
|
uri (1.1.1)
|
|
421
442
|
useragent (0.16.11)
|
|
443
|
+
websocket (1.2.11)
|
|
422
444
|
websocket-driver (0.8.0)
|
|
423
445
|
base64
|
|
424
446
|
websocket-extensions (>= 0.1.0)
|
|
425
447
|
websocket-extensions (0.1.5)
|
|
426
448
|
wisper (2.0.1)
|
|
449
|
+
xpath (3.2.0)
|
|
450
|
+
nokogiri (~> 1.8)
|
|
427
451
|
yaml (0.4.0)
|
|
428
452
|
zeitwerk (2.7.5)
|
|
429
453
|
|
|
@@ -442,6 +466,7 @@ DEPENDENCIES
|
|
|
442
466
|
bcrypt
|
|
443
467
|
brakeman
|
|
444
468
|
bundle-audit
|
|
469
|
+
capybara
|
|
445
470
|
combustion
|
|
446
471
|
importmap-rails
|
|
447
472
|
minitest
|
|
@@ -454,6 +479,7 @@ DEPENDENCIES
|
|
|
454
479
|
rodauth-rails
|
|
455
480
|
rotp
|
|
456
481
|
rqrcode
|
|
482
|
+
selenium-webdriver
|
|
457
483
|
sequel-activerecord_connection
|
|
458
484
|
sqlite3
|
|
459
485
|
standard
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
plutonium (0.
|
|
4
|
+
plutonium (0.48.0)
|
|
5
5
|
action_policy (~> 0.7.0)
|
|
6
6
|
listen (~> 3.8)
|
|
7
7
|
pagy (~> 43.0)
|
|
@@ -98,6 +98,8 @@ GEM
|
|
|
98
98
|
securerandom (>= 0.3)
|
|
99
99
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
100
100
|
uri (>= 0.13.1)
|
|
101
|
+
addressable (2.9.0)
|
|
102
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
101
103
|
ansi (1.5.0)
|
|
102
104
|
appraisal (2.5.0)
|
|
103
105
|
bundler
|
|
@@ -116,6 +118,15 @@ GEM
|
|
|
116
118
|
bundler-audit (0.9.3)
|
|
117
119
|
bundler (>= 1.2.0)
|
|
118
120
|
thor (~> 1.0)
|
|
121
|
+
capybara (3.40.0)
|
|
122
|
+
addressable
|
|
123
|
+
matrix
|
|
124
|
+
mini_mime (>= 0.1.3)
|
|
125
|
+
nokogiri (~> 1.11)
|
|
126
|
+
rack (>= 1.6.0)
|
|
127
|
+
rack-test (>= 0.6.3)
|
|
128
|
+
regexp_parser (>= 1.5, < 3.0)
|
|
129
|
+
xpath (~> 3.2)
|
|
119
130
|
chunky_png (1.4.0)
|
|
120
131
|
combustion (1.5.0)
|
|
121
132
|
activesupport (>= 3.0.0)
|
|
@@ -164,6 +175,7 @@ GEM
|
|
|
164
175
|
net-pop
|
|
165
176
|
net-smtp
|
|
166
177
|
marcel (1.1.0)
|
|
178
|
+
matrix (0.4.3)
|
|
167
179
|
mini_mime (1.1.5)
|
|
168
180
|
minitest (6.0.2)
|
|
169
181
|
drb (~> 2.0)
|
|
@@ -241,6 +253,7 @@ GEM
|
|
|
241
253
|
psych (5.3.1)
|
|
242
254
|
date
|
|
243
255
|
stringio
|
|
256
|
+
public_suffix (7.0.5)
|
|
244
257
|
puma (7.2.0)
|
|
245
258
|
nio4r (~> 2.0)
|
|
246
259
|
rabl (0.17.0)
|
|
@@ -300,6 +313,7 @@ GEM
|
|
|
300
313
|
regexp_parser (2.11.3)
|
|
301
314
|
reline (0.6.3)
|
|
302
315
|
io-console (~> 0.5)
|
|
316
|
+
rexml (3.4.4)
|
|
303
317
|
roda (3.102.0)
|
|
304
318
|
rack
|
|
305
319
|
rodauth (2.42.0)
|
|
@@ -337,7 +351,14 @@ GEM
|
|
|
337
351
|
rubocop-ast (>= 1.47.1, < 2.0)
|
|
338
352
|
ruby-next-core (1.2.0)
|
|
339
353
|
ruby-progressbar (1.13.0)
|
|
354
|
+
rubyzip (3.2.2)
|
|
340
355
|
securerandom (0.4.1)
|
|
356
|
+
selenium-webdriver (4.43.0)
|
|
357
|
+
base64 (~> 0.2)
|
|
358
|
+
logger (~> 1.4)
|
|
359
|
+
rexml (~> 3.2, >= 3.2.5)
|
|
360
|
+
rubyzip (>= 1.2.2, < 4.0)
|
|
361
|
+
websocket (~> 1.0)
|
|
341
362
|
semantic_range (3.1.1)
|
|
342
363
|
sequel (5.102.0)
|
|
343
364
|
bigdecimal
|
|
@@ -387,11 +408,14 @@ GEM
|
|
|
387
408
|
unicode-emoji (4.2.0)
|
|
388
409
|
uri (1.1.1)
|
|
389
410
|
useragent (0.16.11)
|
|
411
|
+
websocket (1.2.11)
|
|
390
412
|
websocket-driver (0.8.0)
|
|
391
413
|
base64
|
|
392
414
|
websocket-extensions (>= 0.1.0)
|
|
393
415
|
websocket-extensions (0.1.5)
|
|
394
416
|
wisper (2.0.1)
|
|
417
|
+
xpath (3.2.0)
|
|
418
|
+
nokogiri (~> 1.8)
|
|
395
419
|
yaml (0.4.0)
|
|
396
420
|
zeitwerk (2.7.5)
|
|
397
421
|
|
|
@@ -403,6 +427,7 @@ DEPENDENCIES
|
|
|
403
427
|
bcrypt
|
|
404
428
|
brakeman
|
|
405
429
|
bundle-audit
|
|
430
|
+
capybara
|
|
406
431
|
combustion
|
|
407
432
|
importmap-rails
|
|
408
433
|
minitest
|
|
@@ -415,6 +440,7 @@ DEPENDENCIES
|
|
|
415
440
|
rodauth-rails
|
|
416
441
|
rotp
|
|
417
442
|
rqrcode
|
|
443
|
+
selenium-webdriver
|
|
418
444
|
sequel-activerecord_connection
|
|
419
445
|
sqlite3
|
|
420
446
|
standard
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../lib/plutonium_generators"
|
|
4
|
+
|
|
5
|
+
module Pu
|
|
6
|
+
module Gem
|
|
7
|
+
# Installs actual_db_schema, which tracks phantom migrations across git
|
|
8
|
+
# branches so switching branches with diverging migration sets doesn't
|
|
9
|
+
# leave db/schema.rb out of sync.
|
|
10
|
+
#
|
|
11
|
+
# https://github.com/share-group/actual_db_schema
|
|
12
|
+
class ActualDbSchemaGenerator < Rails::Generators::Base
|
|
13
|
+
include PlutoniumGenerators::Generator
|
|
14
|
+
|
|
15
|
+
desc "Install the actual_db_schema gem"
|
|
16
|
+
|
|
17
|
+
def start
|
|
18
|
+
bundle "actual_db_schema", group: %i[development test]
|
|
19
|
+
rescue => e
|
|
20
|
+
exception "#{self.class} failed:", e
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -55,7 +55,7 @@ module PlutoniumGenerators
|
|
|
55
55
|
end.compact!
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def new_database(name, migrations_paths: nil)
|
|
58
|
+
def new_database(name, migrations_paths: nil, schema_dump: nil)
|
|
59
59
|
migrations_paths ||= "db/#{name}_migrate"
|
|
60
60
|
db = Psych::Nodes::Mapping.new(name)
|
|
61
61
|
db.children.concat [
|
|
@@ -66,6 +66,12 @@ module PlutoniumGenerators
|
|
|
66
66
|
Psych::Nodes::Scalar.new("database"),
|
|
67
67
|
Psych::Nodes::Scalar.new("storage/<%= Rails.env %>-#{name}.sqlite3")
|
|
68
68
|
]
|
|
69
|
+
unless schema_dump.nil?
|
|
70
|
+
db.children.concat [
|
|
71
|
+
Psych::Nodes::Scalar.new("schema_dump"),
|
|
72
|
+
Psych::Nodes::Scalar.new(schema_dump.to_s)
|
|
73
|
+
]
|
|
74
|
+
end
|
|
69
75
|
"\n" + emit_pair(Psych::Nodes::Scalar.new(name), db)
|
|
70
76
|
end
|
|
71
77
|
|
|
@@ -91,10 +97,10 @@ module PlutoniumGenerators
|
|
|
91
97
|
@database_yaml ||= DatabaseYAML.new(path: File.expand_path("config/database.yml", destination_root))
|
|
92
98
|
end
|
|
93
99
|
|
|
94
|
-
def add_sqlite_database(name, migrations_paths: nil)
|
|
100
|
+
def add_sqlite_database(name, migrations_paths: nil, schema_dump: nil)
|
|
95
101
|
# Define the new database configuration
|
|
96
102
|
insert_into_file "config/database.yml",
|
|
97
|
-
database_yaml.new_database(name, migrations_paths: migrations_paths) + "\n",
|
|
103
|
+
database_yaml.new_database(name, migrations_paths: migrations_paths, schema_dump: schema_dump) + "\n",
|
|
98
104
|
after: database_yaml.database_def_regex("default"),
|
|
99
105
|
verbose: false,
|
|
100
106
|
force: false
|
|
@@ -51,10 +51,13 @@ module Pu
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# Then add database config
|
|
54
|
-
add_sqlite_database(@db_name, migrations_paths: "db/rails_pulse_migrate")
|
|
54
|
+
add_sqlite_database(@db_name, migrations_paths: "db/rails_pulse_migrate", schema_dump: false)
|
|
55
55
|
|
|
56
|
-
#
|
|
57
|
-
|
|
56
|
+
# rails_pulse ships a callable schema lambda; load it idempotently
|
|
57
|
+
# against the rails_pulse connection.
|
|
58
|
+
Bundler.with_unbundled_env do
|
|
59
|
+
run "bin/rails db:schema:load_rails_pulse"
|
|
60
|
+
end
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
def mount_rails_pulse_engine
|
|
@@ -6,6 +6,24 @@ RailsPulse.configure do |config|
|
|
|
6
6
|
|
|
7
7
|
# Asset tracking (disable to reduce noise)
|
|
8
8
|
config.track_assets = false
|
|
9
|
+
|
|
10
|
+
# Background job tracking (off by default in the gem; enabling mounts /jobs)
|
|
11
|
+
config.track_jobs = true
|
|
12
|
+
|
|
13
|
+
# Don't capture job arguments — they may contain sensitive data
|
|
14
|
+
config.capture_job_arguments = false
|
|
15
|
+
|
|
16
|
+
# Match the engine mount so Pulse doesn't self-track its own dashboard
|
|
17
|
+
config.mount_path = "<%= options[:route] %>"
|
|
18
|
+
|
|
19
|
+
# Auth is handled upstream by ManagementConstraint in routes.rb;
|
|
20
|
+
# disable the gem's built-in auth to avoid double prompts.
|
|
21
|
+
config.authentication_enabled = false
|
|
22
|
+
|
|
23
|
+
# Skip Rails' default health check and other mounted management engines
|
|
24
|
+
# (mission_control-jobs, solid_errors, litestream, etc.) so they don't
|
|
25
|
+
# pollute the application route list.
|
|
26
|
+
config.ignored_routes = ["/up", "/cable", %r{^/manage/}]
|
|
9
27
|
<%- if options[:database] -%>
|
|
10
28
|
|
|
11
29
|
# Use separate database for performance data
|
|
@@ -18,7 +18,14 @@ module Plutonium
|
|
|
18
18
|
raise exception
|
|
19
19
|
end
|
|
20
20
|
format.any do
|
|
21
|
-
|
|
21
|
+
# ActionPolicy stores the policy *class* on the exception
|
|
22
|
+
# (see ActionPolicy::Unauthorized#initialize), so reach for the
|
|
23
|
+
# live policy instance instead. Its record may itself be a Class
|
|
24
|
+
# for collection actions where no record is loaded — instantiate
|
|
25
|
+
# so ActiveModel::Errors has a real model instance to work with.
|
|
26
|
+
record = current_policy.record
|
|
27
|
+
record = record.new if record.is_a?(Class)
|
|
28
|
+
@errors = ActiveModel::Errors.new(record)
|
|
22
29
|
@errors.add(:base, :unauthorized, message: exception.result.message)
|
|
23
30
|
render "errors", status: :forbidden
|
|
24
31
|
end
|
|
@@ -248,8 +255,8 @@ module Plutonium
|
|
|
248
255
|
end
|
|
249
256
|
|
|
250
257
|
# Build named route helper, mirroring the pattern used by build_nested_resource_url_args.
|
|
251
|
-
# e.g., "
|
|
252
|
-
# "
|
|
258
|
+
# e.g., "organization_scoped_widgets" (collection), "organization_scoped_widget" (member),
|
|
259
|
+
# "edit_organization_scoped_widget" (edit action)
|
|
253
260
|
is_collection_action = action == :index || action == :create || (no_record && action != :new)
|
|
254
261
|
helper_base = if is_singular || is_collection_action
|
|
255
262
|
model_class.model_name.plural
|
data/lib/plutonium/engine.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Plutonium
|
|
|
19
19
|
# time — they're stable strings and don't depend on the live class
|
|
20
20
|
# identity, so caching them is safe.
|
|
21
21
|
resolved = @scoped_entity_class_name.constantize
|
|
22
|
-
@scoped_entity_param_key = param_key || :"#{resolved.model_name.singular_route_key}
|
|
22
|
+
@scoped_entity_param_key = param_key || :"#{resolved.model_name.singular_route_key}_scoped"
|
|
23
23
|
@scoped_entity_route_key = route_key || resolved.model_name.singular.to_sym
|
|
24
24
|
end
|
|
25
25
|
|
|
@@ -6,32 +6,21 @@ module Plutonium
|
|
|
6
6
|
# @example Basic usage
|
|
7
7
|
# render ColorModeSelector.new
|
|
8
8
|
class ColorModeSelector < Plutonium::UI::Component::Base
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
icon: "w-5 h-5"
|
|
13
|
-
}.freeze
|
|
9
|
+
BUTTON_CLASSES = "inline-flex justify-center items-center p-2 text-[var(--pu-text-muted)] rounded-[var(--pu-radius-md)] cursor-pointer hover:text-[var(--pu-text)] hover:bg-[var(--pu-surface-alt)] transition-colors duration-200"
|
|
10
|
+
ICON_SIZE = 18
|
|
11
|
+
ICON_STROKE = 1.5
|
|
14
12
|
|
|
15
|
-
# Available color modes with their associated icons and actions
|
|
16
|
-
COLOR_MODES = [
|
|
17
|
-
{mode: "light", icon: Phlex::TablerIcons::Sun, action: "setLightColorMode"},
|
|
18
|
-
{mode: "dark", icon: Phlex::TablerIcons::Moon, action: "setDarkColorMode"}
|
|
19
|
-
].freeze
|
|
20
|
-
|
|
21
|
-
# Renders the color mode selector
|
|
22
|
-
# @return [void]
|
|
23
13
|
def view_template
|
|
24
14
|
button(
|
|
25
15
|
type: "button",
|
|
26
|
-
class:
|
|
16
|
+
class: BUTTON_CLASSES,
|
|
27
17
|
data_controller: "color-mode",
|
|
28
18
|
data_action: "click->color-mode#toggleMode",
|
|
29
|
-
data_color_mode_current_value: "light", # Default to light mode
|
|
30
19
|
title: "Toggle color mode"
|
|
31
20
|
) do
|
|
32
|
-
|
|
33
|
-
render Phlex::TablerIcons::Sun.new(class: "
|
|
34
|
-
render Phlex::TablerIcons::Moon.new(class: "
|
|
21
|
+
render Phlex::TablerIcons::DeviceDesktop.new(size: ICON_SIZE, stroke: ICON_STROKE, class: "color-mode-icon-auto")
|
|
22
|
+
render Phlex::TablerIcons::Sun.new(size: ICON_SIZE, stroke: ICON_STROKE, class: "color-mode-icon-light hidden")
|
|
23
|
+
render Phlex::TablerIcons::Moon.new(size: ICON_SIZE, stroke: ICON_STROKE, class: "color-mode-icon-dark hidden")
|
|
35
24
|
end
|
|
36
25
|
end
|
|
37
26
|
end
|
|
@@ -15,6 +15,12 @@ module Plutonium
|
|
|
15
15
|
class: "flex flex-col items-center justify-center gap-2 px-6 py-8 mx-auto lg:py-0"
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
+
def render_before_main
|
|
19
|
+
div(class: "absolute top-4 right-4") {
|
|
20
|
+
render Plutonium::UI::ColorModeSelector.new
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
18
24
|
def render_content(&)
|
|
19
25
|
render_logo
|
|
20
26
|
|
data/lib/plutonium/version.rb
CHANGED
data/package.json
CHANGED
data/plutonium.gemspec
CHANGED
|
@@ -16,6 +16,22 @@ Gem::Specification.new do |spec|
|
|
|
16
16
|
|
|
17
17
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
18
18
|
|
|
19
|
+
spec.post_install_message = <<~MSG
|
|
20
|
+
⚠️ Plutonium #{Plutonium::VERSION} — breaking change
|
|
21
|
+
|
|
22
|
+
Entity-scoped URL helpers and path params have been renamed from
|
|
23
|
+
`<entity>_scope_*` to `<entity>_scoped_*`.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
organization_scope_widgets_path → organization_scoped_widgets_path
|
|
27
|
+
params[:organization_scope] → params[:organization_scoped]
|
|
28
|
+
|
|
29
|
+
If you reference these helpers or params directly (e.g. in tests, custom
|
|
30
|
+
redirects, or hand-written links), update them to the new names.
|
|
31
|
+
|
|
32
|
+
Apps that only use `resource_url_for` are unaffected.
|
|
33
|
+
MSG
|
|
34
|
+
|
|
19
35
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
20
36
|
spec.metadata["source_code_uri"] = "https://github.com/radioactive-labs/plutonium-core"
|
|
21
37
|
# spec.metadata["changelog_uri"] = "https://google.com"
|
|
@@ -1,66 +1,73 @@
|
|
|
1
1
|
import { Controller } from "@hotwired/stimulus";
|
|
2
2
|
|
|
3
3
|
// Connects to data-controller="color-mode"
|
|
4
|
+
//
|
|
5
|
+
// Shared theme state across the app. localStorage key 'theme' holds one of:
|
|
6
|
+
// 'auto' — follow prefers-color-scheme (default when unset)
|
|
7
|
+
// 'light' — force light
|
|
8
|
+
// 'dark' — force dark
|
|
9
|
+
const ORDER = ['auto', 'light', 'dark'];
|
|
10
|
+
|
|
4
11
|
export default class extends Controller {
|
|
5
12
|
static values = { current: String };
|
|
6
13
|
|
|
7
14
|
connect() {
|
|
8
|
-
|
|
9
|
-
const mode = localStorage.getItem('theme') || "light";
|
|
10
|
-
this.setMode(mode);
|
|
15
|
+
this.applyMode(this.readMode());
|
|
11
16
|
|
|
12
|
-
// Listen for cross-tab theme changes
|
|
13
17
|
this.handleStorageChange = (e) => {
|
|
14
|
-
|
|
15
|
-
if (e.key === 'theme' && e.newValue) {
|
|
16
|
-
console.log('Updating color-mode theme to:', e.newValue)
|
|
17
|
-
this.setMode(e.newValue);
|
|
18
|
-
}
|
|
18
|
+
if (e.key === 'theme') this.applyMode(this.readMode());
|
|
19
19
|
};
|
|
20
20
|
window.addEventListener('storage', this.handleStorageChange);
|
|
21
|
+
|
|
22
|
+
this.mq = window.matchMedia('(prefers-color-scheme: dark)');
|
|
23
|
+
this.handleMqChange = () => {
|
|
24
|
+
if (this.readMode() === 'auto') this.applyMode('auto');
|
|
25
|
+
};
|
|
26
|
+
this.mq.addEventListener('change', this.handleMqChange);
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
disconnect() {
|
|
24
|
-
// Clean up event listener
|
|
25
30
|
window.removeEventListener('storage', this.handleStorageChange);
|
|
31
|
+
if (this.mq) this.mq.removeEventListener('change', this.handleMqChange);
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
toggleMode() {
|
|
29
|
-
const current = this.
|
|
30
|
-
const next = current
|
|
35
|
+
const current = this.readMode();
|
|
36
|
+
const next = ORDER[(ORDER.indexOf(current) + 1) % ORDER.length];
|
|
31
37
|
this.setMode(next);
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
setMode(mode) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
} else {
|
|
39
|
-
document.documentElement.classList.remove("dark");
|
|
40
|
-
}
|
|
41
|
+
localStorage.setItem('theme', mode);
|
|
42
|
+
this.applyMode(mode);
|
|
43
|
+
}
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
applyMode(mode) {
|
|
46
|
+
const effective = this.effectiveMode(mode);
|
|
47
|
+
document.documentElement.classList.toggle('dark', effective === 'dark');
|
|
43
48
|
this.currentValue = mode;
|
|
44
|
-
|
|
45
|
-
// Show/hide icons
|
|
46
49
|
this.toggleIcons(mode);
|
|
50
|
+
}
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
localStorage.
|
|
52
|
+
readMode() {
|
|
53
|
+
const saved = localStorage.getItem('theme');
|
|
54
|
+
return ORDER.includes(saved) ? saved : 'auto';
|
|
50
55
|
}
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
effectiveMode(mode) {
|
|
58
|
+
if (mode === 'light' || mode === 'dark') return mode;
|
|
59
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
60
|
+
}
|
|
55
61
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
toggleIcons(mode) {
|
|
63
|
+
const icons = {
|
|
64
|
+
auto: this.element.querySelector(".color-mode-icon-auto"),
|
|
65
|
+
light: this.element.querySelector(".color-mode-icon-light"),
|
|
66
|
+
dark: this.element.querySelector(".color-mode-icon-dark"),
|
|
67
|
+
};
|
|
68
|
+
for (const [key, el] of Object.entries(icons)) {
|
|
69
|
+
if (!el) continue;
|
|
70
|
+
el.classList.toggle("hidden", key !== mode);
|
|
64
71
|
}
|
|
65
72
|
}
|
|
66
73
|
}
|
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.49.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Froelich
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-04
|
|
10
|
+
date: 2026-05-04 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: zeitwerk
|
|
@@ -665,6 +665,7 @@ files:
|
|
|
665
665
|
- lib/generators/pu/field/renderer/templates/renderer.rb.tt
|
|
666
666
|
- lib/generators/pu/gem/active_shrine/active_shrine_generator.rb
|
|
667
667
|
- lib/generators/pu/gem/active_shrine/templates/config/initializers/shrine.rb.tt
|
|
668
|
+
- lib/generators/pu/gem/actual_db_schema/actual_db_schema_generator.rb
|
|
668
669
|
- lib/generators/pu/gem/annotated/annotated_generator.rb
|
|
669
670
|
- lib/generators/pu/gem/annotated/templates/.keep
|
|
670
671
|
- lib/generators/pu/gem/annotated/templates/lib/tasks/auto_annotate_models.rake
|
|
@@ -1140,6 +1141,20 @@ metadata:
|
|
|
1140
1141
|
allowed_push_host: https://rubygems.org
|
|
1141
1142
|
homepage_uri: https://radioactive-labs.github.io/plutonium-core/
|
|
1142
1143
|
source_code_uri: https://github.com/radioactive-labs/plutonium-core
|
|
1144
|
+
post_install_message: |
|
|
1145
|
+
⚠️ Plutonium 0.49.0 — breaking change
|
|
1146
|
+
|
|
1147
|
+
Entity-scoped URL helpers and path params have been renamed from
|
|
1148
|
+
`<entity>_scope_*` to `<entity>_scoped_*`.
|
|
1149
|
+
|
|
1150
|
+
Examples:
|
|
1151
|
+
organization_scope_widgets_path → organization_scoped_widgets_path
|
|
1152
|
+
params[:organization_scope] → params[:organization_scoped]
|
|
1153
|
+
|
|
1154
|
+
If you reference these helpers or params directly (e.g. in tests, custom
|
|
1155
|
+
redirects, or hand-written links), update them to the new names.
|
|
1156
|
+
|
|
1157
|
+
Apps that only use `resource_url_for` are unaffected.
|
|
1143
1158
|
rdoc_options: []
|
|
1144
1159
|
require_paths:
|
|
1145
1160
|
- lib
|