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 +4 -4
- data/.rubocop.yml +19 -0
- data/CHANGELOG.md +17 -0
- data/README.md +38 -0
- data/Rakefile +6 -1
- data/app/controllers/command_deck/actions_controller.rb +1 -1
- data/lib/command_deck/assets/css/main.css +41 -19
- data/lib/command_deck/assets/js/ui/overlay/result_view.js +1 -1
- data/lib/command_deck/configuration.rb +60 -0
- data/lib/command_deck/engine.rb +2 -2
- data/lib/command_deck/executor.rb +3 -2
- data/lib/command_deck/version.rb +1 -1
- data/lib/command_deck.rb +1 -0
- metadata +4 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db8d14ada48c592f9b29c2eb963733d4a8f612cdd757d2f1c27ddb8e0cd05087
|
|
4
|
+
data.tar.gz: 6be87cf67b3aa82c3df82b1c2458adc1df2e22ed159198ede724da4cadb0d784
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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 {
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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;
|
|
@@ -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
|
data/lib/command_deck/engine.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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)
|
data/lib/command_deck/version.rb
CHANGED
data/lib/command_deck.rb
CHANGED
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.
|
|
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:
|
|
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.
|
|
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: []
|