mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
feat(ui): render the Display panel's detection controls from the store
This commit is contained in:
1 parent
343bdebe98
commit
c99c0e8000
12 files changed
+273
-299
No files matched your search
@@ -94,7 +94,7 @@ They contain no application state.
|
||||
renders `LayerRows` synchronously; keyed chips and list items keep focus
|
||||
through the refresh their own click triggers, and clicks re-read the live
|
||||
descriptor before acting), Display (in progress: `DisplayPanel` renders
|
||||
bloom and sharpen from the `gev-display` store, which VisualSettings
|
||||
bloom, sharpen and detection from the `gev-display` store, which VisualSettings
|
||||
publishes to; the other groups follow one owner at a time), Context and
|
||||
Radio, the command dock,
|
||||
the phone shell, then Cockpit. Layers that write into the right rail
|
||||
|
||||
@@ -524,9 +524,9 @@ test('every explicit visual UI gesture claims restore authority before it mutate
|
||||
['setScopeFeather:', 'setSharpenIntensity:', 'setScopeMaskFeather(', 'setScopeFeather control'],
|
||||
['setSharpenIntensity:', 'setHudLayout:', 'this._setSharpenIntensity(', 'setSharpenIntensity control'],
|
||||
['setHudLayout:', 'toggleCleanView:', 'this._setHudVariant(', 'setHudLayout control'],
|
||||
['setDensity:', 'setAllocation:', 'this._applyDetectionDensityFromUi()', 'setDensity control'],
|
||||
['setDensity:', 'setAllocation:', 'this._setDetectionDensity(', 'setDensity control'],
|
||||
['setAllocation:', 'setFade:', 'this._setDetectionAllocation(', 'setAllocation control'],
|
||||
['setFade:', 'toggleCelestial:', 'this._applyDetectionFadeFromUi()', 'setFade control'],
|
||||
['setFade:', 'toggleCelestial:', 'this._setDetectionFade(', 'setFade control'],
|
||||
];
|
||||
for (const [start, end, mutation, label] of gestureRoutes) {
|
||||
const startIndex = initUi.indexOf(start);
|
||||
|
||||
@@ -209,17 +209,6 @@ export class StyleManager extends ShellFacade {
|
||||
elements: {
|
||||
_celestialBtn: this._celestialBtn,
|
||||
_cockpitDisplayToggleBtn: this._cockpitDisplayToggleBtn,
|
||||
_detectionAllocationRow: this._detectionAllocationRow,
|
||||
_detectionBtn: this._detectionBtn,
|
||||
_detectionDensitySlider: this._detectionDensitySlider,
|
||||
_detectionDensityValue: this._detectionDensityValue,
|
||||
_detectionFadeRow: this._detectionFadeRow,
|
||||
_detectionFadeSlider: this._detectionFadeSlider,
|
||||
_detectionFadeValue: this._detectionFadeValue,
|
||||
_detectionOpacityRow: this._detectionOpacityRow,
|
||||
_detectionOpacitySlider: this._detectionOpacitySlider,
|
||||
_detectionOpacityValue: this._detectionOpacityValue,
|
||||
_detectionSliderRow: this._detectionSliderRow,
|
||||
_hudBtn: this._hudBtn,
|
||||
_cyberSonarBtn: this._cyberSonarBtn,
|
||||
_cyberSonarRings: this._cyberSonarRings,
|
||||
@@ -507,7 +496,7 @@ export class StyleManager extends ShellFacade {
|
||||
);
|
||||
initTrackedReadout(viewer);
|
||||
setDetectionStyle(this.activeStyle);
|
||||
this._applyDetectionDensityFromUi();
|
||||
this._setDetectionDensity();
|
||||
|
||||
this._initStages();
|
||||
this._initBloomSharpen();
|
||||
@@ -541,10 +530,10 @@ export class StyleManager extends ShellFacade {
|
||||
_detectionBtn: this._detectionBtn,
|
||||
_detectionFadeSlider: this._detectionFadeSlider,
|
||||
_detectionOpacitySlider: this._detectionOpacitySlider,
|
||||
_detectionAllocationBtns: this._detectionAllocationBtns,
|
||||
_celestialBtn: this._celestialBtn,
|
||||
_models3dBtn: this._models3dBtn,
|
||||
_scopeFeatherValue: this._scopeFeatherValue,
|
||||
_detectionDensityValue: this._detectionDensityValue,
|
||||
},
|
||||
operations: {
|
||||
setStyle: (...args) => this.setStyle(...args),
|
||||
@@ -560,12 +549,10 @@ export class StyleManager extends ShellFacade {
|
||||
_setHudVariant: (...args) => this._setHudVariant(...args),
|
||||
_setCyberSonarEnabled: (...args) => this._setCyberSonarEnabled(...args),
|
||||
_setCyberSonarSetting: (...args) => this._setCyberSonarSetting(...args),
|
||||
_applyDetectionDensityFromUi: (...args) =>
|
||||
this._applyDetectionDensityFromUi(...args),
|
||||
_setDetectionDensity: (...args) => this._setDetectionDensity(...args),
|
||||
_setDetectionAllocation: (...args) =>
|
||||
this._setDetectionAllocation(...args),
|
||||
_applyDetectionFadeFromUi: (...args) =>
|
||||
this._applyDetectionFadeFromUi(...args),
|
||||
_setDetectionFade: (...args) => this._setDetectionFade(...args),
|
||||
setCelestialRingEnabled: (...args) =>
|
||||
this.setCelestialRingEnabled(...args),
|
||||
_setModels3dEnabled: (...args) => this._setModels3dEnabled(...args),
|
||||
@@ -581,7 +568,6 @@ export class StyleManager extends ShellFacade {
|
||||
celestialRingEnabled: this.celestialRingEnabled,
|
||||
_models3dEnabled: this._models3dEnabled,
|
||||
_models3dModeBtns: this._models3dModeBtns,
|
||||
_detectionAllocationBtns: this._detectionAllocationBtns,
|
||||
}),
|
||||
claimDetection: () => {
|
||||
this._visualSettings._detectionUserOverridden = true;
|
||||
|
||||
@@ -93,7 +93,7 @@ function styleOwner(t, initialVariant = 'cyber') {
|
||||
'_syncCockpitInheritedStyle',
|
||||
'_syncCyberSonarControl',
|
||||
'_scheduleAdaptivePanelLayout',
|
||||
'_applyDetectionFadeFromUi',
|
||||
'_setDetectionFade',
|
||||
// Intensities: the fixture has no post-process stages to drive.
|
||||
'_setBloomIntensity',
|
||||
'_setSharpenIntensity',
|
||||
|
||||
+10
-12
@@ -2,7 +2,6 @@ import { clampScopeFeatherPct } from '../scopeMaskPolicy.js';
|
||||
import { createFrameRateMonitor } from './frameRateMonitor.js';
|
||||
import { bindApplicationShortcuts } from './visualInput.js';
|
||||
import { bindDisplayControls, syncScopeControls } from './displayControls.js';
|
||||
import { canonicalizeDensity } from '../data/detectionPolicy.js';
|
||||
|
||||
/** Own keyboard/display event subscriptions; settings remain with their state owners. */
|
||||
export class DisplayBindings {
|
||||
@@ -45,9 +44,6 @@ export class DisplayBindings {
|
||||
get _models3dModeBtns() {
|
||||
return this.readState()._models3dModeBtns;
|
||||
}
|
||||
get _detectionAllocationBtns() {
|
||||
return this.readState()._detectionAllocationBtns;
|
||||
}
|
||||
_initUI() {
|
||||
const {
|
||||
cycleDetectionMode,
|
||||
@@ -121,7 +117,8 @@ export class DisplayBindings {
|
||||
densitySlider: this._detectionDensitySlider,
|
||||
detectionButton: this._detectionBtn,
|
||||
allocationButtons: this._detectionAllocationBtns,
|
||||
fadeSliders: [this._detectionFadeSlider, this._detectionOpacitySlider],
|
||||
fadeSlider: this._detectionFadeSlider,
|
||||
outsideOpacitySlider: this._detectionOpacitySlider,
|
||||
celestialButton: this._celestialBtn,
|
||||
modelsButton: this._models3dBtn,
|
||||
modelModeButtons: this._models3dBtn ? this._models3dModeBtns : [],
|
||||
@@ -167,11 +164,7 @@ export class DisplayBindings {
|
||||
setDensity: (value) => {
|
||||
this.shareLinkManager?.claimRestoreLane?.('visual');
|
||||
this.claimDetection();
|
||||
const pct = canonicalizeDensity(value);
|
||||
this._detectionDensitySlider.value = String(pct);
|
||||
if (this._detectionDensityValue)
|
||||
this._detectionDensityValue.textContent = `${pct}%`;
|
||||
this._applyDetectionDensityFromUi();
|
||||
this._setDetectionDensity(value);
|
||||
this._syncShareState();
|
||||
},
|
||||
setAllocation: (value) => {
|
||||
@@ -179,9 +172,14 @@ export class DisplayBindings {
|
||||
this.claimDetection();
|
||||
this._setDetectionAllocation(value);
|
||||
},
|
||||
setFade: () => {
|
||||
setFade: (fadePct) => {
|
||||
this.shareLinkManager?.claimRestoreLane?.('visual');
|
||||
this._applyDetectionFadeFromUi();
|
||||
this._setDetectionFade({ fadePct });
|
||||
this._syncShareState();
|
||||
},
|
||||
setOutsideOpacity: (outsideOpacityPct) => {
|
||||
this.shareLinkManager?.claimRestoreLane?.('visual');
|
||||
this._setDetectionFade({ outsideOpacityPct });
|
||||
this._syncShareState();
|
||||
},
|
||||
toggleCelestial: () => {
|
||||
|
||||
@@ -65,7 +65,8 @@ export function bindDisplayControls({ elements, actions }) {
|
||||
});
|
||||
removers.push(() => control.destroy());
|
||||
}
|
||||
for (const el of elements.fadeSliders || []) listen(el, 'input', 'setFade');
|
||||
listen(elements.fadeSlider, 'input', 'setFade', integer);
|
||||
listen(elements.outsideOpacitySlider, 'input', 'setOutsideOpacity', integer);
|
||||
return {
|
||||
destroy() {
|
||||
for (const remove of removers.splice(0)) remove();
|
||||
|
||||
@@ -145,11 +145,11 @@ test('optional controls are absent safely and subscriptions stay instance-owned'
|
||||
const a = element();
|
||||
const b = element();
|
||||
const one = bindDisplayControls({
|
||||
elements: { fadeSliders: [null, a] },
|
||||
actions: { setFade: () => calls.push('a') },
|
||||
elements: { fadeSlider: null, outsideOpacitySlider: a },
|
||||
actions: { setOutsideOpacity: () => calls.push('a') },
|
||||
});
|
||||
const two = bindDisplayControls({
|
||||
elements: { fadeSliders: [b] },
|
||||
elements: { fadeSlider: b },
|
||||
actions: { setFade: () => calls.push('b') },
|
||||
});
|
||||
one.destroy();
|
||||
|
||||
@@ -121,3 +121,87 @@ test('a publish never rewrites a slider the user is dragging', async (t) => {
|
||||
assert.equal(writes, 0);
|
||||
assert.equal(slider.value, '30');
|
||||
});
|
||||
|
||||
/** VisualSettings with a stand-in detection engine. */
|
||||
function detectionSettings(store) {
|
||||
const { owner } = settings(store);
|
||||
const engine = {
|
||||
mode: 'OFF',
|
||||
tuning: { densityPct: 50, allocationStrategy: 'ELASTIC' },
|
||||
fade: { fadeRatio: 0.07, outsideOpacity: 0.01 },
|
||||
};
|
||||
Object.assign(owner, {
|
||||
_detectionDensityPct: 50,
|
||||
_detectionFadePct: 7,
|
||||
_detectionOutsidePct: 1,
|
||||
_detectionModeLabel: null,
|
||||
_detectionAllocationShown: null,
|
||||
viewer: { scene: { requestRender() {} } },
|
||||
});
|
||||
owner.services = {
|
||||
...owner.services,
|
||||
getDetectionMode: () => engine.mode,
|
||||
setDetectionModeByLabel: (label) => {
|
||||
engine.mode = label;
|
||||
},
|
||||
getDetectionTuning: () => ({ ...engine.tuning }),
|
||||
setDetectionTuning: (next) => Object.assign(engine.tuning, next),
|
||||
getKeyholeFadeTuning: () => ({ ...engine.fade }),
|
||||
setKeyholeFadeTuning: (next) => Object.assign(engine.fade, next),
|
||||
};
|
||||
return { owner, engine };
|
||||
}
|
||||
|
||||
test('detection renders its mode, rows, sliders and allocation', async (t) => {
|
||||
const { store, $ } = fixture(t);
|
||||
const { owner, engine } = detectionSettings(store);
|
||||
const button = $('detection-toggle');
|
||||
assert.equal(button.querySelector('.pp-label').textContent, 'DETECT');
|
||||
assert.equal(button.getAttribute('aria-label'), 'Detection overlay');
|
||||
|
||||
owner._setDetectionMode('DENSE');
|
||||
owner._setDetectionDensity(80);
|
||||
await nextTick();
|
||||
assert.equal(button.querySelector('.pp-label').textContent, 'DENSE');
|
||||
assert.equal(button.getAttribute('aria-pressed'), 'true');
|
||||
assert.equal(button.getAttribute('aria-label'), 'Detection overlay: dense');
|
||||
assert.equal(button.className, 'pp-toggle-btn active panoptic');
|
||||
for (const id of [
|
||||
'detection-slider-row',
|
||||
'detection-allocation-row',
|
||||
'detection-fade-row',
|
||||
'detection-opacity-row',
|
||||
])
|
||||
assert.equal($(id).classList.contains('visible'), true, id);
|
||||
assert.equal($('detection-density-slider').value, '75', 'nearest stop');
|
||||
assert.equal($('detection-density-value').textContent, '75%');
|
||||
assert.equal(engine.tuning.densityPct, 75);
|
||||
|
||||
owner._setDetectionFade({ fadePct: 55 });
|
||||
owner._setDetectionAllocation('weighted', { persist: false });
|
||||
await nextTick();
|
||||
assert.equal($('detection-fade-slider').value, '40', 'clamped to 40');
|
||||
assert.equal($('detection-fade-value').textContent, '40%');
|
||||
assert.equal($('detection-opacity-value').textContent, '1%', 'kept');
|
||||
assert.equal(engine.fade.fadeRatio, 0.4);
|
||||
const weighted = $('detection-allocation-weighted');
|
||||
const elastic = $('detection-allocation-elastic');
|
||||
assert.equal(weighted.classList.contains('active'), true);
|
||||
assert.equal(weighted.getAttribute('aria-checked'), 'true');
|
||||
assert.equal(elastic.classList.contains('active'), false);
|
||||
assert.equal(elastic.getAttribute('aria-checked'), 'false');
|
||||
assert.deepEqual(owner.getDetectionState(), {
|
||||
detectionMode: 'DENSE',
|
||||
densityPct: 75,
|
||||
allocationStrategy: 'WEIGHTED',
|
||||
fadePct: 40,
|
||||
outsideOpacityPct: 1,
|
||||
});
|
||||
|
||||
owner._setDetectionMode('OFF');
|
||||
await nextTick();
|
||||
assert.equal(button.querySelector('.pp-label').textContent, 'DETECT');
|
||||
assert.equal(button.getAttribute('aria-label'), 'Detection overlay: off');
|
||||
assert.equal(button.className, 'pp-toggle-btn');
|
||||
assert.equal($('detection-slider-row').classList.contains('visible'), false);
|
||||
});
|
||||
@@ -28,22 +28,17 @@ export function readShellElements(document = globalThis.document) {
|
||||
),
|
||||
_cyberSonarSector: document.getElementById('cyber-sonar-sector'),
|
||||
_cyberSonarSectorValue: document.getElementById('cyber-sonar-sector-value'),
|
||||
_detectionSliderRow: document.getElementById('detection-slider-row'),
|
||||
_detectionDensitySlider: document.getElementById(
|
||||
'detection-density-slider',
|
||||
),
|
||||
_detectionDensityValue: document.getElementById('detection-density-value'),
|
||||
_detectionAllocationRow: document.getElementById(
|
||||
'detection-allocation-row',
|
||||
),
|
||||
_detectionFadeRow: document.getElementById('detection-fade-row'),
|
||||
_detectionAllocationBtns: [
|
||||
document.getElementById('detection-allocation-elastic'),
|
||||
document.getElementById('detection-allocation-weighted'),
|
||||
].filter(Boolean),
|
||||
_detectionFadeSlider: document.getElementById('detection-fade-slider'),
|
||||
_detectionFadeValue: document.getElementById('detection-fade-value'),
|
||||
_detectionOpacityRow: document.getElementById('detection-opacity-row'),
|
||||
_detectionOpacitySlider: document.getElementById(
|
||||
'detection-opacity-slider',
|
||||
),
|
||||
_detectionOpacityValue: document.getElementById('detection-opacity-value'),
|
||||
_celestialBtn: document.getElementById('celestial-toggle'),
|
||||
_scopeBtn: document.getElementById('scope-toggle'),
|
||||
_scopeFeatherSlider: document.getElementById('scope-feather-slider'),
|
||||
|
||||
+14
-15
@@ -429,14 +429,6 @@ export class ShellFacade {
|
||||
this._visualSettings._contactsDetectionRestore = value;
|
||||
}
|
||||
|
||||
get _detectionAllocationBtns() {
|
||||
return this._visualSettings._detectionAllocationBtns;
|
||||
}
|
||||
|
||||
set _detectionAllocationBtns(value) {
|
||||
this._visualSettings._detectionAllocationBtns = value;
|
||||
}
|
||||
|
||||
get _detectionAllocationPreference() {
|
||||
return this._visualSettings._detectionAllocationPreference;
|
||||
}
|
||||
@@ -716,17 +708,24 @@ export class ShellFacade {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and canonicalizes the five-stop density control. The engine derives
|
||||
* Sparse/Balanced/Dense from the same stop.
|
||||
* Sets the five-stop density control, canonicalized; the engine derives
|
||||
* Sparse/Balanced/Dense from the same stop. Without a value it reapplies
|
||||
* the current stop.
|
||||
* @param {number} [pct]
|
||||
* @returns {void}
|
||||
*/
|
||||
_applyDetectionDensityFromUi() {
|
||||
return this._visualSettings._applyDetectionDensityFromUi(...arguments);
|
||||
_setDetectionDensity(pct) {
|
||||
return this._visualSettings._setDetectionDensity(...arguments);
|
||||
}
|
||||
|
||||
/** Apply responsive keyhole fade controls from normalized UI percentages. */
|
||||
_applyDetectionFadeFromUi() {
|
||||
return this._visualSettings._applyDetectionFadeFromUi(...arguments);
|
||||
/**
|
||||
* Sets the keyhole fade distance and outside opacity, in percent; either
|
||||
* one left out keeps its current value.
|
||||
* @param {{fadePct?: number, outsideOpacityPct?: number}} [tuning]
|
||||
* @returns {void}
|
||||
*/
|
||||
_setDetectionFade(tuning) {
|
||||
return this._visualSettings._setDetectionFade(...arguments);
|
||||
}
|
||||
|
||||
_syncDetectionUiFromEngine() {
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed } from 'vue';
|
||||
import { t } from '../../../i18n/index.js';
|
||||
import { vRangeValue } from '../../rail/rangeValue.js';
|
||||
import { useDisplayStore } from '../../stores/display.js';
|
||||
|
||||
// The Display panel. VisualSettings presents bloom and sharpen and
|
||||
// publishes them; until it does, the panel shows its initial markup. The
|
||||
// other groups keep their owners' element bindings.
|
||||
// The Display panel. VisualSettings presents bloom, sharpen and detection
|
||||
// and publishes them; until it does, the panel shows its initial markup.
|
||||
// The other groups keep their owners' element bindings.
|
||||
const { view } = storeToRefs(useDisplayStore());
|
||||
const pct = (value) => `${value}%`;
|
||||
const detection = computed(() => view.value?.detection ?? null);
|
||||
const detectButton = computed(() => detection.value?.button ?? null);
|
||||
const allocation = computed(() => detection.value?.allocation ?? 'ELASTIC');
|
||||
const allocationSegment = (value) => ({
|
||||
class: { active: allocation.value === value },
|
||||
'aria-checked': String(allocation.value === value),
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -63,31 +73,31 @@ const { view } = storeToRefs(useDisplayStore());
|
||||
</div>
|
||||
</div>
|
||||
<div class="pp-toggle-group">
|
||||
<button class="pp-toggle-btn" id="detection-toggle" title="Detection Overlay (D)" aria-label="Detection overlay" aria-pressed="false" data-i18n-title data-i18n-aria-label>
|
||||
<button class="pp-toggle-btn" :class="{ active: detectButton?.active, panoptic: detectButton?.panoptic }" id="detection-toggle" title="Detection Overlay (D)" :aria-label="detectButton?.ariaLabel ?? t('Detection overlay')" :aria-pressed="String(detectButton?.pressed ?? false)" data-i18n-title>
|
||||
<span class="pp-icon">▣</span>
|
||||
<span class="pp-label" data-i18n>DETECT</span>
|
||||
<span class="pp-label">{{ detectButton?.label ?? t('DETECT') }}</span>
|
||||
</button>
|
||||
<div class="pp-slider-row detection-slider-row" id="detection-slider-row">
|
||||
<div class="pp-slider-row detection-slider-row" :class="{ visible: detection?.rowsVisible }" id="detection-slider-row">
|
||||
<label class="pp-slider-mini-label" for="detection-density-slider" data-i18n>Density</label>
|
||||
<input type="range" class="gev-quantitative-slider pp-slider" id="detection-density-slider" min="0" max="100" step="25" value="50" aria-label="Detection label density" data-i18n-aria-label />
|
||||
<span class="pp-slider-value" id="detection-density-value">50%</span>
|
||||
<input type="range" class="gev-quantitative-slider pp-slider" id="detection-density-slider" min="0" max="100" step="25" value="50" v-range-value="detection?.densityPct ?? 50" aria-label="Detection label density" data-i18n-aria-label />
|
||||
<span class="pp-slider-value" id="detection-density-value">{{ pct(detection?.densityPct ?? 50) }}</span>
|
||||
</div>
|
||||
<div class="pp-slider-row detection-allocation-row" id="detection-allocation-row">
|
||||
<div class="pp-slider-row detection-allocation-row" :class="{ visible: detection?.rowsVisible }" id="detection-allocation-row">
|
||||
<span class="pp-slider-mini-label" data-i18n>Allocation</span>
|
||||
<div class="gev-segmented pp-mode-seg detection-allocation-seg" role="radiogroup" aria-label="Detection label allocation" data-i18n-aria-label>
|
||||
<button class="gev-segment pp-mode-btn active" id="detection-allocation-elastic" data-allocation="ELASTIC" role="radio" aria-checked="true" data-i18n>Elastic</button>
|
||||
<button class="gev-segment pp-mode-btn" id="detection-allocation-weighted" data-allocation="WEIGHTED" role="radio" aria-checked="false" data-i18n>Weighted</button>
|
||||
<button class="gev-segment pp-mode-btn" v-bind="allocationSegment('ELASTIC')" id="detection-allocation-elastic" data-allocation="ELASTIC" role="radio" data-i18n>Elastic</button>
|
||||
<button class="gev-segment pp-mode-btn" v-bind="allocationSegment('WEIGHTED')" id="detection-allocation-weighted" data-allocation="WEIGHTED" role="radio" data-i18n>Weighted</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pp-slider-row detection-fade-row" id="detection-fade-row">
|
||||
<div class="pp-slider-row detection-fade-row" :class="{ visible: detection?.rowsVisible }" id="detection-fade-row">
|
||||
<label class="pp-slider-mini-label" for="detection-fade-slider" data-i18n>Fade</label>
|
||||
<input type="range" class="gev-quantitative-slider pp-slider" id="detection-fade-slider" min="0" max="40" step="1" value="7" aria-label="Detection fade distance" title="World-overlay fade distance outside the keyhole as a percentage of its radius" data-i18n-aria-label data-i18n-title />
|
||||
<span class="pp-slider-value" id="detection-fade-value">7%</span>
|
||||
<input type="range" class="gev-quantitative-slider pp-slider" id="detection-fade-slider" min="0" max="40" step="1" value="7" v-range-value="detection?.fadePct ?? 7" aria-label="Detection fade distance" title="World-overlay fade distance outside the keyhole as a percentage of its radius" data-i18n-aria-label data-i18n-title />
|
||||
<span class="pp-slider-value" id="detection-fade-value">{{ pct(detection?.fadePct ?? 7) }}</span>
|
||||
</div>
|
||||
<div class="pp-slider-row detection-opacity-row" id="detection-opacity-row">
|
||||
<div class="pp-slider-row detection-opacity-row" :class="{ visible: detection?.rowsVisible }" id="detection-opacity-row">
|
||||
<label class="pp-slider-mini-label" for="detection-opacity-slider" data-i18n>Outside</label>
|
||||
<input type="range" class="gev-quantitative-slider pp-slider" id="detection-opacity-slider" min="0" max="100" step="1" value="1" aria-label="Detection opacity outside the keyhole" title="World-overlay label and card opacity beyond the fade distance" data-i18n-aria-label data-i18n-title />
|
||||
<span class="pp-slider-value" id="detection-opacity-value">1%</span>
|
||||
<input type="range" class="gev-quantitative-slider pp-slider" id="detection-opacity-slider" min="0" max="100" step="1" value="1" v-range-value="detection?.outsideOpacityPct ?? 1" aria-label="Detection opacity outside the keyhole" title="World-overlay label and card opacity beyond the fade distance" data-i18n-aria-label data-i18n-title />
|
||||
<span class="pp-slider-value" id="detection-opacity-value">{{ pct(detection?.outsideOpacityPct ?? 1) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- The one shared preset-parameter surface is inserted here at runtime. -->
|
||||
@@ -180,7 +190,7 @@ const { view } = storeToRefs(useDisplayStore());
|
||||
</button>
|
||||
<div class="pp-slider-row" :class="{ visible: view?.bloom.active }" id="bloom-slider-row">
|
||||
<input type="range" class="gev-quantitative-slider pp-slider" id="bloom-intensity-slider" min="0" max="200" value="0" v-range-value="view?.bloom.intensity ?? 0" aria-label="Bloom intensity" data-i18n-aria-label />
|
||||
<span class="pp-slider-value" id="bloom-intensity-value">{{ view?.bloom.text ?? '0%' }}</span>
|
||||
<span class="pp-slider-value" id="bloom-intensity-value">{{ pct(view?.bloom.intensity ?? 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pp-toggle-group">
|
||||
@@ -190,7 +200,7 @@ const { view } = storeToRefs(useDisplayStore());
|
||||
</button>
|
||||
<div class="pp-slider-row" :class="{ visible: view?.sharpen.active }" id="sharpen-slider-row">
|
||||
<input type="range" class="gev-quantitative-slider pp-slider" id="sharpen-intensity-slider" min="0" max="100" value="49" v-range-value="view?.sharpen.intensity ?? 49" aria-label="Sharpen intensity" data-i18n-aria-label />
|
||||
<span class="pp-slider-value" id="sharpen-intensity-value">{{ view?.sharpen.text ?? '49%' }}</span>
|
||||
<span class="pp-slider-value" id="sharpen-intensity-value">{{ pct(view?.sharpen.intensity ?? 49) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+119
-218
@@ -2,7 +2,6 @@ import { clampScopeFeatherPct } from '../scopeMaskPolicy.js';
|
||||
import { syncScopeControls } from './displayControls.js';
|
||||
import { STYLE_STATUS_LABELS } from './visualPresets.js';
|
||||
import { RETIRED_STYLES } from '../visualStylePolicy.js';
|
||||
import { syncSegmentedControl } from './segmentedControl.js';
|
||||
import { useDisplayStore } from './stores/display.js';
|
||||
import { applicationPinia } from './vueRuntime.js';
|
||||
import { t } from '../i18n/index.js';
|
||||
@@ -117,28 +116,22 @@ export class VisualSettings {
|
||||
this.hud.setMode(hudVisible ? 'on' : 'off');
|
||||
this._updateHudButtonState();
|
||||
}
|
||||
if (typeof detectionDensity === 'number' && this._detectionDensitySlider) {
|
||||
const pct = canonicalizeDensity(detectionDensity);
|
||||
this._detectionDensitySlider.value = String(pct);
|
||||
this._detectionDensityValue.textContent = `${pct}%`;
|
||||
this._applyDetectionDensityFromUi();
|
||||
}
|
||||
if (typeof detectionDensity === 'number')
|
||||
this._setDetectionDensity(detectionDensity);
|
||||
if (detectionAllocation) {
|
||||
this._setDetectionAllocation(detectionAllocation, {
|
||||
syncShare: false,
|
||||
persist: false,
|
||||
});
|
||||
}
|
||||
if (typeof detectionFadePct === 'number' && this._detectionFadeSlider) {
|
||||
this._detectionFadeSlider.value = String(detectionFadePct);
|
||||
}
|
||||
if (
|
||||
typeof detectionOutsideOpacityPct === 'number' &&
|
||||
this._detectionOpacitySlider
|
||||
) {
|
||||
this._detectionOpacitySlider.value = String(detectionOutsideOpacityPct);
|
||||
}
|
||||
this._applyDetectionFadeFromUi();
|
||||
this._setDetectionFade({
|
||||
fadePct:
|
||||
typeof detectionFadePct === 'number' ? detectionFadePct : undefined,
|
||||
outsideOpacityPct:
|
||||
typeof detectionOutsideOpacityPct === 'number'
|
||||
? detectionOutsideOpacityPct
|
||||
: undefined,
|
||||
});
|
||||
if (detectionMode) this._setDetectionMode(detectionMode);
|
||||
if (typeof celestialRing === 'boolean') {
|
||||
this.setCelestialRingEnabled(celestialRing, {
|
||||
@@ -212,6 +205,14 @@ export class VisualSettings {
|
||||
// The sharpen amount as a whole percentage. VisualEffects keeps the
|
||||
// fraction it applies, and drops writes once it is stopped.
|
||||
this._sharpenPct = 49;
|
||||
// Detection's density stop, keyhole fade and outside opacity, in whole
|
||||
// percentages; the mode label the button shows (null until first
|
||||
// synced) and the allocation its segments show (null until set).
|
||||
this._detectionDensityPct = 50;
|
||||
this._detectionFadePct = 7;
|
||||
this._detectionOutsidePct = 1;
|
||||
this._detectionModeLabel = null;
|
||||
this._detectionAllocationShown = null;
|
||||
this.activeStyle = 'normal';
|
||||
this._preCyberStyle = null;
|
||||
this._cyberStyleUserSelected = false;
|
||||
@@ -220,10 +221,6 @@ export class VisualSettings {
|
||||
this._cockpitVisionMode = 'optical';
|
||||
this._cockpitVisionRestore = null;
|
||||
this._contactsDetectionRestore = null;
|
||||
this._detectionAllocationBtns = [
|
||||
document.getElementById('detection-allocation-elastic'),
|
||||
document.getElementById('detection-allocation-weighted'),
|
||||
].filter(Boolean);
|
||||
let storedDetectionAllocation = 'ELASTIC';
|
||||
try {
|
||||
storedDetectionAllocation =
|
||||
@@ -475,14 +472,43 @@ export class VisualSettings {
|
||||
|
||||
/** Present the Display panel's post-processing groups. */
|
||||
_presentDisplay() {
|
||||
const bloom = this.bloomIntensityPct;
|
||||
return {
|
||||
bloom: { active: this.bloomEnabled, intensity: bloom, text: `${bloom}%` },
|
||||
sharpen: {
|
||||
active: this.sharpenEnabled,
|
||||
intensity: this._sharpenPct,
|
||||
text: `${this._sharpenPct}%`,
|
||||
},
|
||||
bloom: { active: this.bloomEnabled, intensity: this.bloomIntensityPct },
|
||||
sharpen: { active: this.sharpenEnabled, intensity: this._sharpenPct },
|
||||
detection: this._presentDetection(),
|
||||
};
|
||||
}
|
||||
|
||||
/** Detection's button, rows, sliders and allocation segments. */
|
||||
_presentDetection() {
|
||||
const mode = this._detectionModeLabel;
|
||||
const enabled = mode != null && mode !== 'OFF';
|
||||
const labels = {
|
||||
SPARSE: t('SPARSE'),
|
||||
BALANCED: t('BALANCED'),
|
||||
DENSE: t('DENSE'),
|
||||
};
|
||||
return {
|
||||
// Null until the first sync, so the initial markup stands.
|
||||
button:
|
||||
mode == null
|
||||
? null
|
||||
: {
|
||||
pressed: enabled,
|
||||
ariaLabel: enabled
|
||||
? t('Detection overlay: {mode}', {
|
||||
mode: String(mode).toLowerCase(),
|
||||
})
|
||||
: t('Detection overlay: off'),
|
||||
label: labels[mode] ?? t('DETECT'),
|
||||
active: Object.hasOwn(labels, mode),
|
||||
panoptic: mode === 'DENSE',
|
||||
},
|
||||
rowsVisible: enabled,
|
||||
densityPct: this._detectionDensityPct,
|
||||
fadePct: this._detectionFadePct,
|
||||
outsideOpacityPct: this._detectionOutsidePct,
|
||||
allocation: this._detectionAllocationShown,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -547,44 +573,31 @@ export class VisualSettings {
|
||||
this._layoutRightPanels();
|
||||
}
|
||||
|
||||
_applyDetectionDensityFromUi() {
|
||||
/** Set detection's density to its nearest stop and apply it. */
|
||||
_setDetectionDensity(pct = this._detectionDensityPct) {
|
||||
const { getDetectionMode, setDetectionTuning } = this.services;
|
||||
if (!this._detectionDensitySlider) return;
|
||||
const pct = canonicalizeDensity(this._detectionDensitySlider.value);
|
||||
this._detectionDensitySlider.value = String(pct);
|
||||
if (this._detectionDensityValue)
|
||||
this._detectionDensityValue.textContent = `${pct}%`;
|
||||
setDetectionTuning({ densityPct: pct });
|
||||
this._detectionDensityPct = canonicalizeDensity(pct);
|
||||
setDetectionTuning({ densityPct: this._detectionDensityPct });
|
||||
this._updateDetectionButton(getDetectionMode());
|
||||
}
|
||||
|
||||
_applyDetectionFadeFromUi() {
|
||||
/**
|
||||
* Set the keyhole fade distance (0-40%) and the opacity outside it
|
||||
* (0-100%), keeping either one that is not given, and apply them.
|
||||
*/
|
||||
_setDetectionFade({
|
||||
fadePct = this._detectionFadePct,
|
||||
outsideOpacityPct = this._detectionOutsidePct,
|
||||
} = {}) {
|
||||
const { setKeyholeFadeTuning } = this.services;
|
||||
const fadePct = Math.max(
|
||||
0,
|
||||
Math.min(40, Math.round(Number(this._detectionFadeSlider?.value) || 0)),
|
||||
);
|
||||
const outsideOpacityValue = this._detectionOpacitySlider?.value;
|
||||
const outsideOpacityPct = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
100,
|
||||
Math.round(
|
||||
outsideOpacityValue == null ? 3 : Number(outsideOpacityValue) || 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (this._detectionFadeSlider)
|
||||
this._detectionFadeSlider.value = String(fadePct);
|
||||
if (this._detectionFadeValue)
|
||||
this._detectionFadeValue.textContent = `${fadePct}%`;
|
||||
if (this._detectionOpacitySlider)
|
||||
this._detectionOpacitySlider.value = String(outsideOpacityPct);
|
||||
if (this._detectionOpacityValue)
|
||||
this._detectionOpacityValue.textContent = `${outsideOpacityPct}%`;
|
||||
const whole = (value, max) =>
|
||||
Math.max(0, Math.min(max, Math.round(Number(value) || 0)));
|
||||
this._detectionFadePct = whole(fadePct, 40);
|
||||
this._detectionOutsidePct = whole(outsideOpacityPct, 100);
|
||||
this._publishDisplay();
|
||||
setKeyholeFadeTuning({
|
||||
fadeRatio: fadePct / 100,
|
||||
outsideOpacity: outsideOpacityPct / 100,
|
||||
fadeRatio: this._detectionFadePct / 100,
|
||||
outsideOpacity: this._detectionOutsidePct / 100,
|
||||
});
|
||||
this.viewer.scene.requestRender?.();
|
||||
}
|
||||
@@ -598,9 +611,8 @@ export class VisualSettings {
|
||||
const normalized = normalizeAllocationStrategy(raw);
|
||||
this._detectionAllocationPreference = normalized;
|
||||
setDetectionTuning({ allocationStrategy: normalized });
|
||||
syncSegmentedControl(this._detectionAllocationBtns, normalized, {
|
||||
valueOf: (button) => button.dataset.allocation,
|
||||
});
|
||||
this._detectionAllocationShown = normalized;
|
||||
this._publishDisplay();
|
||||
if (persist) {
|
||||
try {
|
||||
localStorage.setItem(DETECTION_ALLOCATION_STORAGE_KEY, normalized);
|
||||
@@ -616,25 +628,16 @@ export class VisualSettings {
|
||||
const { getKeyholeFadeTuning, getDetectionTuning, getDetectionMode } =
|
||||
this.services;
|
||||
const tuning = getDetectionTuning();
|
||||
if (this._detectionDensitySlider)
|
||||
this._detectionDensitySlider.value = String(tuning.densityPct);
|
||||
if (this._detectionDensityValue)
|
||||
this._detectionDensityValue.textContent = `${tuning.densityPct}%`;
|
||||
this._detectionDensityPct = canonicalizeDensity(tuning.densityPct);
|
||||
this._setDetectionAllocation(tuning.allocationStrategy, {
|
||||
syncShare: false,
|
||||
persist: false,
|
||||
});
|
||||
const fadeTuning = getKeyholeFadeTuning();
|
||||
if (this._detectionFadeSlider)
|
||||
this._detectionFadeSlider.value = String(
|
||||
Math.round(fadeTuning.fadeRatio * 100),
|
||||
);
|
||||
if (this._detectionOpacitySlider) {
|
||||
this._detectionOpacitySlider.value = String(
|
||||
Math.round(fadeTuning.outsideOpacity * 100),
|
||||
);
|
||||
}
|
||||
this._applyDetectionFadeFromUi();
|
||||
this._setDetectionFade({
|
||||
fadePct: fadeTuning.fadeRatio * 100,
|
||||
outsideOpacityPct: fadeTuning.outsideOpacity * 100,
|
||||
});
|
||||
this._updateDetectionButton(getDetectionMode());
|
||||
}
|
||||
|
||||
@@ -810,13 +813,8 @@ export class VisualSettings {
|
||||
|
||||
_applyDetectionPreset(det) {
|
||||
if (!det) return;
|
||||
if (typeof det.densityPct === 'number' && this._detectionDensitySlider) {
|
||||
const pct = canonicalizeDensity(det.densityPct);
|
||||
this._detectionDensitySlider.value = String(pct);
|
||||
if (this._detectionDensityValue)
|
||||
this._detectionDensityValue.textContent = `${pct}%`;
|
||||
this._applyDetectionDensityFromUi();
|
||||
}
|
||||
if (typeof det.densityPct === 'number')
|
||||
this._setDetectionDensity(det.densityPct);
|
||||
if (det.mode) this._setDetectionMode(String(det.mode).toUpperCase());
|
||||
}
|
||||
|
||||
@@ -850,30 +848,18 @@ export class VisualSettings {
|
||||
if (defaults.detectionMode) {
|
||||
this._setDetectionMode(defaults.detectionMode);
|
||||
}
|
||||
if (
|
||||
typeof defaults.detectionDensity === 'number' &&
|
||||
this._detectionDensitySlider
|
||||
) {
|
||||
const density = canonicalizeDensity(defaults.detectionDensity);
|
||||
this._detectionDensitySlider.value = String(density);
|
||||
this._detectionDensityValue.textContent = `${density}%`;
|
||||
this._applyDetectionDensityFromUi();
|
||||
}
|
||||
if (typeof defaults.detectionDensity === 'number')
|
||||
this._setDetectionDensity(defaults.detectionDensity);
|
||||
this._setDetectionAllocation(
|
||||
this._detectionAllocationPreference ||
|
||||
defaults.detectionAllocation ||
|
||||
'ELASTIC',
|
||||
{ syncShare: false, persist: false },
|
||||
);
|
||||
if (this._detectionFadeSlider) {
|
||||
this._detectionFadeSlider.value = String(defaults.detectionFadePct ?? 7);
|
||||
}
|
||||
if (this._detectionOpacitySlider) {
|
||||
this._detectionOpacitySlider.value = String(
|
||||
defaults.detectionOutsideOpacityPct ?? 1,
|
||||
);
|
||||
}
|
||||
this._applyDetectionFadeFromUi();
|
||||
this._setDetectionFade({
|
||||
fadePct: defaults.detectionFadePct ?? 7,
|
||||
outsideOpacityPct: defaults.detectionOutsideOpacityPct ?? 1,
|
||||
});
|
||||
if (typeof defaults.celestialRing === 'boolean') {
|
||||
this.setCelestialRingEnabled(defaults.celestialRing, {
|
||||
syncShare: false,
|
||||
@@ -887,7 +873,7 @@ export class VisualSettings {
|
||||
return shareableDetectionState({
|
||||
owned: this._contactsDetectionRestore,
|
||||
liveMode: getDetectionMode(),
|
||||
liveDensityPct: parseInt(this._detectionDensitySlider?.value || '50', 10),
|
||||
liveDensityPct: this._detectionDensityPct,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -911,11 +897,8 @@ export class VisualSettings {
|
||||
detectionMode: detection.mode,
|
||||
detectionDensity: detection.densityPct,
|
||||
detectionAllocation: getDetectionTuning().allocationStrategy,
|
||||
detectionFadePct: parseInt(this._detectionFadeSlider?.value || '7', 10),
|
||||
detectionOutsideOpacityPct: parseInt(
|
||||
this._detectionOpacitySlider?.value || '1',
|
||||
10,
|
||||
),
|
||||
detectionFadePct: this._detectionFadePct,
|
||||
detectionOutsideOpacityPct: this._detectionOutsidePct,
|
||||
celestialRingEnabled: this.celestialRingEnabled,
|
||||
scopeEnabled: isScopeMaskEnabled(),
|
||||
scopeFeatherPct: Math.round(getScopeMaskFeather() * 100),
|
||||
@@ -931,18 +914,12 @@ export class VisualSettings {
|
||||
|
||||
getDetectionState() {
|
||||
const { getDetectionTuning, getDetectionMode } = this.services;
|
||||
const pct = this._detectionDensitySlider
|
||||
? parseInt(this._detectionDensitySlider.value, 10)
|
||||
: null;
|
||||
return {
|
||||
detectionMode: getDetectionMode(),
|
||||
densityPct: pct,
|
||||
densityPct: this._detectionDensityPct,
|
||||
allocationStrategy: getDetectionTuning().allocationStrategy,
|
||||
fadePct: parseInt(this._detectionFadeSlider?.value || '7', 10),
|
||||
outsideOpacityPct: parseInt(
|
||||
this._detectionOpacitySlider?.value || '0',
|
||||
10,
|
||||
),
|
||||
fadePct: this._detectionFadePct,
|
||||
outsideOpacityPct: this._detectionOutsidePct,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1047,18 +1024,11 @@ export class VisualSettings {
|
||||
if (requestedAllocation) {
|
||||
this._setDetectionAllocation(requestedAllocation, { syncShare: false });
|
||||
}
|
||||
if (fadePct != null && this._detectionFadeSlider) {
|
||||
this._detectionFadeSlider.value = String(
|
||||
Math.max(0, Math.min(40, Math.round(Number(fadePct)))),
|
||||
);
|
||||
}
|
||||
if (outsideOpacityPct != null && this._detectionOpacitySlider) {
|
||||
this._detectionOpacitySlider.value = String(
|
||||
Math.max(0, Math.min(100, Math.round(Number(outsideOpacityPct)))),
|
||||
);
|
||||
}
|
||||
if (fadePct != null || outsideOpacityPct != null)
|
||||
this._applyDetectionFadeFromUi();
|
||||
this._setDetectionFade({
|
||||
fadePct: fadePct ?? undefined,
|
||||
outsideOpacityPct: outsideOpacityPct ?? undefined,
|
||||
});
|
||||
|
||||
if (
|
||||
requestedProfile &&
|
||||
@@ -1067,10 +1037,7 @@ export class VisualSettings {
|
||||
) {
|
||||
requestedDensity = defaultDensityForProfile(requestedProfile);
|
||||
}
|
||||
if (requestedDensity != null && this._detectionDensitySlider) {
|
||||
this._detectionDensitySlider.value = String(requestedDensity);
|
||||
this._applyDetectionDensityFromUi();
|
||||
}
|
||||
if (requestedDensity != null) this._setDetectionDensity(requestedDensity);
|
||||
|
||||
if (enabled === false || requestedProfile === 'OFF') {
|
||||
setDetectionModeByLabel('OFF');
|
||||
@@ -1078,9 +1045,7 @@ export class VisualSettings {
|
||||
setDetectionModeByLabel(requestedProfile);
|
||||
} else if (enabled === true && getDetectionMode() === 'OFF') {
|
||||
setDetectionModeByLabel(
|
||||
profileForDensity(
|
||||
requestedDensity ?? this._detectionDensitySlider?.value ?? 50,
|
||||
),
|
||||
profileForDensity(requestedDensity ?? this._detectionDensityPct),
|
||||
);
|
||||
}
|
||||
this._syncDetectionUiFromEngine();
|
||||
@@ -1255,13 +1220,10 @@ export class VisualSettings {
|
||||
},
|
||||
detection: {
|
||||
mode: getDetectionMode(),
|
||||
density: parseInt(this._detectionDensitySlider?.value || '50', 10),
|
||||
density: this._detectionDensityPct,
|
||||
allocation: getDetectionTuning().allocationStrategy,
|
||||
fadePct: parseInt(this._detectionFadeSlider?.value || '7', 10),
|
||||
outsideOpacityPct: parseInt(
|
||||
this._detectionOpacitySlider?.value || '0',
|
||||
10,
|
||||
),
|
||||
fadePct: this._detectionFadePct,
|
||||
outsideOpacityPct: this._detectionOutsidePct,
|
||||
},
|
||||
scope: {
|
||||
enabled: isScopeMaskEnabled(),
|
||||
@@ -1328,36 +1290,23 @@ export class VisualSettings {
|
||||
}
|
||||
|
||||
const detectionState = state.detection || {};
|
||||
if (
|
||||
typeof detectionState.density === 'number' &&
|
||||
this._detectionDensitySlider
|
||||
) {
|
||||
const pct = canonicalizeDensity(detectionState.density);
|
||||
this._detectionDensitySlider.value = String(pct);
|
||||
if (this._detectionDensityValue)
|
||||
this._detectionDensityValue.textContent = `${pct}%`;
|
||||
this._applyDetectionDensityFromUi();
|
||||
}
|
||||
if (typeof detectionState.density === 'number')
|
||||
this._setDetectionDensity(detectionState.density);
|
||||
if (detectionState.allocation) {
|
||||
this._setDetectionAllocation(detectionState.allocation, {
|
||||
syncShare: false,
|
||||
});
|
||||
}
|
||||
if (
|
||||
typeof detectionState.fadePct === 'number' &&
|
||||
this._detectionFadeSlider
|
||||
) {
|
||||
this._detectionFadeSlider.value = String(detectionState.fadePct);
|
||||
}
|
||||
if (
|
||||
typeof detectionState.outsideOpacityPct === 'number' &&
|
||||
this._detectionOpacitySlider
|
||||
) {
|
||||
this._detectionOpacitySlider.value = String(
|
||||
detectionState.outsideOpacityPct,
|
||||
);
|
||||
}
|
||||
this._applyDetectionFadeFromUi();
|
||||
this._setDetectionFade({
|
||||
fadePct:
|
||||
typeof detectionState.fadePct === 'number'
|
||||
? detectionState.fadePct
|
||||
: undefined,
|
||||
outsideOpacityPct:
|
||||
typeof detectionState.outsideOpacityPct === 'number'
|
||||
? detectionState.outsideOpacityPct
|
||||
: undefined,
|
||||
});
|
||||
if (detectionState.mode) {
|
||||
this._setDetectionMode(detectionState.mode);
|
||||
}
|
||||
@@ -1452,15 +1401,8 @@ export class VisualSettings {
|
||||
if (preset.detectionMode) {
|
||||
this._setDetectionMode(preset.detectionMode);
|
||||
}
|
||||
if (
|
||||
typeof preset.detectionDensity === 'number' &&
|
||||
this._detectionDensitySlider
|
||||
) {
|
||||
const density = canonicalizeDensity(preset.detectionDensity);
|
||||
this._detectionDensitySlider.value = String(density);
|
||||
this._detectionDensityValue.textContent = `${density}%`;
|
||||
this._applyDetectionDensityFromUi();
|
||||
}
|
||||
if (typeof preset.detectionDensity === 'number')
|
||||
this._setDetectionDensity(preset.detectionDensity);
|
||||
if (preset.detectionAllocation) {
|
||||
this._setDetectionAllocation(preset.detectionAllocation, {
|
||||
syncShare: false,
|
||||
@@ -1653,49 +1595,8 @@ export class VisualSettings {
|
||||
}
|
||||
|
||||
_updateDetectionButton(modeLabel) {
|
||||
const btn = this._detectionBtn;
|
||||
const enabled = modeLabel !== 'OFF';
|
||||
btn.setAttribute('aria-pressed', String(enabled));
|
||||
btn.setAttribute(
|
||||
'aria-label',
|
||||
enabled
|
||||
? t('Detection overlay: {mode}', {
|
||||
mode: String(modeLabel).toLowerCase(),
|
||||
})
|
||||
: t('Detection overlay: off'),
|
||||
);
|
||||
btn.classList.remove('active', 'god', 'panoptic');
|
||||
if (modeLabel === 'SPARSE') {
|
||||
btn.querySelector('.pp-label').textContent = t('SPARSE');
|
||||
btn.classList.add('active');
|
||||
} else if (modeLabel === 'BALANCED') {
|
||||
btn.querySelector('.pp-label').textContent = t('BALANCED');
|
||||
btn.classList.add('active');
|
||||
} else if (modeLabel === 'DENSE') {
|
||||
btn.querySelector('.pp-label').textContent = t('DENSE');
|
||||
btn.classList.add('active', 'panoptic');
|
||||
} else {
|
||||
btn.querySelector('.pp-label').textContent = t('DETECT');
|
||||
}
|
||||
|
||||
if (this._detectionSliderRow) {
|
||||
this._detectionSliderRow.classList.toggle('visible', modeLabel !== 'OFF');
|
||||
}
|
||||
if (this._detectionAllocationRow) {
|
||||
this._detectionAllocationRow.classList.toggle(
|
||||
'visible',
|
||||
modeLabel !== 'OFF',
|
||||
);
|
||||
}
|
||||
if (this._detectionFadeRow) {
|
||||
this._detectionFadeRow.classList.toggle('visible', modeLabel !== 'OFF');
|
||||
}
|
||||
if (this._detectionOpacityRow) {
|
||||
this._detectionOpacityRow.classList.toggle(
|
||||
'visible',
|
||||
modeLabel !== 'OFF',
|
||||
);
|
||||
}
|
||||
this._detectionModeLabel = modeLabel;
|
||||
this._publishDisplay();
|
||||
this._layoutRightPanels();
|
||||
}
|
||||
stop() {
|
||||
|
||||
Reference in new issue
Block a user