Keep Scope opacity at half or more, and ignore a value that is not a number

A fainter mask reads as Scope off while labels still fade outside the
keyhole and the HUD gets no backplates.

Co-authored-by: Manjunath Paparaju <[email protected]>
This commit is contained in:
Sameh KhamisandManjunath Paparaju committed 2026-11-14 15:31:33 -08:00
1 parent a1e7b2a394
commit 2fd8d32a5d
7 files changed
+106 -42

No files matched your search

+1 -1
View File
@@ -253,7 +253,7 @@ test('detection-on-by-default is a default, not an operator override', () => {
// A factory default is not that. If applying the baseline set the flag, a // A factory default is not that. If applying the baseline set the flag, a
// fresh session would silently lose the style auto-enable behaviour — a // fresh session would silently lose the style auto-enable behaviour — a
// separate landed feature, taken out by an unrelated change. // separate landed feature, taken out by an unrelated change.
const applyDefaults = uiBlock(' _applyGlobalPostDefaults() {', '\n }\n'); const applyDefaults = uiBlock(' _applyGlobalPostDefaults({ keepHudVariant = false } = {}) {', '\n }\n');
assert.match(applyDefaults, /this\._setDetectionMode\(defaults\.detectionMode\)/, assert.match(applyDefaults, /this\._setDetectionMode\(defaults\.detectionMode\)/,
'the baseline still goes through the real detection path'); 'the baseline still goes through the real detection path');
assert.doesNotMatch(applyDefaults, /_detectionUserOverridden/, assert.doesNotMatch(applyDefaults, /_detectionUserOverridden/,
+10 -6
View File
@@ -316,13 +316,13 @@ test('a shared scope opacity is restored and rides in the next share state', asy
}); });
let shown = null; let shown = null;
owner.store = { publish: (view) => (shown = view.scope) }; owner.store = { publish: (view) => (shown = view.scope) };
await owner.restoreShareState({ scopeOpacityPct: 40 }); await owner.restoreShareState({ scopeOpacityPct: 60 });
assert.equal(getScopeMaskOpacity(), 0.4); assert.equal(getScopeMaskOpacity(), 0.6);
assert.equal(shown.opacityPct, 40); assert.equal(shown.opacityPct, 60);
// A state without it leaves the opacity alone. // A state without it leaves the opacity alone.
await owner.restoreShareState({ scopeFeatherPct: 80 }); await owner.restoreShareState({ scopeFeatherPct: 80 });
assert.equal(getScopeMaskOpacity(), 0.4); assert.equal(getScopeMaskOpacity(), 0.6);
assert.equal(shown.opacityPct, 40); assert.equal(shown.opacityPct, 60);
Object.assign(owner.services, { Object.assign(owner.services, {
getDetectionTuning: () => ({ allocationStrategy: 'ELASTIC' }), getDetectionTuning: () => ({ allocationStrategy: 'ELASTIC' }),
getDetectionMode: () => 'OFF', getDetectionMode: () => 'OFF',
@@ -332,7 +332,11 @@ test('a shared scope opacity is restored and rides in the next share state', asy
getScopeTerminusOverride: () => null, getScopeTerminusOverride: () => null,
}); });
owner.readCelestialRing = () => null; owner.readCelestialRing = () => null;
assert.equal(owner._readShareState().options.scopeOpacityPct, 40); assert.equal(owner._readShareState().options.scopeOpacityPct, 60);
// A link asking for less than the floor opens at the floor, and says so.
await owner.restoreShareState({ scopeOpacityPct: 20 });
assert.equal(getScopeMaskOpacity(), 0.5);
assert.equal(owner._readShareState().options.scopeOpacityPct, 50);
}); });
test('a scene shot saved with the retired Snow style plays on Normal', async (t) => { test('a scene shot saved with the retired Snow style plays on Normal', async (t) => {
+3 -1
View File
@@ -179,7 +179,9 @@ export class DisplayBindings {
resetVisual: () => { resetVisual: () => {
this.shareLinkManager?.claimRestoreLane?.('visual'); this.shareLinkManager?.claimRestoreLane?.('visual');
this.setStyle('normal', { userInitiated: true }); this.setStyle('normal', { userInitiated: true });
this._applyGlobalPostDefaults(); // The HUD layout follows the Normal style, as choosing it does: the
// Cyber HUD stays Cyber.
this._applyGlobalPostDefaults({ keepHudVariant: true });
this._setDetectionAllocation('ELASTIC'); this._setDetectionAllocation('ELASTIC');
this._setScope({ enabled: true, featherPct: 100, opacityPct: 100 }); this._setScope({ enabled: true, featherPct: 100, opacityPct: 100 });
this._setModels3dEnabled(true); this._setModels3dEnabled(true);
+63 -20
View File
@@ -108,7 +108,11 @@ test('bloom and sharpen render their state, slider and readout', async (t) => {
await nextTick(); await nextTick();
assert.equal($('bloom-toggle').classList.contains('active'), false); assert.equal($('bloom-toggle').classList.contains('active'), false);
assert.equal($('bloom-slider-row').classList.contains('visible'), false); assert.equal($('bloom-slider-row').classList.contains('visible'), false);
assert.equal($('bloom-intensity-value').textContent, '140%', 'the amount stays'); assert.equal(
$('bloom-intensity-value').textContent,
'140%',
'the amount stays',
);
}); });
test('a publish never rewrites a slider the user is dragging', async (t) => { test('a publish never rewrites a slider the user is dragging', async (t) => {
@@ -326,36 +330,50 @@ test('scope opacity renders what VisualSettings shows and sets the mask', async
assert.equal($('scope-opacity-slider').value, '100'); assert.equal($('scope-opacity-slider').value, '100');
assert.equal($('scope-opacity-value').textContent, '100%'); assert.equal($('scope-opacity-value').textContent, '100%');
owner._setScope({ opacityPct: 42.4 }); owner._setScope({ opacityPct: 62.4 });
await nextTick(); await nextTick();
assert.equal(mask.opacity, 0.42); assert.equal(mask.opacity, 0.62);
assert.equal($('scope-opacity-slider').value, '42'); assert.equal($('scope-opacity-slider').value, '62');
assert.equal($('scope-opacity-value').textContent, '42%'); assert.equal($('scope-opacity-value').textContent, '62%');
assert.equal(store.view.scope.opacityPct, 42); assert.equal(store.view.scope.opacityPct, 62);
// No fainter than the floor, where the view would read as Scope off.
owner._setScope({ opacityPct: 10 });
await nextTick();
assert.equal(mask.opacity, 0.5, 'floored');
assert.equal($('scope-opacity-value').textContent, '50%');
assert.equal($('scope-opacity-slider').min, '50');
// A value that is not a number changes nothing.
owner._setScope({ opacityPct: Number.NaN });
await nextTick();
assert.equal($('scope-opacity-value').textContent, '50%');
owner._setScope({ opacityPct: 180 }); owner._setScope({ opacityPct: 180 });
await nextTick(); await nextTick();
assert.equal(mask.opacity, 1, 'clamped'); assert.equal(mask.opacity, 1, 'clamped');
assert.equal($('scope-opacity-value').textContent, '100%'); assert.equal($('scope-opacity-value').textContent, '100%');
owner._setScope({ opacityPct: 30 }, { apply: false }); owner._setScope({ opacityPct: 70 }, { apply: false });
await nextTick(); await nextTick();
assert.equal(mask.opacity, 1, 'only shown'); assert.equal(mask.opacity, 1, 'only shown');
assert.equal($('scope-opacity-value').textContent, '30%'); assert.equal($('scope-opacity-value').textContent, '70%');
owner._setScope({ enabled: false }); owner._setScope({ enabled: false });
await nextTick(); await nextTick();
assert.equal($('scope-opacity-slider').disabled, true, 'off with the scope'); assert.equal($('scope-opacity-slider').disabled, true, 'off with the scope');
assert.equal(store.view.scope.opacityPct, 30, 'kept while off'); assert.equal(store.view.scope.opacityPct, 70, 'kept while off');
}); });
test('the Visual panel groups its controls and words its on/off pills', (t) => { test('the Visual panel groups its controls and words its on/off pills', (t) => {
const { $ } = fixture(t); const { $ } = fixture(t);
const panel = $('pp-toggles'); const panel = $('pp-toggles');
assert.deepEqual( assert.deepEqual(
[...panel.querySelectorAll('.pp-section-heading, .visual-presets__heading')].map( [
(el) => el.textContent.trim(), ...panel.querySelectorAll(
), '.pp-section-heading, .visual-presets__heading',
),
].map((el) => el.textContent.trim()),
['Look', 'MAP SOURCE 3D', 'Overlay', 'Effects'], ['Look', 'MAP SOURCE 3D', 'Overlay', 'Effects'],
); );
assert.equal(panel.querySelector('.pp-header-label').textContent, 'VISUAL'); assert.equal(panel.querySelector('.pp-header-label').textContent, 'VISUAL');
@@ -384,7 +402,10 @@ test('the 3D aircraft group renders what AircraftDisplay shows', async (t) => {
assert.equal(coreClasses(button), 'pp-toggle-btn active'); assert.equal(coreClasses(button), 'pp-toggle-btn active');
assert.equal(button.getAttribute('aria-pressed'), 'true'); assert.equal(button.getAttribute('aria-pressed'), 'true');
assert.equal(row.classList.contains('visible'), true); assert.equal(row.classList.contains('visible'), true);
assert.equal($('models3d-mode-proximity').getAttribute('aria-checked'), 'true'); assert.equal(
$('models3d-mode-proximity').getAttribute('aria-checked'),
'true',
);
// The button leaves the row to its own sync. // The button leaves the row to its own sync.
owner._setModels3dEnabled(false); owner._setModels3dEnabled(false);
@@ -400,7 +421,10 @@ test('the 3D aircraft group renders what AircraftDisplay shows', async (t) => {
await nextTick(); await nextTick();
assert.equal(all.classList.contains('active'), true); assert.equal(all.classList.contains('active'), true);
assert.equal(all.getAttribute('aria-checked'), 'true'); assert.equal(all.getAttribute('aria-checked'), 'true');
assert.equal($('models3d-mode-proximity').classList.contains('active'), false); assert.equal(
$('models3d-mode-proximity').classList.contains('active'),
false,
);
assert.deepEqual(params.at(-1), ['military', { models3dMode: 'all' }]); assert.deepEqual(params.at(-1), ['military', { models3dMode: 'all' }]);
owner._syncModels3dFromLayerState({ owner._syncModels3dFromLayerState({
@@ -429,12 +453,18 @@ test('the Draw controls render what the draw tool publishes', async (t) => {
assert.equal(segment('area').getAttribute('aria-label'), 'Area'); assert.equal(segment('area').getAttribute('aria-label'), 'Area');
assert.equal(segment('area').title, 'Area'); assert.equal(segment('area').title, 'Area');
assert.equal(segment('area').textContent, 'pentagon'); assert.equal(segment('area').textContent, 'pentagon');
const swatch = (color) => document.querySelector(`#draw-colors [data-color="${color}"]`); const swatch = (color) =>
document.querySelector(`#draw-colors [data-color="${color}"]`);
assert.equal(swatch('primary').getAttribute('aria-checked'), 'true'); assert.equal(swatch('primary').getAttribute('aria-checked'), 'true');
assert.equal(swatch('amber').getAttribute('aria-label'), 'Amber'); assert.equal(swatch('amber').getAttribute('aria-label'), 'Amber');
assert.equal($('draw-hint').textContent, ''); assert.equal($('draw-hint').textContent, '');
store.publishDraw({ active: true, shape: 'line', color: 'amber', hint: 'Click to add points.' }); store.publishDraw({
active: true,
shape: 'line',
color: 'amber',
hint: 'Click to add points.',
});
await nextTick(); await nextTick();
assert.equal(swatch('amber').getAttribute('aria-checked'), 'true'); assert.equal(swatch('amber').getAttribute('aria-checked'), 'true');
assert.equal(swatch('primary').classList.contains('active'), false); assert.equal(swatch('primary').classList.contains('active'), false);
@@ -530,7 +560,10 @@ test('the imported files render what the file import publishes and keep focus',
assert.equal(document.activeElement === button(first, 'toggle'), true); assert.equal(document.activeElement === button(first, 'toggle'), true);
assert.equal(button(first, 'toggle').getAttribute('aria-pressed'), 'true'); assert.equal(button(first, 'toggle').getAttribute('aria-pressed'), 'true');
assert.equal(button(first, 'fly').title, 'Fly to <b>a</b>.geojson'); assert.equal(button(first, 'fly').title, 'Fly to <b>a</b>.geojson');
assert.equal(button(first, 'remove').getAttribute('aria-label'), 'Remove <b>a</b>.geojson'); assert.equal(
button(first, 'remove').getAttribute('aria-label'),
'Remove <b>a</b>.geojson',
);
await press(button(first, 'remove')); await press(button(first, 'remove'));
assert.equal(rowsNow().length, 1); assert.equal(rowsNow().length, 1);
@@ -646,7 +679,11 @@ test('VisualSettings publishes a style’s parameters and writes a moved slider
assert.equal(store.parameters, null, 'the sliders go with their owner'); assert.equal(store.parameters, null, 'the sliders go with their owner');
assert.equal($('param-slider-panel').classList.contains('active'), false); assert.equal($('param-slider-panel').classList.contains('active'), false);
assert.equal(store.act('setStyleParameter', 'grainAmt', 0.2), undefined); assert.equal(store.act('setStyleParameter', 'grainAmt', 0.2), undefined);
assert.equal(owner.stages.noir.uniforms.grainAmt, 0.3, 'a stopped owner no longer answers'); assert.equal(
owner.stages.noir.uniforms.grainAmt,
0.3,
'a stopped owner no longer answers',
);
// An older owner's stop leaves a newer owner's rows. // An older owner's stop leaves a newer owner's rows.
const newer = { style: 'noir', rows: [] }; const newer = { style: 'noir', rows: [] };
owner._parameters = { style: 'noir', rows: [] }; owner._parameters = { style: 'noir', rows: [] };
@@ -658,10 +695,16 @@ test('VisualSettings publishes a style’s parameters and writes a moved slider
test('the four Cockpit groups teleport to the published slots and back to their places', async (t) => { test('the four Cockpit groups teleport to the published slots and back to their places', async (t) => {
const { store, $ } = fixture(t); const { store, $ } = fixture(t);
const groupOf = (id) => $(id).closest('.pp-toggle-group') ?? $(id); const groupOf = (id) => $(id).closest('.pp-toggle-group') ?? $(id);
const ids = ['hud-toggle', 'detection-toggle', 'param-slider-panel', 'models3d-toggle']; const ids = [
'hud-toggle',
'detection-toggle',
'param-slider-panel',
'models3d-toggle',
];
const body = $('pp-toggles').querySelector('.pp-panel-body'); const body = $('pp-toggles').querySelector('.pp-panel-body');
const order = () => const order = () =>
[...body.children].filter((el) => el.matches('.pp-toggle-group, #param-slider-panel')) [...body.children]
.filter((el) => el.matches('.pp-toggle-group, #param-slider-panel'))
.map((el) => el.id || el.querySelector('[id]')?.id); .map((el) => el.id || el.querySelector('[id]')?.id);
const home = order(); const home = order();
const popover = document.body.appendChild(document.createElement('div')); const popover = document.body.appendChild(document.createElement('div'));
+7 -6
View File
@@ -246,7 +246,7 @@ test('the Visual panel’s scope opacity, reset and presets call their actions',
], ],
}); });
await nextTick(); await nextTick();
input($('scope-opacity-slider'), 40); input($('scope-opacity-slider'), 60);
document.querySelector('#pp-toggles .pp-reset-btn').click(); document.querySelector('#pp-toggles .pp-reset-btn').click();
const presets = document.querySelector('#pp-toggles .visual-presets'); const presets = document.querySelector('#pp-toggles .visual-presets');
presets.querySelector('[data-style="noir"]').click(); presets.querySelector('[data-style="noir"]').click();
@@ -254,7 +254,7 @@ test('the Visual panel’s scope opacity, reset and presets call their actions',
chips[0].click(); chips[0].click();
chips[1].click(); chips[1].click();
assert.deepEqual(calls, [ assert.deepEqual(calls, [
['setScopeOpacity', 40], ['setScopeOpacity', 60],
['resetVisual'], ['resetVisual'],
['setStyle', 'noir'], ['setStyle', 'noir'],
]); ]);
@@ -290,7 +290,8 @@ test('DisplayBindings sets the scope opacity and resets Visual to its defaults',
elements: { _locationSearch: document.createElement('input') }, elements: { _locationSearch: document.createElement('input') },
operations: { operations: {
setStyle: (style, options) => calls.push(['style', style, options]), setStyle: (style, options) => calls.push(['style', style, options]),
_applyGlobalPostDefaults: () => calls.push(['postDefaults']), _applyGlobalPostDefaults: (options) =>
calls.push(['postDefaults', options]),
_setDetectionAllocation: (value) => calls.push(['allocation', value]), _setDetectionAllocation: (value) => calls.push(['allocation', value]),
_setScope: (scope) => calls.push(['scope', scope]), _setScope: (scope) => calls.push(['scope', scope]),
_setModels3dEnabled: (on) => calls.push(['models3d', on]), _setModels3dEnabled: (on) => calls.push(['models3d', on]),
@@ -310,10 +311,10 @@ test('DisplayBindings sets the scope opacity and resets Visual to its defaults',
bindings._initUI(); bindings._initUI();
t.after(() => bindings.destroy()); t.after(() => bindings.destroy());
calls.length = 0; calls.length = 0;
input($('scope-opacity-slider'), 35); input($('scope-opacity-slider'), 65);
assert.deepEqual(calls, [ assert.deepEqual(calls, [
['claim', 'visual'], ['claim', 'visual'],
['scope', { opacityPct: 35 }], ['scope', { opacityPct: 65 }],
['share'], ['share'],
]); ]);
calls.length = 0; calls.length = 0;
@@ -321,7 +322,7 @@ test('DisplayBindings sets the scope opacity and resets Visual to its defaults',
assert.deepEqual(calls, [ assert.deepEqual(calls, [
['claim', 'visual'], ['claim', 'visual'],
['style', 'normal', { userInitiated: true }], ['style', 'normal', { userInitiated: true }],
['postDefaults'], ['postDefaults', { keepHudVariant: true }],
['allocation', 'ELASTIC'], ['allocation', 'ELASTIC'],
['scope', { enabled: true, featherPct: 100, opacityPct: 100 }], ['scope', { enabled: true, featherPct: 100, opacityPct: 100 }],
['models3d', true], ['models3d', true],
+4 -4
View File
@@ -71,7 +71,7 @@ const allocationOptions = computed(() => [
<p class="pp-section-heading">{{ t('Overlay') }}</p> <p class="pp-section-heading">{{ t('Overlay') }}</p>
<Teleport :to="cockpitPortal?.hud ?? 'body'" :disabled="!cockpitPortal?.hud"> <Teleport :to="cockpitPortal?.hud ?? 'body'" :disabled="!cockpitPortal?.hud">
<div class="pp-toggle-group"> <div class="pp-toggle-group">
<button class="pp-toggle-btn" :class="{ active: hud?.visible }" id="hud-toggle" @click="act('toggleHud')" :title="t('Intelligence HUD (H)')"> <button class="pp-toggle-btn" :class="{ active: hud?.visible }" :aria-pressed="String(Boolean(hud?.visible))" id="hud-toggle" @click="act('toggleHud')" :title="t('Intelligence HUD (H)')">
<span class="pp-icon">&#x25CE;</span> <span class="pp-icon">&#x25CE;</span>
<span class="pp-label" translate="no">HUD</span> <span class="pp-label" translate="no">HUD</span>
</button> </button>
@@ -182,7 +182,7 @@ const allocationOptions = computed(() => [
</div> </div>
<div class="pp-slider-row visible" id="scope-opacity-row"> <div class="pp-slider-row visible" id="scope-opacity-row">
<span class="pp-slider-mini-label">{{ t('Opacity') }}</span> <span class="pp-slider-mini-label">{{ t('Opacity') }}</span>
<UiSlider class="pp-slider" id="scope-opacity-slider" @update:model-value="act('setScopeOpacity', $event)" min="0" max="100" :model-value="scopeOpacity" :disabled="!scopeOn" :aria-label="t('Scope opacity')" :title="t('How dark the scope shades the view outside the keyhole')" /> <UiSlider class="pp-slider" id="scope-opacity-slider" @update:model-value="act('setScopeOpacity', $event)" min="50" max="100" :model-value="scopeOpacity" :disabled="!scopeOn" :aria-label="t('Scope opacity')" :title="t('How dark the scope shades the view outside the keyhole')" />
<span class="pp-slider-value" id="scope-opacity-value">{{ pct(scopeOpacity) }}</span> <span class="pp-slider-value" id="scope-opacity-value">{{ pct(scopeOpacity) }}</span>
</div> </div>
</div> </div>
@@ -198,7 +198,7 @@ const allocationOptions = computed(() => [
</UiButton> </UiButton>
</div> </div>
<div class="pp-toggle-group"> <div class="pp-toggle-group">
<button class="pp-toggle-btn" :class="{ active: view?.bloom.active }" id="bloom-toggle" @click="act('toggleBloom')" :title="t('Bloom / Glow')"> <button class="pp-toggle-btn" :class="{ active: view?.bloom.active }" :aria-pressed="String(Boolean(view?.bloom.active))" id="bloom-toggle" @click="act('toggleBloom')" :title="t('Bloom / Glow')">
<span class="pp-icon">&#x2728;</span> <span class="pp-icon">&#x2728;</span>
<span class="pp-label">{{ t('Bloom') }}</span> <span class="pp-label">{{ t('Bloom') }}</span>
</button> </button>
@@ -208,7 +208,7 @@ const allocationOptions = computed(() => [
</div> </div>
</div> </div>
<div class="pp-toggle-group"> <div class="pp-toggle-group">
<button class="pp-toggle-btn" :class="{ active: view?.sharpen.active }" id="sharpen-toggle" @click="act('toggleSharpen')" :title="t('Sharpening')"> <button class="pp-toggle-btn" :class="{ active: view?.sharpen.active }" :aria-pressed="String(Boolean(view?.sharpen.active))" id="sharpen-toggle" @click="act('toggleSharpen')" :title="t('Sharpening')">
<span class="pp-icon">&#x1F50D;</span> <span class="pp-icon">&#x1F50D;</span>
<span class="pp-label">{{ t('Sharpen') }}</span> <span class="pp-label">{{ t('Sharpen') }}</span>
</button> </button>
+18 -4
View File
@@ -46,6 +46,13 @@ import { cyberVisualDefaultsForHudTransition } from '../hudLayouts.js';
const DETECTION_ALLOCATION_STORAGE_KEY = 'gev:detection-allocation:v1'; const DETECTION_ALLOCATION_STORAGE_KEY = 'gev:detection-allocation:v1';
/** Own visual preferences, detection overrides and display-control state. */ /** Own visual preferences, detection overrides and display-control state. */
/**
* The faintest the Scope mask goes. Below about half, the view reads as if
* Scope were off while labels still fade outside the keyhole and the HUD gets
* no backplates, so the slider stops here.
*/
export const SCOPE_OPACITY_MIN_PCT = 50;
export class VisualSettings { export class VisualSettings {
async restoreShareState(state) { async restoreShareState(state) {
const { setScopeTerminusOverride, clampScopeTerminusPct } = this.services; const { setScopeTerminusOverride, clampScopeTerminusPct } = this.services;
@@ -511,8 +518,11 @@ export class VisualSettings {
* given stays as it is. `apply: false` only shows the mask's state. * given stays as it is. `apply: false` only shows the mask's state.
*/ */
_setScope({ enabled, featherPct, opacityPct } = {}, { apply = true } = {}) { _setScope({ enabled, featherPct, opacityPct } = {}, { apply = true } = {}) {
if (typeof opacityPct === 'number') { if (Number.isFinite(opacityPct)) {
const pct = Math.max(0, Math.min(100, Math.round(opacityPct))); const pct = Math.max(
SCOPE_OPACITY_MIN_PCT,
Math.min(100, Math.round(opacityPct)),
);
if (apply) this.services.setScopeMaskOpacity?.(pct / 100); if (apply) this.services.setScopeMaskOpacity?.(pct / 100);
this._scopeOpacityShown = pct; this._scopeOpacityShown = pct;
} }
@@ -846,7 +856,11 @@ export class VisualSettings {
if (det.mode) this._setDetectionMode(String(det.mode).toUpperCase()); if (det.mode) this._setDetectionMode(String(det.mode).toUpperCase());
} }
_applyGlobalPostDefaults() { /**
* @param {{keepHudVariant?: boolean}} [options] `keepHudVariant` leaves the
* HUD layout to the style, as choosing a style does (Visual's reset).
*/
_applyGlobalPostDefaults({ keepHudVariant = false } = {}) {
const defaults = GLOBAL_POST_DEFAULTS; const defaults = GLOBAL_POST_DEFAULTS;
if (typeof defaults.bloom?.intensity === 'number') { if (typeof defaults.bloom?.intensity === 'number') {
this._setBloomIntensity(clampBloomIntensity(defaults.bloom.intensity), { this._setBloomIntensity(clampBloomIntensity(defaults.bloom.intensity), {
@@ -865,7 +879,7 @@ export class VisualSettings {
this._setSharpenEnabled(defaults.sharpen.enabled); this._setSharpenEnabled(defaults.sharpen.enabled);
} }
if (defaults.hudVariant) { if (defaults.hudVariant && !keepHudVariant) {
this._setHudVariant(defaults.hudVariant); this._setHudVariant(defaults.hudVariant);
} }
if (typeof defaults.hudVisible === 'boolean') { if (typeof defaults.hudVisible === 'boolean') {