command_deck 0.3.2 → 0.3.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ebceefafcd11660d8efdd358ad30b1d779b236d1cd135286b9fdb79871dd11bf
4
- data.tar.gz: 9d4bffc3981b9959dfd491d4d17a5f1e8e89465f54c461d20ffa63eb9b62328a
3
+ metadata.gz: db8d14ada48c592f9b29c2eb963733d4a8f612cdd757d2f1c27ddb8e0cd05087
4
+ data.tar.gz: 6be87cf67b3aa82c3df82b1c2458adc1df2e22ed159198ede724da4cadb0d784
5
5
  SHA512:
6
- metadata.gz: 9ea6c387e844f38d2cbdf0772b53979df7c93be42b8626f7be1120e61e9a052ae42fa0bb53bab411af1572723ce6824743137f16e1bf20ce040951c06ec7eda2
7
- data.tar.gz: 11969e5bff8030f91e81cd4a6ddf466fea747d35843da09661377e17f835d847b3dc9c185be941da7ca63597f88877965e6748c4b38214ec71dadfa8588dccf8
6
+ metadata.gz: 0db0f2c1ac8b73e7fff36ebba8369c60f4c1e741ef90bad6661fc9d2d234db890489af97354e1a2bf52b9c48b5ad07ab8b36b37131318eb83b53dc228d903c91
7
+ data.tar.gz: fa048ba9895ef507917aae29ca04a7e5cf60a208f5cb9db103e945eb9254676828925a2abbf36a32ede61b2d1b8d7bdd465cede0870a10690d52477f06b2284b
data/.rubocop.yml CHANGED
@@ -19,3 +19,22 @@ Style/StringLiterals:
19
19
 
20
20
  Style/StringLiteralsInInterpolation:
21
21
  EnforcedStyle: double_quotes
22
+
23
+ Metrics/MethodLength:
24
+ Exclude:
25
+ - "test/**/*"
26
+
27
+ Metrics/ClassLength:
28
+ Exclude:
29
+ - "test/**/*"
30
+
31
+ Metrics/AbcSize:
32
+ Exclude:
33
+ - "test/**/*"
34
+
35
+ Minitest/MultipleAssertions:
36
+ Enabled: false
37
+
38
+ Lint/EmptyBlock:
39
+ Exclude:
40
+ - "test/**/*"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## [0.3.3] - 2025-12-15
4
+
5
+ ### Added
6
+
7
+ - **Configurable context provider**: New `CommandDeck.configure` block allows injecting application context (current user, session, etc.) into action `perform` blocks via the `ctx` parameter
8
+ - **Loading spinner**: Actions now display a spinner animation while running
9
+ - **Full test coverage**: Comprehensive Minitest suite achieving 100% line coverage and 97.73% branch coverage
10
+
11
+ ### Fixed
12
+
13
+ - **Panel hot-reload**: Changed from `require_dependency` to `load` for more reliable panel reloading in development, also catches `SyntaxError` for better error reporting
14
+ - **Large panels overflow**: Added max-height and scrollbar support for panels with many actions
15
+
16
+ ### Changed
17
+
18
+ - Relaxed RuboCop metrics for test files to allow comprehensive test methods
19
+
3
20
  ## [0.3.2] - 2025-10-15
4
21
 
5
22
  ### Added
data/README.md CHANGED
@@ -169,6 +169,44 @@ Selector-specific options:
169
169
 
170
170
  The `perform` block receives coerced params and a context hash. Return any JSON-serializable object (Hash recommended).
171
171
 
172
+ ## Context Provider
173
+
174
+ The `ctx` parameter in perform blocks is populated via a configurable context provider. This allows you to inject application-specific context like the current user:
175
+
176
+ ```ruby
177
+ # config/initializers/command_deck.rb
178
+ CommandDeck.configure do |config|
179
+ config.context_provider = ->(request) do
180
+ {
181
+ current_user: request.env['warden']&.user, # Works with Devise
182
+ session: request.session,
183
+ # Add any other context your actions need
184
+ }
185
+ end
186
+ end
187
+ ```
188
+
189
+ Then in your panels:
190
+
191
+ ```ruby
192
+ action 'Toggle Admin', key: 'user.toggle_admin' do
193
+ perform do |_params, ctx|
194
+ user = ctx[:current_user]
195
+ return { error: 'Not logged in' } unless user
196
+
197
+ user.update!(admin: !user.admin?)
198
+ { ok: true, admin: user.admin? }
199
+ end
200
+ end
201
+ ```
202
+
203
+ The context provider receives the full `ActionDispatch::Request` object, giving you access to:
204
+
205
+ - `request.env['warden']` - Warden/Devise user
206
+ - `request.session` - Rails session
207
+ - `request.cookies` - Request cookies
208
+ - `request.env` - Full Rack environment
209
+
172
210
  ## Security
173
211
 
174
212
  Intended for development only. **DO NOT ENABLE IN PRODUCTION**.
data/Rakefile CHANGED
@@ -3,7 +3,12 @@
3
3
  require "bundler/gem_tasks"
4
4
  require "minitest/test_task"
5
5
 
6
- Minitest::TestTask.create
6
+ Minitest::TestTask.create do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_prelude = 'require "test_helper"'
10
+ t.test_globs = ["test/*_test.rb"]
11
+ end
7
12
 
8
13
  require "rubocop/rake_task"
9
14
 
@@ -6,7 +6,7 @@ module CommandDeck
6
6
  def create
7
7
  key = params[:key].to_s
8
8
  begin
9
- result = Executor.call(key: key, params: params[:params])
9
+ result = Executor.call(key: key, params: params[:params], request: request)
10
10
  render json: { ok: true, result: result }
11
11
  rescue ArgumentError => e
12
12
  render json: { ok: false, error: e.message }, status: :not_found
@@ -17,7 +17,7 @@
17
17
  align-items: center;
18
18
  justify-content: center;
19
19
  font: 600 14px system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
20
- box-shadow: 0 6px 20px rgba(0, 0, 0, .25);
20
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
21
21
  cursor: pointer;
22
22
  }
23
23
 
@@ -25,7 +25,7 @@
25
25
  #command-deck-toggle.cd-theme-dark {
26
26
  background: #000; /* full black */
27
27
  color: #fff;
28
- box-shadow: 0 6px 20px rgba(0, 0, 0, .6);
28
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.6);
29
29
  }
30
30
 
31
31
  #command-deck-panel {
@@ -38,9 +38,9 @@
38
38
  background: #fff;
39
39
  color: #111;
40
40
  border-radius: 12px;
41
- box-shadow: 0 12px 40px rgba(0, 0, 0, .28);
41
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.28);
42
42
  padding: 12px;
43
- border: 1px solid rgba(0, 0, 0, .06);
43
+ border: 1px solid rgba(0, 0, 0, 0.06);
44
44
  }
45
45
 
46
46
  /* Dark theme for panel */
@@ -48,7 +48,7 @@
48
48
  background: #000; /* full black */
49
49
  color: #f1f1f1;
50
50
  border-color: #222;
51
- box-shadow: 0 12px 40px rgba(0,0,0,.7);
51
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
52
52
  }
53
53
 
54
54
  #command-deck-panel.cd-theme-dark #cd-tabs-wrap .cd-tab-btn,
@@ -62,7 +62,9 @@
62
62
  border-color: #333;
63
63
  }
64
64
 
65
- #command-deck-panel.cd-theme-dark .cd-action { border-color: #222; }
65
+ #command-deck-panel.cd-theme-dark .cd-action {
66
+ border-color: #222;
67
+ }
66
68
 
67
69
  /* Dark mode: improve readability of labels and placeholders */
68
70
  #command-deck-panel.cd-theme-dark .cd-form label {
@@ -102,7 +104,8 @@
102
104
  }
103
105
 
104
106
  #command-deck-panel * {
105
- font: 500 13px/1.35 system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
107
+ font: 500 13px/1.35 system-ui, -apple-system, Segoe UI, Roboto, Arial,
108
+ sans-serif;
106
109
  }
107
110
 
108
111
  /* ---------- Header & Settings ---------- */
@@ -168,7 +171,7 @@
168
171
  background: #fff;
169
172
  border: 1px solid #e5e5e5;
170
173
  border-radius: 8px;
171
- box-shadow: 0 10px 28px rgba(0,0,0,.12);
174
+ box-shadow: 0 10px 28px rgba(0, 0, 0, 0.12);
172
175
  padding: 8px;
173
176
  z-index: 2147483001;
174
177
  min-width: 180px;
@@ -178,7 +181,7 @@
178
181
  #command-deck-panel.cd-theme-dark #cd-settings-panel {
179
182
  background: #0d0d0d;
180
183
  border-color: #333;
181
- box-shadow: 0 10px 28px rgba(0,0,0,.6);
184
+ box-shadow: 0 10px 28px rgba(0, 0, 0, 0.6);
182
185
  }
183
186
 
184
187
  .cd-settings-title {
@@ -227,6 +230,8 @@
227
230
  display: flex;
228
231
  flex-direction: column;
229
232
  gap: 12px;
233
+ max-height: 50vh;
234
+ overflow-y: auto;
230
235
  }
231
236
 
232
237
  .cd-action {
@@ -309,18 +314,18 @@
309
314
  border: 1px solid #fecaca;
310
315
  }
311
316
 
312
- #command-deck-panel.cd-theme-dark .cd-param-hint {
313
- color: #bdbdbd;
317
+ #command-deck-panel.cd-theme-dark .cd-param-hint {
318
+ color: #bdbdbd;
314
319
  }
315
- #command-deck-panel.cd-theme-dark .cd-badge.cd-on {
316
- background: #0b2f1c;
317
- color: #a7f3d0;
318
- border-color: #065f46;
320
+ #command-deck-panel.cd-theme-dark .cd-badge.cd-on {
321
+ background: #0b2f1c;
322
+ color: #a7f3d0;
323
+ border-color: #065f46;
319
324
  }
320
- #command-deck-panel.cd-theme-dark .cd-badge.cd-off {
321
- background: #3b0c0c;
322
- color: #fecaca;
323
- border-color: #7f1d1d;
325
+ #command-deck-panel.cd-theme-dark .cd-badge.cd-off {
326
+ background: #3b0c0c;
327
+ color: #fecaca;
328
+ border-color: #7f1d1d;
324
329
  }
325
330
 
326
331
  .cd-form .cd-run {
@@ -397,6 +402,23 @@
397
402
  overflow: auto;
398
403
  }
399
404
 
405
+ /* ---------- Spinner ---------- */
406
+ .cd-spinner {
407
+ display: inline-block;
408
+ width: 12px;
409
+ height: 12px;
410
+ border: 2px solid currentColor;
411
+ border-right-color: transparent;
412
+ border-radius: 50%;
413
+ animation: cd-spin 0.75s linear infinite;
414
+ vertical-align: middle;
415
+ margin-right: 4px;
416
+ }
417
+
418
+ @keyframes cd-spin {
419
+ to { transform: rotate(360deg); }
420
+ }
421
+
400
422
  /* ---------- Corner Positioning ---------- */
401
423
  #command-deck-toggle.cd-pos-tl {
402
424
  left: 16px;
@@ -57,7 +57,7 @@ export class ResultView {
57
57
 
58
58
  setRunning() {
59
59
  this.setVisible(true);
60
- this.pre.textContent = 'Running...';
60
+ this.pre.innerHTML = '<span class="cd-spinner"></span> Running...';
61
61
  }
62
62
 
63
63
  setResult(obj) {
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Command Deck configuration module.
4
+ # Configure context providers and other global settings here.
5
+ module CommandDeck
6
+ # Configuration for Command Deck
7
+ #
8
+ # Example usage in an initializer:
9
+ #
10
+ # # config/initializers/command_deck.rb
11
+ # CommandDeck.configure do |config|
12
+ # config.context_provider = ->(request) do
13
+ # {
14
+ # current_user: request.env['warden']&.user,
15
+ # session: request.session
16
+ # }
17
+ # end
18
+ # end
19
+ class Configuration
20
+ # A proc that receives the ActionDispatch::Request and returns a hash
21
+ # to be passed as the `ctx` parameter in perform blocks.
22
+ #
23
+ # @example
24
+ # config.context_provider = ->(request) do
25
+ # { current_user: request.env['warden']&.user }
26
+ # end
27
+ attr_accessor :context_provider
28
+
29
+ def initialize
30
+ @context_provider = ->(_request) { {} }
31
+ end
32
+
33
+ # Builds the context hash from the given request
34
+ #
35
+ # @param request [ActionDispatch::Request] the current request
36
+ # @return [Hash] context to pass to perform blocks
37
+ def build_context(request)
38
+ return {} unless context_provider.respond_to?(:call)
39
+
40
+ context_provider.call(request) || {}
41
+ rescue StandardError => e
42
+ Rails.logger.warn "[CommandDeck] Context provider error: #{e.message}" if defined?(Rails)
43
+ {}
44
+ end
45
+ end
46
+
47
+ class << self
48
+ def configuration
49
+ @configuration ||= Configuration.new
50
+ end
51
+
52
+ def configure
53
+ yield(configuration)
54
+ end
55
+
56
+ def reset_configuration!
57
+ @configuration = Configuration.new
58
+ end
59
+ end
60
+ end
@@ -40,8 +40,8 @@ module CommandDeck
40
40
 
41
41
  Engine.discover_panel_paths.each do |path|
42
42
  Dir.glob(path.join("**/*.rb")).each do |file|
43
- require_dependency file
44
- rescue LoadError, NameError => e
43
+ load file
44
+ rescue LoadError, SyntaxError, NameError => e
45
45
  Rails.logger.warn "[CommandDeck] Could not load panel #{file}: #{e.message}"
46
46
  end
47
47
  end
@@ -3,12 +3,13 @@
3
3
  module CommandDeck
4
4
  # Executor for running actions
5
5
  class Executor
6
- def self.call(key:, params:)
6
+ def self.call(key:, params:, request: nil)
7
7
  action = Registry.find_action(key)
8
8
  raise ArgumentError, "Unknown action #{key}" unless action
9
9
 
10
10
  coerced = coerce(action.params, params || {})
11
- action.block.call(coerced, {})
11
+ ctx = request ? CommandDeck.configuration.build_context(request) : {}
12
+ action.block.call(coerced, ctx)
12
13
  end
13
14
 
14
15
  def self.coerce(schema, raw)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommandDeck
4
- VERSION = "0.3.2"
4
+ VERSION = "0.3.3"
5
5
  end
data/lib/command_deck.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "command_deck/version"
4
+ require "command_deck/configuration"
4
5
  require "command_deck/registry"
5
6
  require "command_deck/executor"
6
7
  require "command_deck/base_panel"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command_deck
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - crowrojas
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-10-15 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: actionpack
@@ -90,6 +89,7 @@ files:
90
89
  - lib/command_deck/assets/js/ui/panel_selector.js
91
90
  - lib/command_deck/assets/js/ui/settings_dropdown.js
92
91
  - lib/command_deck/base_panel.rb
92
+ - lib/command_deck/configuration.rb
93
93
  - lib/command_deck/engine.rb
94
94
  - lib/command_deck/executor.rb
95
95
  - lib/command_deck/injector.rb
@@ -108,7 +108,6 @@ metadata:
108
108
  source_code_uri: https://github.com/crow-rojas/command_deck/tree/master
109
109
  changelog_uri: https://github.com/crow-rojas/command_deck/blob/master/CHANGELOG.md
110
110
  rubygems_mfa_required: 'true'
111
- post_install_message:
112
111
  rdoc_options: []
113
112
  require_paths:
114
113
  - lib
@@ -123,8 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
122
  - !ruby/object:Gem::Version
124
123
  version: '0'
125
124
  requirements: []
126
- rubygems_version: 3.5.22
127
- signing_key:
125
+ rubygems_version: 3.6.9
128
126
  specification_version: 4
129
127
  summary: Dev-oriented floating panel for running custom actions in Rails apps
130
128
  test_files: []