Loading noctua/web/static/js/guider-panel.js +49 −36 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ import { showToast } from './ui.js'; import { get as getViewer } from './viewer/viewer-registry.js'; import { resolveValue } from './ui-core.js'; // --- Constants --- Loading @@ -26,23 +27,41 @@ const ACTUATOR_PARAMS = { // --- 1. Camera → exptime --- /** * Disables and clears the exptime input for passive cameras (scicam reads FITS * from disk — exptime is irrelevant and could mislead the operator). * Re-enables it when an active (triggering) camera is selected. * No-op for now: exptime enable/disable is driven by scicam telemetry globally. * Kept as a hook for future per-camera UI changes on camera switch. * * @param {string} panelId * @param {string} camera */ function onCameraChange(panelId, camera) { const el = document.getElementById(`guider-exptime-val-${panelId}`); if (!el) return; const passive = PASSIVE_CAMERAS.has(camera); el.disabled = passive; if (passive) el.value = ''; function onCameraChange(panelId, camera) { // eslint-disable-line no-unused-vars } // --- 2. Target → coord fields --- /** * Sets state.targetX/Y from the current target mode without touching the DOM. * Reads TX/TY field values for 'custom'; reads image size for 'center'. * * @param {string} panelId * @param {string} target * @param {object} state - mutable per-panel pick state */ function resolveTargetCoords(panelId, target, state) { if (target === 'center') { const { width, height } = state.viewer?.getImageSize() ?? {}; state.targetX = (width && height) ? Math.round(width / 2) : null; state.targetY = (width && height) ? Math.round(height / 2) : null; } else if (target === 'stick') { state.targetX = state.centerX; state.targetY = state.centerY; } else { const tx = parseFloat(document.getElementById(`guider-tx-${panelId}`)?.value); const ty = parseFloat(document.getElementById(`guider-ty-${panelId}`)?.value); state.targetX = isNaN(tx) ? null : tx; state.targetY = isNaN(ty) ? null : ty; } } /** * Enables/disables TX/TY and auto-populates them based on target mode: * center → image centre (width/2, height/2) from the registered viewer Loading Loading @@ -82,19 +101,7 @@ function onTargetChange(panelId, target) { const state = _pickState[panelId]; if (state?.annotating && state.phase === 'idle') { if (target === 'center') { const { width, height } = state.viewer?.getImageSize() ?? {}; state.targetX = (width && height) ? Math.round(width / 2) : null; state.targetY = (width && height) ? Math.round(height / 2) : null; } else if (target === 'stick') { state.targetX = state.centerX; state.targetY = state.centerY; } else { const txv = parseFloat(tx.value); const tyv = parseFloat(ty.value); state.targetX = isNaN(txv) ? null : txv; state.targetY = isNaN(tyv) ? null : tyv; } resolveTargetCoords(panelId, target, state); state.viewer?.redrawOverlay(); } } Loading Loading @@ -334,25 +341,14 @@ function initPickMode(panelId) { wrap?.classList.remove('pick-warning', 'pick-success'); const target = document.getElementById(`guider-target-${panelId}`)?.value; if (target === 'center') { const { width, height } = state.viewer?.getImageSize() ?? {}; state.targetX = (width && height) ? Math.round(width / 2) : null; state.targetY = (width && height) ? Math.round(height / 2) : null; } else if (target === 'stick') { state.targetX = state.centerX; state.targetY = state.centerY; if (target === 'stick') { // sync TX/TY fields to the center that was just locked in const txEl = document.getElementById(`guider-tx-${panelId}`); const tyEl = document.getElementById(`guider-ty-${panelId}`); if (txEl) txEl.value = state.centerX !== null ? Math.round(state.centerX) : ''; if (tyEl) tyEl.value = state.centerY !== null ? Math.round(state.centerY) : ''; } else { // custom: read whatever is in the TX/TY fields const tx = parseFloat(document.getElementById(`guider-tx-${panelId}`)?.value); const ty = parseFloat(document.getElementById(`guider-ty-${panelId}`)?.value); state.targetX = isNaN(tx) ? null : tx; state.targetY = isNaN(ty) ? null : ty; } resolveTargetCoords(panelId, target, state); state.viewer?.redrawOverlay(); for (const id of [`guider-tx-${panelId}`, `guider-ty-${panelId}`]) { document.getElementById(id)?.classList.remove('border-warning'); Loading Loading @@ -495,6 +491,15 @@ function initPanel(panelId) { targetEl?.addEventListener('change', e => onTargetChange(panelId, e.target.value)); guideBtn?.addEventListener('click', () => postGuider(guideBtn, collectParams(panelId))); if (guideBtn) { document.addEventListener('noctua-telemetry', e => { const msg = e.detail; if (msg?.name !== 'all-guider') return; const active = resolveValue(msg.data, 'guider-active')?.value; guideBtn.disabled = !!active; }); } // Keep TX/TY in sync with CX/CY when target=stick. // syncStickTarget listeners are added before redrawIfAnnotating so that // TX/TY are updated before the overlay redraw reads them. Loading Loading @@ -542,4 +547,12 @@ document.addEventListener('DOMContentLoaded', () => { const panelId = btn.id.replace(/^btn-/, '').replace(/-guide-start$/, ''); initPanel(panelId); }); document.addEventListener('noctua-telemetry', e => { const msg = e.detail; if (msg?.name !== 'all-camera') return; const exposing = resolveValue(msg.data, 'camera-snapshot-state')?.value === 2; document.querySelectorAll('[id^="guider-exptime-val-"]') .forEach(el => { el.disabled = exposing; }); }); }); Loading
noctua/web/static/js/guider-panel.js +49 −36 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ import { showToast } from './ui.js'; import { get as getViewer } from './viewer/viewer-registry.js'; import { resolveValue } from './ui-core.js'; // --- Constants --- Loading @@ -26,23 +27,41 @@ const ACTUATOR_PARAMS = { // --- 1. Camera → exptime --- /** * Disables and clears the exptime input for passive cameras (scicam reads FITS * from disk — exptime is irrelevant and could mislead the operator). * Re-enables it when an active (triggering) camera is selected. * No-op for now: exptime enable/disable is driven by scicam telemetry globally. * Kept as a hook for future per-camera UI changes on camera switch. * * @param {string} panelId * @param {string} camera */ function onCameraChange(panelId, camera) { const el = document.getElementById(`guider-exptime-val-${panelId}`); if (!el) return; const passive = PASSIVE_CAMERAS.has(camera); el.disabled = passive; if (passive) el.value = ''; function onCameraChange(panelId, camera) { // eslint-disable-line no-unused-vars } // --- 2. Target → coord fields --- /** * Sets state.targetX/Y from the current target mode without touching the DOM. * Reads TX/TY field values for 'custom'; reads image size for 'center'. * * @param {string} panelId * @param {string} target * @param {object} state - mutable per-panel pick state */ function resolveTargetCoords(panelId, target, state) { if (target === 'center') { const { width, height } = state.viewer?.getImageSize() ?? {}; state.targetX = (width && height) ? Math.round(width / 2) : null; state.targetY = (width && height) ? Math.round(height / 2) : null; } else if (target === 'stick') { state.targetX = state.centerX; state.targetY = state.centerY; } else { const tx = parseFloat(document.getElementById(`guider-tx-${panelId}`)?.value); const ty = parseFloat(document.getElementById(`guider-ty-${panelId}`)?.value); state.targetX = isNaN(tx) ? null : tx; state.targetY = isNaN(ty) ? null : ty; } } /** * Enables/disables TX/TY and auto-populates them based on target mode: * center → image centre (width/2, height/2) from the registered viewer Loading Loading @@ -82,19 +101,7 @@ function onTargetChange(panelId, target) { const state = _pickState[panelId]; if (state?.annotating && state.phase === 'idle') { if (target === 'center') { const { width, height } = state.viewer?.getImageSize() ?? {}; state.targetX = (width && height) ? Math.round(width / 2) : null; state.targetY = (width && height) ? Math.round(height / 2) : null; } else if (target === 'stick') { state.targetX = state.centerX; state.targetY = state.centerY; } else { const txv = parseFloat(tx.value); const tyv = parseFloat(ty.value); state.targetX = isNaN(txv) ? null : txv; state.targetY = isNaN(tyv) ? null : tyv; } resolveTargetCoords(panelId, target, state); state.viewer?.redrawOverlay(); } } Loading Loading @@ -334,25 +341,14 @@ function initPickMode(panelId) { wrap?.classList.remove('pick-warning', 'pick-success'); const target = document.getElementById(`guider-target-${panelId}`)?.value; if (target === 'center') { const { width, height } = state.viewer?.getImageSize() ?? {}; state.targetX = (width && height) ? Math.round(width / 2) : null; state.targetY = (width && height) ? Math.round(height / 2) : null; } else if (target === 'stick') { state.targetX = state.centerX; state.targetY = state.centerY; if (target === 'stick') { // sync TX/TY fields to the center that was just locked in const txEl = document.getElementById(`guider-tx-${panelId}`); const tyEl = document.getElementById(`guider-ty-${panelId}`); if (txEl) txEl.value = state.centerX !== null ? Math.round(state.centerX) : ''; if (tyEl) tyEl.value = state.centerY !== null ? Math.round(state.centerY) : ''; } else { // custom: read whatever is in the TX/TY fields const tx = parseFloat(document.getElementById(`guider-tx-${panelId}`)?.value); const ty = parseFloat(document.getElementById(`guider-ty-${panelId}`)?.value); state.targetX = isNaN(tx) ? null : tx; state.targetY = isNaN(ty) ? null : ty; } resolveTargetCoords(panelId, target, state); state.viewer?.redrawOverlay(); for (const id of [`guider-tx-${panelId}`, `guider-ty-${panelId}`]) { document.getElementById(id)?.classList.remove('border-warning'); Loading Loading @@ -495,6 +491,15 @@ function initPanel(panelId) { targetEl?.addEventListener('change', e => onTargetChange(panelId, e.target.value)); guideBtn?.addEventListener('click', () => postGuider(guideBtn, collectParams(panelId))); if (guideBtn) { document.addEventListener('noctua-telemetry', e => { const msg = e.detail; if (msg?.name !== 'all-guider') return; const active = resolveValue(msg.data, 'guider-active')?.value; guideBtn.disabled = !!active; }); } // Keep TX/TY in sync with CX/CY when target=stick. // syncStickTarget listeners are added before redrawIfAnnotating so that // TX/TY are updated before the overlay redraw reads them. Loading Loading @@ -542,4 +547,12 @@ document.addEventListener('DOMContentLoaded', () => { const panelId = btn.id.replace(/^btn-/, '').replace(/-guide-start$/, ''); initPanel(panelId); }); document.addEventListener('noctua-telemetry', e => { const msg = e.detail; if (msg?.name !== 'all-camera') return; const exposing = resolveValue(msg.data, 'camera-snapshot-state')?.value === 2; document.querySelectorAll('[id^="guider-exptime-val-"]') .forEach(el => { el.disabled = exposing; }); }); });