ultra_settings 2.10.0 → 2.10.1
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 +26 -0
- data/README.md +18 -0
- data/VERSION +1 -1
- data/app/_config_description.html.erb +10 -1
- data/app/_data_source.html.erb +2 -2
- data/app/application.css +116 -24
- data/app/application_vars.css.erb +30 -12
- data/app/configuration.html.erb +19 -1
- data/app/layout.css +5 -5
- data/app/layout_vars.css.erb +4 -4
- data/app/locales/ar.json +5 -2
- data/app/locales/cs.json +5 -2
- data/app/locales/da.json +5 -2
- data/app/locales/de.json +5 -2
- data/app/locales/el.json +5 -2
- data/app/locales/en.json +5 -2
- data/app/locales/es.json +5 -2
- data/app/locales/fa.json +5 -2
- data/app/locales/fr.json +5 -2
- data/app/locales/gd.json +5 -2
- data/app/locales/he.json +5 -2
- data/app/locales/hi.json +5 -2
- data/app/locales/it.json +5 -2
- data/app/locales/ja.json +5 -2
- data/app/locales/ko.json +5 -2
- data/app/locales/lt.json +5 -2
- data/app/locales/nb.json +5 -2
- data/app/locales/nl.json +5 -2
- data/app/locales/pl.json +5 -2
- data/app/locales/pt-br.json +5 -2
- data/app/locales/pt.json +5 -2
- data/app/locales/ru.json +5 -2
- data/app/locales/sv.json +5 -2
- data/app/locales/ta.json +5 -2
- data/app/locales/tr.json +5 -2
- data/app/locales/uk.json +5 -2
- data/app/locales/ur.json +5 -2
- data/app/locales/vi.json +5 -2
- data/app/locales/zh-cn.json +5 -2
- data/app/locales/zh-tw.json +5 -2
- data/lib/ultra_settings/application_view.rb +13 -4
- data/lib/ultra_settings/audit_data_sources.rb +1 -1
- data/lib/ultra_settings/coerce.rb +1 -1
- data/lib/ultra_settings/configuration.rb +36 -10
- data/lib/ultra_settings/configuration_view.rb +166 -9
- data/lib/ultra_settings/mini_i18n.rb +14 -10
- data/lib/ultra_settings/rack_app.rb +4 -4
- data/lib/ultra_settings/view_helper.rb +27 -5
- data/lib/ultra_settings/web_view.rb +17 -10
- data/lib/ultra_settings/yaml_config.rb +10 -2
- data/lib/ultra_settings.rb +57 -1
- data/ultra_settings.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 92ad90432ce22bfa615bda0956cbd80d8f06115c925b99b0a250dc9c76be7dcf
|
|
4
|
+
data.tar.gz: 3163c675d4ef57edab7cc438f0d1553bf160dd8003b03f14c59f30b7f39d79fb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b938c8d2275dfc72319485edde8bc36d5ae866d31225201967d98c9c78873256b838f682cd8709a52ca645b713eea578820baaf96cf8ebb8242debcdb355a9a0
|
|
7
|
+
data.tar.gz: e689fd1da5cc1a647475a61efb7586544ae6ae383c9bd1d057c738e7067f055341ab8e83c7f3b5103d0b49dd0700972d45ad4cbea9be2873b28611c68676d90b
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 2.10.1
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added a button to the web UI for copying the current value of a setting to the clipboard. The button is disabled for secret settings and is not shown for settings that have no value.
|
|
12
|
+
- Added `UltraSettings.with_runtime_settings_reloaded` for rendering several embedded views with a single runtime settings reload.
|
|
13
|
+
- Added a toggle switch to the web UI for showing and hiding the YAML keys of a configuration. The switch is only shown when the YAML file does not exist, and the keys are hidden by default in that case since they are not used by the application.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- The web UI now fully reloads the runtime settings cache before rendering instead of requesting an incremental refresh. Previously a value changed from the UI could still render as its old value after the page reloaded, since the incremental refresh would silently do nothing if another thread was already refreshing, if the cache had never been loaded, or if the change had been saved by a different process. The reload is performed on any runtime settings object that responds to `load_settings` — including wrappers around `SuperSettings` — rather than only on `SuperSettings` itself, and an error raised while reloading is now reported to stderr instead of failing the page.
|
|
18
|
+
- The reload is now performed by `UltraSettings::ApplicationView#render` and `UltraSettings::ConfigurationView#render` rather than only by the standalone Rack app, so views embedded in a host application also display current values. Rendering a page with many configurations through `ApplicationView` still only reloads once; a page that embeds several `ConfigurationView` instances directly can wrap them in `UltraSettings.with_runtime_settings_reloaded` to reload once for the page rather than once per view.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- YAML configuration files with anchors and aliases (e.g. `<<: *defaults`) and unquoted date or time values now load correctly on Ruby 3.1+ where `YAML.load` is safe by default.
|
|
23
|
+
- Fixed a race condition where concurrent requests to the web UI could render with each other's locale because per-request state was stored on a shared view instance.
|
|
24
|
+
- Fixed a race condition where the web UI translation cache could return a partially loaded set of locales to concurrent requests while the cache was being populated.
|
|
25
|
+
- The web UI now recognizes `RAILS_ENV` and `APP_ENV` in addition to `RACK_ENV` when determining if the app is running in development mode. Previously a Rails app running in production without `RACK_ENV` set would re-read templates and translations from disk on every request. The check is shared by the template cache, the translation cache, and the Rack app's view cache so that they cannot disagree.
|
|
26
|
+
- Fixed a memory leak where `Configuration#override!` retained an entry for every thread that had ever used it.
|
|
27
|
+
- Fixed `AuditDataSources` treating a `false` YAML value as missing and falling back to the field default.
|
|
28
|
+
- Static field memoization and configuration subclass tracking are now fully synchronized for Ruby runtimes without a global VM lock (JRuby, TruffleRuby).
|
|
29
|
+
- Web UI asset, template, translation, and YAML configuration files are now always read as UTF-8 so the gem works when the default external encoding is not UTF-8 (e.g. when `LANG` is not set).
|
|
30
|
+
- The web UI no longer raises an error when determining relative configuration file paths if the YAML configuration directory does not exist or is not set.
|
|
31
|
+
- The web UI no longer displays configuration file paths with a leading relative path prefix (e.g. `../app/config/settings.yml`). Paths are now shown relative to the working directory or to the YAML configuration directory, and the absolute path is shown if the file is not inside either one.
|
|
32
|
+
|
|
7
33
|
## 2.10.0
|
|
8
34
|
|
|
9
35
|
### Added
|
data/README.md
CHANGED
|
@@ -171,6 +171,8 @@ UltraSettings.runtime_settings = RedisRuntimeSettings.new
|
|
|
171
171
|
|
|
172
172
|
The runtime settings implementation may also define an `array` method that takes a single parameter to return an array value. If this method is not implemented, then array values must be returned as single line CSV strings.
|
|
173
173
|
|
|
174
|
+
The runtime settings implementation may also define a `load_settings` method. If it does, the web UI will call it before rendering so that values changed elsewhere are displayed immediately instead of whenever the implementation's own cache next refreshes. It should reload synchronously and return once the values are current. Errors raised from it are logged to stderr and do not prevent the page from rendering.
|
|
175
|
+
|
|
174
176
|
> [!TIP]
|
|
175
177
|
> If your runtime settings implementation does not securely store values, you should set `UltraSettings.runtime_settings_secure` to `false`. This will disable runtime settings on fields marked as secret to prevent leaking sensitive information.
|
|
176
178
|
|
|
@@ -391,6 +393,8 @@ end
|
|
|
391
393
|
|
|
392
394
|
UltraSettings provides a web UI via a mountable Rack application. You can use this to view the settings values and documentation. The UI will not display the value of any setting marked as secret.
|
|
393
395
|
|
|
396
|
+
Each setting has a button for copying its current value to the clipboard. The copied text is the raw value rather than the quoted form shown on the page (arrays are copied as one entry per line). The button is disabled for secret settings since their values are never sent to the browser.
|
|
397
|
+
|
|
394
398
|

|
|
395
399
|
|
|
396
400
|
It is strongly recommended to secure the web UI with your application's authorization framework so that it is only visible to internal admin users.
|
|
@@ -445,6 +449,15 @@ You'll also need to include the CSS for the configuration view on your page.
|
|
|
445
449
|
</head>
|
|
446
450
|
```
|
|
447
451
|
|
|
452
|
+
Each view reloads the runtime settings before rendering so that it displays current values. If you embed more than one view on a page, wrap them in `UltraSettings.with_runtime_settings_reloaded` so that the settings are only reloaded once for the whole page instead of once per view.
|
|
453
|
+
|
|
454
|
+
```erb
|
|
455
|
+
<% UltraSettings.with_runtime_settings_reloaded do %>
|
|
456
|
+
<%= UltraSettings::ConfigurationView.new(MyServiceConfiguration.instance).render %>
|
|
457
|
+
<%= UltraSettings::ConfigurationView.new(OtherServiceConfiguration.instance).render %>
|
|
458
|
+
<% end %>
|
|
459
|
+
```
|
|
460
|
+
|
|
448
461
|
#### Customizing The Web UI
|
|
449
462
|
|
|
450
463
|
You can specify the color scheme by setting by providing the `color_scheme` option to the `UltraSettings::ApplicationView` constructor. The default color scheme is `:light`. You can also set the scheme to `:dark` or `:system`.
|
|
@@ -482,6 +495,11 @@ All authorization is handled by the SuperSettings API — the same permissions t
|
|
|
482
495
|
> [!IMPORTANT]
|
|
483
496
|
> You will need to have the SuperSettings API mounted and properly locked down with authentication. If the API is not accessible, then the edit buttons will not be shown. Check the browser console for any errors if you expect the buttons to be shown but they are not appearing.
|
|
484
497
|
|
|
498
|
+
`SuperSettings` responds to `load_settings`, so the web UI reloads the SuperSettings cache before each render and a setting saved from the edit panel is displayed as soon as the page reloads.
|
|
499
|
+
|
|
500
|
+
> [!NOTE]
|
|
501
|
+
> In a Rails application, `SuperSettings::Context::RackMiddleware` pins each setting's value for the remainder of a request once it has been read. This does not affect the standalone Rack app since the reload happens before any value is read. However, if you embed `UltraSettings::ApplicationView` in a page that already read a runtime setting earlier in the same request, that particular setting will still render with its pre-reload value.
|
|
502
|
+
|
|
485
503
|
##### Embedding with Edit Support
|
|
486
504
|
|
|
487
505
|
When embedding the settings view using `UltraSettings::ApplicationView`, you can pass the `super_settings_api_path` option to enable the inline editing UI:
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.10.
|
|
1
|
+
2.10.1
|
|
@@ -19,8 +19,17 @@
|
|
|
19
19
|
<span class="ultra-settings-warn">
|
|
20
20
|
<%= warning_icon(13) %>
|
|
21
21
|
<code><%= html_escape(relative_path(configuration.class.configuration_file)) %></code>
|
|
22
|
-
<%= t('config.file_not_found') %>
|
|
22
|
+
• <%= t('config.file_not_found') %>
|
|
23
23
|
</span>
|
|
24
|
+
|
|
25
|
+
<button type="button"
|
|
26
|
+
class="ultra-settings-yaml-toggle"
|
|
27
|
+
onclick="<%= html_escape(toggle_yaml_keys_script) %>"
|
|
28
|
+
role="switch"
|
|
29
|
+
aria-checked="false">
|
|
30
|
+
<span class="ultra-settings-yaml-toggle-switch" aria-hidden="true"></span>
|
|
31
|
+
<%= t('config.show_yaml_keys') %>
|
|
32
|
+
</button>
|
|
24
33
|
<% end %>
|
|
25
34
|
</div>
|
|
26
35
|
<% end %>
|
data/app/_data_source.html.erb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<% source_label = source_chip_label(source) %>
|
|
3
3
|
<% chip_class = source_chip_class(source) %>
|
|
4
4
|
<% source_name = source_key_name(field, source) %>
|
|
5
|
-
<div class="ultra-settings-source-row<%= ' active' if current_source == source %>">
|
|
5
|
+
<div class="ultra-settings-source-row<%= ' active' if current_source == source %>" data-source="<%= html_escape(source) %>">
|
|
6
6
|
<span class="ultra-settings-source-chip <%= chip_class %>"><%= source_label %></span>
|
|
7
7
|
|
|
8
8
|
<% if source_name %>
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
<% end %>
|
|
47
47
|
|
|
48
48
|
<% if current_source == source %>
|
|
49
|
-
<span class="ultra-settings-source-badge ultra-settings-sb-active"
|
|
49
|
+
<span class="ultra-settings-source-badge ultra-settings-sb-active">● <%= t('source.active') %></span>
|
|
50
50
|
<% elsif source_overridden_by(source, current_source) && !source_value.nil? %>
|
|
51
51
|
<span class="ultra-settings-source-badge ultra-settings-sb-overridden"><%= t('source.overridden') %></span>
|
|
52
52
|
<% end %>
|
data/app/application.css
CHANGED
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
width: 100%;
|
|
54
54
|
padding: 10px 38px 10px 38px;
|
|
55
55
|
background: var(--bg-card);
|
|
56
|
-
border: 1px solid var(--border-
|
|
56
|
+
border: 1px solid var(--border-input);
|
|
57
57
|
border-radius: 8px;
|
|
58
58
|
color: var(--text-primary);
|
|
59
59
|
font-size: 0.875rem;
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
border-radius: 8px;
|
|
131
131
|
cursor: pointer;
|
|
132
132
|
user-select: none;
|
|
133
|
-
border: 1px solid var(--border-
|
|
133
|
+
border: 1px solid var(--border-input);
|
|
134
134
|
background: var(--bg-card);
|
|
135
135
|
color: var(--text-secondary);
|
|
136
136
|
font-size: 0.75rem;
|
|
@@ -181,7 +181,7 @@
|
|
|
181
181
|
width: 12px;
|
|
182
182
|
height: 12px;
|
|
183
183
|
color: inherit;
|
|
184
|
-
opacity: 0.
|
|
184
|
+
opacity: 0.75;
|
|
185
185
|
transition: transform 0.15s ease, opacity 0.15s ease;
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
.ultra-settings-language-menu[open] .ultra-settings-language-trigger-caret {
|
|
199
|
-
opacity:
|
|
199
|
+
opacity: 1;
|
|
200
200
|
transform: rotate(180deg);
|
|
201
201
|
}
|
|
202
202
|
|
|
@@ -416,6 +416,7 @@
|
|
|
416
416
|
.ultra-settings-config-file {
|
|
417
417
|
display: inline-flex;
|
|
418
418
|
align-items: center;
|
|
419
|
+
flex-wrap: wrap;
|
|
419
420
|
gap: 6px;
|
|
420
421
|
font-size: 0.8125rem;
|
|
421
422
|
color: var(--text-tertiary);
|
|
@@ -435,7 +436,52 @@
|
|
|
435
436
|
border: 1px solid var(--border-light);
|
|
436
437
|
}
|
|
437
438
|
|
|
438
|
-
.ultra-settings-config-file .ultra-settings-warn { color:
|
|
439
|
+
.ultra-settings-config-file .ultra-settings-warn { color: var(--warn-color); font-size: 0.8125rem; }
|
|
440
|
+
|
|
441
|
+
.ultra-settings-yaml-toggle {
|
|
442
|
+
display: inline-flex;
|
|
443
|
+
align-items: center;
|
|
444
|
+
gap: 6px;
|
|
445
|
+
font-family: var(--font-ui);
|
|
446
|
+
font-size: 0.8125rem;
|
|
447
|
+
color: var(--text-tertiary);
|
|
448
|
+
background: none;
|
|
449
|
+
border: none;
|
|
450
|
+
padding: 0;
|
|
451
|
+
cursor: pointer;
|
|
452
|
+
white-space: nowrap;
|
|
453
|
+
transition: color 0.2s ease;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.ultra-settings-yaml-toggle:hover { color: var(--text-primary); }
|
|
457
|
+
|
|
458
|
+
/* Switch showing whether the keys are visible. */
|
|
459
|
+
.ultra-settings-yaml-toggle-switch {
|
|
460
|
+
position: relative;
|
|
461
|
+
width: 26px;
|
|
462
|
+
height: 14px;
|
|
463
|
+
flex-shrink: 0;
|
|
464
|
+
border-radius: 7px;
|
|
465
|
+
background: var(--accent);
|
|
466
|
+
transition: background 0.15s ease;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.ultra-settings-yaml-toggle-switch::before {
|
|
470
|
+
content: "";
|
|
471
|
+
position: absolute;
|
|
472
|
+
top: 2px;
|
|
473
|
+
inset-inline-start: 14px;
|
|
474
|
+
width: 10px;
|
|
475
|
+
height: 10px;
|
|
476
|
+
border-radius: 50%;
|
|
477
|
+
background: var(--btn-primary-text);
|
|
478
|
+
transition: inset-inline-start 0.15s ease;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.ultra-settings-yaml-hidden .ultra-settings-yaml-toggle-switch { background: var(--border-input); }
|
|
482
|
+
.ultra-settings-yaml-hidden .ultra-settings-yaml-toggle-switch::before { inset-inline-start: 2px; }
|
|
483
|
+
|
|
484
|
+
.ultra-settings-yaml-hidden .ultra-settings-source-row[data-source="yaml"] { display: none; }
|
|
439
485
|
|
|
440
486
|
/* ══════════════════════════════════════════
|
|
441
487
|
FIELD CARDS
|
|
@@ -535,6 +581,10 @@
|
|
|
535
581
|
margin-inline-start: auto;
|
|
536
582
|
flex-shrink: 0;
|
|
537
583
|
max-width: 50%;
|
|
584
|
+
display: flex;
|
|
585
|
+
align-items: center;
|
|
586
|
+
gap: 6px;
|
|
587
|
+
min-width: 0;
|
|
538
588
|
}
|
|
539
589
|
|
|
540
590
|
.ultra-settings-field-value {
|
|
@@ -547,6 +597,7 @@
|
|
|
547
597
|
border-radius: 6px;
|
|
548
598
|
border: 1px solid var(--border-light);
|
|
549
599
|
max-width: 320px;
|
|
600
|
+
min-width: 0;
|
|
550
601
|
overflow: hidden;
|
|
551
602
|
text-overflow: ellipsis;
|
|
552
603
|
white-space: nowrap;
|
|
@@ -565,6 +616,52 @@
|
|
|
565
616
|
font-style: italic;
|
|
566
617
|
}
|
|
567
618
|
|
|
619
|
+
/* Copy value button */
|
|
620
|
+
.ultra-settings-copy-btn {
|
|
621
|
+
flex-shrink: 0;
|
|
622
|
+
display: inline-flex;
|
|
623
|
+
align-items: center;
|
|
624
|
+
justify-content: center;
|
|
625
|
+
width: 26px;
|
|
626
|
+
height: 26px;
|
|
627
|
+
padding: 0;
|
|
628
|
+
background: transparent;
|
|
629
|
+
border: 1px solid transparent;
|
|
630
|
+
border-radius: 6px;
|
|
631
|
+
color: var(--text-tertiary);
|
|
632
|
+
cursor: pointer;
|
|
633
|
+
transition: color 0.12s ease, background 0.12s ease, border-color 0.12s ease;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.ultra-settings-copy-btn:hover:not(:disabled) {
|
|
637
|
+
color: var(--accent);
|
|
638
|
+
background: var(--accent-light);
|
|
639
|
+
border-color: var(--accent);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.ultra-settings-copy-btn:disabled {
|
|
643
|
+
opacity: 0.4;
|
|
644
|
+
cursor: not-allowed;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.ultra-settings-copy-btn .ultra-settings-copy-check { display: none; }
|
|
648
|
+
|
|
649
|
+
.ultra-settings-copy-btn.copied {
|
|
650
|
+
color: var(--active-color);
|
|
651
|
+
border-color: transparent;
|
|
652
|
+
background: var(--active-bg);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.ultra-settings-copy-btn.copied .ultra-settings-copy-icon { display: none; }
|
|
656
|
+
.ultra-settings-copy-btn.copied .ultra-settings-copy-check { display: inline; }
|
|
657
|
+
|
|
658
|
+
/* Stands in for the copy button on rows that have no value to copy. */
|
|
659
|
+
.ultra-settings-copy-placeholder {
|
|
660
|
+
flex-shrink: 0;
|
|
661
|
+
width: 26px;
|
|
662
|
+
height: 26px;
|
|
663
|
+
}
|
|
664
|
+
|
|
568
665
|
/* Description */
|
|
569
666
|
.ultra-settings-field-description {
|
|
570
667
|
padding: 0 18px 14px 18px;
|
|
@@ -653,14 +750,13 @@
|
|
|
653
750
|
color: var(--accent);
|
|
654
751
|
text-decoration: none;
|
|
655
752
|
margin-inline-start: 0.5rem;
|
|
656
|
-
|
|
657
|
-
transition: opacity 0.2s ease;
|
|
753
|
+
transition: color 0.2s ease;
|
|
658
754
|
display: inline-flex;
|
|
659
755
|
align-items: center;
|
|
660
756
|
}
|
|
661
757
|
|
|
662
758
|
.ultra-settings-edit-link:hover {
|
|
663
|
-
|
|
759
|
+
color: var(--text-primary);
|
|
664
760
|
}
|
|
665
761
|
|
|
666
762
|
.ultra-settings-edit-link svg { width: 13px; height: 13px; }
|
|
@@ -714,7 +810,7 @@
|
|
|
714
810
|
.ultra-settings-dp-close {
|
|
715
811
|
width: 32px; height: 32px;
|
|
716
812
|
border-radius: 7px;
|
|
717
|
-
border: 1px solid var(--border-
|
|
813
|
+
border: 1px solid var(--border-input);
|
|
718
814
|
background: transparent;
|
|
719
815
|
cursor: pointer;
|
|
720
816
|
display: flex;
|
|
@@ -800,7 +896,7 @@
|
|
|
800
896
|
.ultra-settings-dialog-close {
|
|
801
897
|
width: 32px; height: 32px;
|
|
802
898
|
border-radius: 7px;
|
|
803
|
-
border: 1px solid var(--border-
|
|
899
|
+
border: 1px solid var(--border-input);
|
|
804
900
|
background: transparent;
|
|
805
901
|
cursor: pointer;
|
|
806
902
|
display: flex;
|
|
@@ -855,7 +951,7 @@
|
|
|
855
951
|
|
|
856
952
|
.ultra-settings-ss-edit-btn:hover {
|
|
857
953
|
background: var(--accent);
|
|
858
|
-
color: #fff;
|
|
954
|
+
color: var(--btn-primary-text, #fff);
|
|
859
955
|
}
|
|
860
956
|
|
|
861
957
|
.ultra-settings-ss-panel-bg {
|
|
@@ -901,12 +997,10 @@
|
|
|
901
997
|
font-size: 0.75rem;
|
|
902
998
|
color: var(--accent);
|
|
903
999
|
text-decoration: none;
|
|
904
|
-
transition: opacity 0.15s;
|
|
905
1000
|
}
|
|
906
1001
|
|
|
907
1002
|
.ultra-settings-ss-external-link:hover {
|
|
908
1003
|
text-decoration: underline;
|
|
909
|
-
opacity: 0.85;
|
|
910
1004
|
}
|
|
911
1005
|
|
|
912
1006
|
.ultra-settings-ss-loading {
|
|
@@ -943,7 +1037,7 @@
|
|
|
943
1037
|
font-family: var(--font-mono);
|
|
944
1038
|
color: var(--text-primary);
|
|
945
1039
|
background: var(--bg-code);
|
|
946
|
-
border: 1px solid var(--border-
|
|
1040
|
+
border: 1px solid var(--border-input);
|
|
947
1041
|
border-radius: 6px;
|
|
948
1042
|
transition: border-color 0.15s;
|
|
949
1043
|
box-sizing: border-box;
|
|
@@ -964,7 +1058,7 @@ select.ultra-settings-ss-input {
|
|
|
964
1058
|
-webkit-appearance: none;
|
|
965
1059
|
appearance: none;
|
|
966
1060
|
padding-inline-end: 28px;
|
|
967
|
-
background-image:
|
|
1061
|
+
background-image: var(--select-arrow);
|
|
968
1062
|
background-repeat: no-repeat;
|
|
969
1063
|
background-position: right 10px center;
|
|
970
1064
|
}
|
|
@@ -1018,10 +1112,10 @@ input[type="number"].ultra-settings-ss-input::-webkit-outer-spin-button {
|
|
|
1018
1112
|
|
|
1019
1113
|
.ultra-settings-ss-errors {
|
|
1020
1114
|
padding: 10px 12px;
|
|
1021
|
-
background:
|
|
1022
|
-
border: 1px solid
|
|
1115
|
+
background: var(--danger-bg);
|
|
1116
|
+
border: 1px solid var(--danger-border);
|
|
1023
1117
|
border-radius: 6px;
|
|
1024
|
-
color:
|
|
1118
|
+
color: var(--danger-text);
|
|
1025
1119
|
font-size: 0.8125rem;
|
|
1026
1120
|
line-height: 1.4;
|
|
1027
1121
|
}
|
|
@@ -1044,7 +1138,7 @@ input[type="number"].ultra-settings-ss-input::-webkit-outer-spin-button {
|
|
|
1044
1138
|
font-size: 0.8125rem;
|
|
1045
1139
|
font-weight: 600;
|
|
1046
1140
|
border-radius: 6px;
|
|
1047
|
-
border: 1px solid var(--border-
|
|
1141
|
+
border: 1px solid var(--border-input);
|
|
1048
1142
|
cursor: pointer;
|
|
1049
1143
|
transition: background 0.15s, border-color 0.15s, color 0.15s;
|
|
1050
1144
|
font-family: var(--font-ui);
|
|
@@ -1096,12 +1190,10 @@ input[type="number"].ultra-settings-ss-input::-webkit-outer-spin-button {
|
|
|
1096
1190
|
color: var(--accent);
|
|
1097
1191
|
text-decoration: none;
|
|
1098
1192
|
cursor: pointer;
|
|
1099
|
-
transition: opacity 0.15s;
|
|
1100
1193
|
}
|
|
1101
1194
|
|
|
1102
1195
|
.ultra-settings-ss-history-link:hover {
|
|
1103
1196
|
text-decoration: underline;
|
|
1104
|
-
opacity: 0.85;
|
|
1105
1197
|
}
|
|
1106
1198
|
|
|
1107
1199
|
.ultra-settings-ss-history {
|
|
@@ -1153,9 +1245,9 @@ input[type="number"].ultra-settings-ss-input::-webkit-outer-spin-button {
|
|
|
1153
1245
|
}
|
|
1154
1246
|
|
|
1155
1247
|
.ultra-settings-ss-history-entry-badge.deleted {
|
|
1156
|
-
background:
|
|
1157
|
-
color:
|
|
1158
|
-
border: 1px solid
|
|
1248
|
+
background: var(--danger-bg);
|
|
1249
|
+
color: var(--danger-text);
|
|
1250
|
+
border: 1px solid var(--danger-border);
|
|
1159
1251
|
}
|
|
1160
1252
|
|
|
1161
1253
|
.ultra-settings-ss-history-entry-badge.created {
|
|
@@ -13,17 +13,19 @@
|
|
|
13
13
|
/* Borders */
|
|
14
14
|
--border-light: #e2e5ea;
|
|
15
15
|
--border-card: #dfe2e8;
|
|
16
|
+
/* Interactive control borders — 3:1 against every surface (WCAG 1.4.11) */
|
|
17
|
+
--border-input: #7e8492;
|
|
16
18
|
|
|
17
19
|
/* Text */
|
|
18
20
|
--text-primary: #111827;
|
|
19
21
|
--text-secondary: #4b5563;
|
|
20
|
-
--text-tertiary: #
|
|
22
|
+
--text-tertiary: #646b7a;
|
|
21
23
|
|
|
22
24
|
/* Accent — teal */
|
|
23
|
-
--accent: #
|
|
24
|
-
--accent-light: rgba(
|
|
25
|
-
--accent-lighter: rgba(
|
|
26
|
-
--accent-glow: rgba(
|
|
25
|
+
--accent: #0f766e;
|
|
26
|
+
--accent-light: rgba(15, 118, 110, 0.06);
|
|
27
|
+
--accent-lighter: rgba(15, 118, 110, 0.03);
|
|
28
|
+
--accent-glow: rgba(15, 118, 110, 0.12);
|
|
27
29
|
--btn-primary-text: #ffffff;
|
|
28
30
|
--btn-secondary-text: #4b5563;
|
|
29
31
|
--btn-secondary-hover-text: #111827;
|
|
@@ -32,24 +34,28 @@
|
|
|
32
34
|
--src-env: #2563eb;
|
|
33
35
|
--src-env-bg: #eff6ff;
|
|
34
36
|
--src-env-border: #bfdbfe;
|
|
35
|
-
--src-setting: #
|
|
37
|
+
--src-setting: #0f766e;
|
|
36
38
|
--src-setting-bg: #f0fdfa;
|
|
37
39
|
--src-setting-border: #99f6e4;
|
|
38
40
|
--src-yaml: #7c3aed;
|
|
39
41
|
--src-yaml-bg: #f5f3ff;
|
|
40
42
|
--src-yaml-border: #ddd6fe;
|
|
41
|
-
--src-default: #
|
|
43
|
+
--src-default: #5f6673;
|
|
42
44
|
--src-default-bg: #f3f4f6;
|
|
43
45
|
--src-default-border: #d1d5db;
|
|
44
46
|
|
|
45
47
|
/* Status */
|
|
46
|
-
--active-color: #
|
|
47
|
-
--active-bg: rgba(
|
|
48
|
+
--active-color: #047857;
|
|
49
|
+
--active-bg: rgba(4, 120, 87, 0.05);
|
|
48
50
|
--overridden-color: #6b7280;
|
|
51
|
+
--warn-color: #8a6508;
|
|
52
|
+
--danger-text: #b3261e;
|
|
53
|
+
--danger-bg: rgba(179, 38, 30, 0.10);
|
|
54
|
+
--danger-border: rgba(179, 38, 30, 0.35);
|
|
49
55
|
|
|
50
56
|
/* Badges */
|
|
51
57
|
--badge-secret-bg: #fef2f2;
|
|
52
|
-
--badge-secret-text: #
|
|
58
|
+
--badge-secret-text: #b91c1c;
|
|
53
59
|
--badge-secret-border: #fecaca;
|
|
54
60
|
--badge-static-bg: #f0f2f5;
|
|
55
61
|
--badge-static-text: #4b5563;
|
|
@@ -71,6 +77,9 @@
|
|
|
71
77
|
|
|
72
78
|
/* Source row divider */
|
|
73
79
|
--border-source-row: rgba(0,0,0,0.025);
|
|
80
|
+
|
|
81
|
+
/* Select dropdown caret (must be 3:1 against --bg-code) */
|
|
82
|
+
--select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%237e8492'/%3E%3C/svg%3E");
|
|
74
83
|
}
|
|
75
84
|
<% end %>
|
|
76
85
|
|
|
@@ -94,11 +103,13 @@
|
|
|
94
103
|
/* Borders */
|
|
95
104
|
--border-light: #2a2d3a;
|
|
96
105
|
--border-card: #252836;
|
|
106
|
+
/* Interactive control borders — 3:1 against every surface (WCAG 1.4.11) */
|
|
107
|
+
--border-input: #666c86;
|
|
97
108
|
|
|
98
109
|
/* Text */
|
|
99
110
|
--text-primary: #e4e6f0;
|
|
100
111
|
--text-secondary: #9ba1b5;
|
|
101
|
-
--text-tertiary: #
|
|
112
|
+
--text-tertiary: #8f95ad;
|
|
102
113
|
|
|
103
114
|
/* Accent — teal */
|
|
104
115
|
--accent: #14b8a6;
|
|
@@ -126,7 +137,11 @@
|
|
|
126
137
|
/* Status */
|
|
127
138
|
--active-color: #34d399;
|
|
128
139
|
--active-bg: rgba(52, 211, 153, 0.08);
|
|
129
|
-
--overridden-color: #
|
|
140
|
+
--overridden-color: #8f95ad;
|
|
141
|
+
--warn-color: #d6ba1d;
|
|
142
|
+
--danger-text: #f87171;
|
|
143
|
+
--danger-bg: rgba(248, 113, 113, 0.12);
|
|
144
|
+
--danger-border: rgba(248, 113, 113, 0.30);
|
|
130
145
|
|
|
131
146
|
/* Badges */
|
|
132
147
|
--badge-secret-bg: rgba(220, 38, 38, 0.12);
|
|
@@ -152,6 +167,9 @@
|
|
|
152
167
|
|
|
153
168
|
/* Source row divider */
|
|
154
169
|
--border-source-row: rgba(255,255,255,0.04);
|
|
170
|
+
|
|
171
|
+
/* Select dropdown caret (must be 3:1 against --bg-code) */
|
|
172
|
+
--select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%238f95ad'/%3E%3C/svg%3E");
|
|
155
173
|
}
|
|
156
174
|
<% end %>
|
|
157
175
|
<% if color_scheme == :system || dark_mode_selector %>
|
data/app/configuration.html.erb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<div class="ultra-settings-block">
|
|
1
|
+
<div class="ultra-settings-block<%= " ultra-settings-yaml-hidden" if hide_yaml_keys?(configuration) %>">
|
|
2
2
|
<%= render_partial "config_description", configuration: configuration %>
|
|
3
3
|
|
|
4
4
|
<div class="ultra-settings-fields">
|
|
@@ -61,6 +61,24 @@
|
|
|
61
61
|
tabindex="0"
|
|
62
62
|
role="button"><%= html_escape(display_value(configuration[field.name])) %></code>
|
|
63
63
|
<% end %>
|
|
64
|
+
|
|
65
|
+
<% if field.secret? %>
|
|
66
|
+
<button type="button"
|
|
67
|
+
class="ultra-settings-copy-btn"
|
|
68
|
+
disabled
|
|
69
|
+
title="<%= t('field.copy_disabled') %>"
|
|
70
|
+
aria-label="<%= t('field.copy_disabled') %>"><%= copy_icon %><%= check_icon %></button>
|
|
71
|
+
<% elsif configuration[field.name].nil? %>
|
|
72
|
+
<%# There is nothing to copy, but the placeholder keeps the values aligned with the other rows. %>
|
|
73
|
+
<span class="ultra-settings-copy-placeholder" aria-hidden="true"></span>
|
|
74
|
+
<% else %>
|
|
75
|
+
<button type="button"
|
|
76
|
+
class="ultra-settings-copy-btn"
|
|
77
|
+
onclick="<%= html_escape(copy_value_script) %>"
|
|
78
|
+
data-copy-value="<%= html_escape(copy_value(configuration[field.name])) %>"
|
|
79
|
+
title="<%= t('field.copy') %>"
|
|
80
|
+
aria-label="<%= t('field.copy') %>"><%= copy_icon %><%= check_icon %></button>
|
|
81
|
+
<% end %>
|
|
64
82
|
</div>
|
|
65
83
|
</div>
|
|
66
84
|
|
data/app/layout.css
CHANGED
|
@@ -43,7 +43,7 @@ body {
|
|
|
43
43
|
display: flex;
|
|
44
44
|
align-items: center;
|
|
45
45
|
justify-content: center;
|
|
46
|
-
color: var(--accent, #
|
|
46
|
+
color: var(--accent, #0f766e);
|
|
47
47
|
flex-shrink: 0;
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -66,7 +66,7 @@ body {
|
|
|
66
66
|
font-family: var(--font-ui);
|
|
67
67
|
font-size: 0.8125rem;
|
|
68
68
|
font-weight: 400;
|
|
69
|
-
color: var(--text-tertiary, #
|
|
69
|
+
color: var(--text-tertiary, #646b7a);
|
|
70
70
|
line-height: 1.3;
|
|
71
71
|
margin: 0;
|
|
72
72
|
letter-spacing: 0.01em;
|
|
@@ -95,15 +95,15 @@ body {
|
|
|
95
95
|
border: 1px solid transparent;
|
|
96
96
|
border-radius: 8px;
|
|
97
97
|
background: transparent;
|
|
98
|
-
color: var(--text-tertiary, #
|
|
98
|
+
color: var(--text-tertiary, #646b7a);
|
|
99
99
|
cursor: pointer;
|
|
100
100
|
flex-shrink: 0;
|
|
101
101
|
transition: color 0.2s, border-color 0.2s;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
.ultra-settings-theme-toggle:hover {
|
|
105
|
-
color: var(--accent, #
|
|
106
|
-
border-color: var(--accent, #
|
|
105
|
+
color: var(--accent, #0f766e);
|
|
106
|
+
border-color: var(--accent, #0f766e);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
.ultra-settings-theme-icon {
|
data/app/layout_vars.css.erb
CHANGED
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
--bg-page: #f4f5f7;
|
|
6
6
|
--text-primary: #111827;
|
|
7
7
|
--text-secondary: #4b5563;
|
|
8
|
-
--text-tertiary: #
|
|
9
|
-
--accent: #
|
|
8
|
+
--text-tertiary: #646b7a;
|
|
9
|
+
--accent: #0f766e;
|
|
10
10
|
--border-light: #e2e5ea;
|
|
11
11
|
--bg-card: #ffffff;
|
|
12
12
|
--border-card: #dfe2e8;
|
|
13
13
|
--shadow-card: 0 1px 3px rgba(0,0,0,0.04), 0 0 0 1px rgba(0,0,0,0.03);
|
|
14
14
|
--shadow-lg: 0 20px 48px rgba(0,0,0,0.12);
|
|
15
15
|
--bg-code: #f0f2f5;
|
|
16
|
-
--accent-light: rgba(
|
|
16
|
+
--accent-light: rgba(15, 118, 110, 0.06);
|
|
17
17
|
}
|
|
18
18
|
<% end %>
|
|
19
19
|
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
--bg-page: #0f1117;
|
|
32
32
|
--text-primary: #eef0f8;
|
|
33
33
|
--text-secondary: #adb3c8;
|
|
34
|
-
--text-tertiary: #
|
|
34
|
+
--text-tertiary: #8f95ad;
|
|
35
35
|
--accent: #14b8a6;
|
|
36
36
|
--border-light: #2a2d3a;
|
|
37
37
|
--bg-card: #1a1c26;
|
data/app/locales/ar.json
CHANGED
|
@@ -17,18 +17,21 @@
|
|
|
17
17
|
"field.nil": "nil",
|
|
18
18
|
"field.click_to_expand": "انقر للتوسيع",
|
|
19
19
|
"field.not_set": "غير مضبوط",
|
|
20
|
+
"field.copy": "نسخ القيمة",
|
|
21
|
+
"field.copy_disabled": "لا يمكن نسخ القيم السرية",
|
|
20
22
|
"source.env": "ENV",
|
|
21
23
|
"source.setting": "إعداد",
|
|
22
24
|
"source.yaml": "YAML",
|
|
23
25
|
"source.default": "الافتراضي",
|
|
24
|
-
"source.active": "
|
|
26
|
+
"source.active": "نشط",
|
|
25
27
|
"source.overridden": "تم تجاوزه",
|
|
26
28
|
"detail.field_detail": "تفاصيل الحقل",
|
|
27
29
|
"detail.close_panel": "أغلق اللوحة",
|
|
28
30
|
"detail.type_label": "النوع:",
|
|
29
31
|
"detail.secret_badge": "سري",
|
|
30
32
|
"detail.secret_value": "•••••••• (سري)",
|
|
31
|
-
"config.file_not_found": "
|
|
33
|
+
"config.file_not_found": "الملف غير موجود",
|
|
34
|
+
"config.show_yaml_keys": "إظهار المفاتيح",
|
|
32
35
|
"config.edit_tooltip": "حرّر ${name}",
|
|
33
36
|
"edit.title": "تحرير إعداد وقت التشغيل",
|
|
34
37
|
"edit.open_dashboard": "افتح في لوحة الإعدادات ↗",
|
data/app/locales/cs.json
CHANGED
|
@@ -17,18 +17,21 @@
|
|
|
17
17
|
"field.nil": "nil",
|
|
18
18
|
"field.click_to_expand": "Kliknutím rozbalíte",
|
|
19
19
|
"field.not_set": "nenastaveno",
|
|
20
|
+
"field.copy": "Kopírovat hodnotu",
|
|
21
|
+
"field.copy_disabled": "Tajné hodnoty nelze kopírovat",
|
|
20
22
|
"source.env": "ENV",
|
|
21
23
|
"source.setting": "NASTAVENÍ",
|
|
22
24
|
"source.yaml": "YAML",
|
|
23
25
|
"source.default": "VÝCHOZÍ",
|
|
24
|
-
"source.active": "
|
|
26
|
+
"source.active": "Aktivní",
|
|
25
27
|
"source.overridden": "Přepsáno",
|
|
26
28
|
"detail.field_detail": "Detail pole",
|
|
27
29
|
"detail.close_panel": "Zavřít panel",
|
|
28
30
|
"detail.type_label": "Typ:",
|
|
29
31
|
"detail.secret_badge": "TAJNÉ",
|
|
30
32
|
"detail.secret_value": "•••••••• (tajné)",
|
|
31
|
-
"config.file_not_found": "
|
|
33
|
+
"config.file_not_found": "soubor neexistuje",
|
|
34
|
+
"config.show_yaml_keys": "zobrazit klíče",
|
|
32
35
|
"config.edit_tooltip": "Upravit ${name}",
|
|
33
36
|
"edit.title": "Upravit runtime nastavení",
|
|
34
37
|
"edit.open_dashboard": "Otevřít v panelu nastavení ↗",
|