Loading noctua/web/pages/macros/control_elements.html +163 −48 Original line number Diff line number Diff line Loading @@ -199,20 +199,52 @@ Preserves aspect ratio and provides all necessary controls for FitsViewer. #} <template id="tpl-fits-viewer"> <div class="viewer-layout d-flex flex-column h-100"> <!-- Main Canvas Area with 1:1 Aspect Ratio to prevent stretching --> <div class="viewer-canvas-wrap position-relative bg-black overflow-hidden mb-2 shadow-sm" style="border-radius: 4px; width: 100%; aspect-ratio: 1 / 1;"> <div class="d-flex align-items-center gap-2"> {# Colormap selector (applies to all cameras) #} <label class="text-muted small" for="global-colormap">Colormap</label> <select id="global-colormap" class="form-select form-select-sm ctrl-colormap" style="width:110px; background:#111; border-color:#333;"> <option value="viridis" selected>Viridis</option> <option value="brbg">BrBG</option> </select> {# Refresh button — fetches current images on demand #} <button id="btn-refresh-all" class="btn btn-sm btn-outline-secondary" title="Fetch current images now"> ↺ Refresh </button> </div> </div> <div id="panel-{{ station }}" data-station="{{ station }}" data-api-base="{{ api_base }}"> {# ── Two-column layout: primary camera left, secondary stack right ── #} <div class="row g-2 align-items-start"> {# ── Left: primary camera (first in list) ── #} {% set primary = "scicam" %} <div class="col-xl-9 col-lg-8"> <div class="viewer-canvas-wrap position-relative" style="background:#000; overflow:hidden; border-radius:6px;" data-camera="{{ primary }}"> <canvas class="cv-main d-block w-100 h-100" style="image-rendering: pixelated;"></canvas> <canvas class="cv-main d-block" width="800" height="800" style="width:100%; cursor:crosshair; transform-origin:0 0;"> </canvas> <svg class="overlay-main position-absolute top-0 start-0 w-100 h-100" style="pointer-events: none; overflow: visible;" xmlns="http://www.w3.org/2000/svg"></svg> style="pointer-events:none; overflow:visible;" xmlns="http://www.w3.org/2000/svg"> </svg> <!-- Crosshair element --> <div class="cv-crosshair position-absolute" style="pointer-events: none; width: 12px; height: 12px; transform: translate(-50%,-50%); display: none;"> style="pointer-events:none; width:12px; height:12px; transform:translate(-50%,-50%); display:none;"> <svg width="12" height="12" viewBox="0 0 12 12"> <line x1="6" y1="0" x2="6" y2="12" stroke="#ffff00" stroke-width="1"/> <line x1="0" y1="6" x2="12" y2="6" stroke="#ffff00" stroke-width="1"/> Loading @@ -220,44 +252,127 @@ </div> <div class="position-absolute bottom-0 start-0 m-2"> <span class="badge bg-black bg-opacity-75 info-main" style="font-family: monospace; font-size: 0.7rem;"></span> <span class="badge bg-black bg-opacity-75 info-main" style="font-family:monospace; font-size:.65rem;"></span> </div> </div> <!-- Previews and Exploration Controls --> <div class="row g-1 mb-2"> <div class="col-4"> <canvas class="cv-explore w-100 bg-dark" width="256" height="256" style="image-rendering: pixelated; border: 1px solid #333; aspect-ratio: 1/1;"></canvas> {# Controls strip for primary camera #} <div class="viewer-controls mt-2 p-2 rounded" style="background:#0d0d0d; border:1px solid #222;"> <div class="row g-2 align-items-center"> <div class="col-auto d-flex align-items-center gap-1"> <label class="text-muted small">Min</label> <input type="number" class="ctrl-vmin form-control form-control-sm" style="width:90px; background:#111; border-color:#333;" step="any"> <label class="text-muted small">Max</label> <input type="number" class="ctrl-vmax form-control form-control-sm" style="width:90px; background:#111; border-color:#333;" step="any"> </div> <div class="col-4"> <canvas class="cv-panoramic w-100 bg-dark" width="256" height="256" style="border: 1px solid #333; aspect-ratio: 1/1;"></canvas> <div class="col-auto"> <div class="btn-group btn-group-sm"> <button class="ctrl-png btn btn-outline-secondary active" data-camera="{{ primary }}">PNG</button> <button class="ctrl-fits btn btn-outline-secondary" data-camera="{{ primary }}">FITS</button> </div> <div class="col-4 d-flex flex-column gap-1"> <!-- Added missing Explore Box input to prevent JS null pointer error --> </div> <div class="col-auto d-flex align-items-center gap-1 ms-auto"> <input type="checkbox" class="ctrl-auto form-check-input" id="auto-{{ primary }}-{{ station }}" checked> <label class="text-muted small" for="auto-{{ primary }}-{{ station }}">Auto update</label> </div> </div> </div> </div> {# ── Right: panoramic + explore + secondary cameras ── #} <div class="col-xl-3 col-lg-4 d-flex flex-column gap-2"> {# Panoramic + Explore pair (always for the primary camera) #} <div class="row g-1"> <div class="col-6" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-explore d-block" width="256" height="256" style="width:100%; image-rendering:pixelated;"></canvas> </div> <div class="col-6" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-panoramic d-block" width="256" height="256" style="width:100%; height:auto;"></canvas> </div> <div class="col-12 d-flex align-items-center gap-1 mt-1"> <div class="input-group input-group-sm"> <span class="input-group-text bg-dark border-secondary text-muted" style="font-size: 0.6rem; padding: 2px 4px;">Box</span> <input type="number" class="ctrl-explore-w form-control form-control-sm bg-black text-info border-secondary" value="70" step="10"> <span class="input-group-text" style="font-size:.65rem; padding:2px 6px; background:#1a1a1a; border-color:#333; color:#aaa;"> Box </span> <input type="number" class="ctrl-explore-w form-control form-control-sm" value="70" step="10" style="background:#111; border-color:#333; font-size:.7rem; color:#b0ffb0;"> </div> <input type="number" class="ctrl-vmin form-control form-control-sm bg-black text-success border-secondary" placeholder="vmin" style="font-size: 0.7rem;"> <input type="number" class="ctrl-vmax form-control form-control-sm bg-black text-success border-secondary" placeholder="vmax" style="font-size: 0.7rem;"> </div> </div> <!-- Action bar --> <div class="btn-group btn-group-sm w-100"> <button class="btn btn-outline-secondary ctrl-png active">PNG</button> <button class="btn btn-outline-secondary ctrl-fits">FITS</button> <select class="form-select form-select-sm bg-dark text-light border-secondary ctrl-colormap" style="max-width: 100px; font-size: 0.7rem;"> <option value="viridis">Viridis</option> <option value="brbg">BrBG</option> </select> <div class="input-group-text bg-dark border-secondary"> <input type="checkbox" class="ctrl-auto form-check-input mt-0" checked> {# Secondary cameras (everything after the first) #} {% set cam = 'teccam' %} <div data-secondary-camera="{{ cam }}"> <div class="text-muted small mb-1" style="font-size:.65rem; letter-spacing:.08em; text-transform:uppercase;"> {{ cam }} <span class="badge bg-black bg-opacity-75 info-sec-{{ cam }} ms-1" style="font-family:monospace; font-size:.6rem;"></span> </div> <div class="position-relative" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-sec-{{ cam }} d-block" width="256" height="256" style="width:100%;"></canvas> <svg class="overlay-sec-{{ cam }} position-absolute top-0 start-0 w-100 h-100" style="pointer-events:none; overflow:visible;" xmlns="http://www.w3.org/2000/svg"> </svg> </div> <div class="mt-1 p-1 rounded" style="background:#0d0d0d; border:1px solid #1a1a1a;"> <div class="d-flex flex-wrap gap-1 align-items-center"> <input type="number" class="ctrl-vmin-sec-{{ cam }} form-control form-control-sm" style="width:72px; background:#111; border-color:#333; font-size:.7rem;" step="any" placeholder="min"> <input type="number" class="ctrl-vmax-sec-{{ cam }} form-control form-control-sm" style="width:72px; background:#111; border-color:#333; font-size:.7rem;" step="any" placeholder="max"> <div class="btn-group btn-group-sm"> <button class="ctrl-png btn btn-outline-secondary" data-camera="{{ cam }}" style="font-size:.65rem;">PNG</button> <button class="ctrl-fits btn btn-outline-secondary" data-camera="{{ cam }}" style="font-size:.65rem;">FITS</button> </div> <div class="d-flex align-items-center gap-1"> <input type="checkbox" class="ctrl-auto form-check-input" id="auto-{{ cam }}-{{ station }}" checked> <label class="text-muted" style="font-size:.65rem;" for="auto-{{ cam }}-{{ station }}">Auto</label> </div> </div> </div> </div> </div> </div> </template> {% endmacro %} Loading Loading
noctua/web/pages/macros/control_elements.html +163 −48 Original line number Diff line number Diff line Loading @@ -199,20 +199,52 @@ Preserves aspect ratio and provides all necessary controls for FitsViewer. #} <template id="tpl-fits-viewer"> <div class="viewer-layout d-flex flex-column h-100"> <!-- Main Canvas Area with 1:1 Aspect Ratio to prevent stretching --> <div class="viewer-canvas-wrap position-relative bg-black overflow-hidden mb-2 shadow-sm" style="border-radius: 4px; width: 100%; aspect-ratio: 1 / 1;"> <div class="d-flex align-items-center gap-2"> {# Colormap selector (applies to all cameras) #} <label class="text-muted small" for="global-colormap">Colormap</label> <select id="global-colormap" class="form-select form-select-sm ctrl-colormap" style="width:110px; background:#111; border-color:#333;"> <option value="viridis" selected>Viridis</option> <option value="brbg">BrBG</option> </select> {# Refresh button — fetches current images on demand #} <button id="btn-refresh-all" class="btn btn-sm btn-outline-secondary" title="Fetch current images now"> ↺ Refresh </button> </div> </div> <div id="panel-{{ station }}" data-station="{{ station }}" data-api-base="{{ api_base }}"> {# ── Two-column layout: primary camera left, secondary stack right ── #} <div class="row g-2 align-items-start"> {# ── Left: primary camera (first in list) ── #} {% set primary = "scicam" %} <div class="col-xl-9 col-lg-8"> <div class="viewer-canvas-wrap position-relative" style="background:#000; overflow:hidden; border-radius:6px;" data-camera="{{ primary }}"> <canvas class="cv-main d-block w-100 h-100" style="image-rendering: pixelated;"></canvas> <canvas class="cv-main d-block" width="800" height="800" style="width:100%; cursor:crosshair; transform-origin:0 0;"> </canvas> <svg class="overlay-main position-absolute top-0 start-0 w-100 h-100" style="pointer-events: none; overflow: visible;" xmlns="http://www.w3.org/2000/svg"></svg> style="pointer-events:none; overflow:visible;" xmlns="http://www.w3.org/2000/svg"> </svg> <!-- Crosshair element --> <div class="cv-crosshair position-absolute" style="pointer-events: none; width: 12px; height: 12px; transform: translate(-50%,-50%); display: none;"> style="pointer-events:none; width:12px; height:12px; transform:translate(-50%,-50%); display:none;"> <svg width="12" height="12" viewBox="0 0 12 12"> <line x1="6" y1="0" x2="6" y2="12" stroke="#ffff00" stroke-width="1"/> <line x1="0" y1="6" x2="12" y2="6" stroke="#ffff00" stroke-width="1"/> Loading @@ -220,44 +252,127 @@ </div> <div class="position-absolute bottom-0 start-0 m-2"> <span class="badge bg-black bg-opacity-75 info-main" style="font-family: monospace; font-size: 0.7rem;"></span> <span class="badge bg-black bg-opacity-75 info-main" style="font-family:monospace; font-size:.65rem;"></span> </div> </div> <!-- Previews and Exploration Controls --> <div class="row g-1 mb-2"> <div class="col-4"> <canvas class="cv-explore w-100 bg-dark" width="256" height="256" style="image-rendering: pixelated; border: 1px solid #333; aspect-ratio: 1/1;"></canvas> {# Controls strip for primary camera #} <div class="viewer-controls mt-2 p-2 rounded" style="background:#0d0d0d; border:1px solid #222;"> <div class="row g-2 align-items-center"> <div class="col-auto d-flex align-items-center gap-1"> <label class="text-muted small">Min</label> <input type="number" class="ctrl-vmin form-control form-control-sm" style="width:90px; background:#111; border-color:#333;" step="any"> <label class="text-muted small">Max</label> <input type="number" class="ctrl-vmax form-control form-control-sm" style="width:90px; background:#111; border-color:#333;" step="any"> </div> <div class="col-4"> <canvas class="cv-panoramic w-100 bg-dark" width="256" height="256" style="border: 1px solid #333; aspect-ratio: 1/1;"></canvas> <div class="col-auto"> <div class="btn-group btn-group-sm"> <button class="ctrl-png btn btn-outline-secondary active" data-camera="{{ primary }}">PNG</button> <button class="ctrl-fits btn btn-outline-secondary" data-camera="{{ primary }}">FITS</button> </div> <div class="col-4 d-flex flex-column gap-1"> <!-- Added missing Explore Box input to prevent JS null pointer error --> </div> <div class="col-auto d-flex align-items-center gap-1 ms-auto"> <input type="checkbox" class="ctrl-auto form-check-input" id="auto-{{ primary }}-{{ station }}" checked> <label class="text-muted small" for="auto-{{ primary }}-{{ station }}">Auto update</label> </div> </div> </div> </div> {# ── Right: panoramic + explore + secondary cameras ── #} <div class="col-xl-3 col-lg-4 d-flex flex-column gap-2"> {# Panoramic + Explore pair (always for the primary camera) #} <div class="row g-1"> <div class="col-6" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-explore d-block" width="256" height="256" style="width:100%; image-rendering:pixelated;"></canvas> </div> <div class="col-6" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-panoramic d-block" width="256" height="256" style="width:100%; height:auto;"></canvas> </div> <div class="col-12 d-flex align-items-center gap-1 mt-1"> <div class="input-group input-group-sm"> <span class="input-group-text bg-dark border-secondary text-muted" style="font-size: 0.6rem; padding: 2px 4px;">Box</span> <input type="number" class="ctrl-explore-w form-control form-control-sm bg-black text-info border-secondary" value="70" step="10"> <span class="input-group-text" style="font-size:.65rem; padding:2px 6px; background:#1a1a1a; border-color:#333; color:#aaa;"> Box </span> <input type="number" class="ctrl-explore-w form-control form-control-sm" value="70" step="10" style="background:#111; border-color:#333; font-size:.7rem; color:#b0ffb0;"> </div> <input type="number" class="ctrl-vmin form-control form-control-sm bg-black text-success border-secondary" placeholder="vmin" style="font-size: 0.7rem;"> <input type="number" class="ctrl-vmax form-control form-control-sm bg-black text-success border-secondary" placeholder="vmax" style="font-size: 0.7rem;"> </div> </div> <!-- Action bar --> <div class="btn-group btn-group-sm w-100"> <button class="btn btn-outline-secondary ctrl-png active">PNG</button> <button class="btn btn-outline-secondary ctrl-fits">FITS</button> <select class="form-select form-select-sm bg-dark text-light border-secondary ctrl-colormap" style="max-width: 100px; font-size: 0.7rem;"> <option value="viridis">Viridis</option> <option value="brbg">BrBG</option> </select> <div class="input-group-text bg-dark border-secondary"> <input type="checkbox" class="ctrl-auto form-check-input mt-0" checked> {# Secondary cameras (everything after the first) #} {% set cam = 'teccam' %} <div data-secondary-camera="{{ cam }}"> <div class="text-muted small mb-1" style="font-size:.65rem; letter-spacing:.08em; text-transform:uppercase;"> {{ cam }} <span class="badge bg-black bg-opacity-75 info-sec-{{ cam }} ms-1" style="font-family:monospace; font-size:.6rem;"></span> </div> <div class="position-relative" style="background:#000; border-radius:4px; overflow:hidden;"> <canvas class="cv-sec-{{ cam }} d-block" width="256" height="256" style="width:100%;"></canvas> <svg class="overlay-sec-{{ cam }} position-absolute top-0 start-0 w-100 h-100" style="pointer-events:none; overflow:visible;" xmlns="http://www.w3.org/2000/svg"> </svg> </div> <div class="mt-1 p-1 rounded" style="background:#0d0d0d; border:1px solid #1a1a1a;"> <div class="d-flex flex-wrap gap-1 align-items-center"> <input type="number" class="ctrl-vmin-sec-{{ cam }} form-control form-control-sm" style="width:72px; background:#111; border-color:#333; font-size:.7rem;" step="any" placeholder="min"> <input type="number" class="ctrl-vmax-sec-{{ cam }} form-control form-control-sm" style="width:72px; background:#111; border-color:#333; font-size:.7rem;" step="any" placeholder="max"> <div class="btn-group btn-group-sm"> <button class="ctrl-png btn btn-outline-secondary" data-camera="{{ cam }}" style="font-size:.65rem;">PNG</button> <button class="ctrl-fits btn btn-outline-secondary" data-camera="{{ cam }}" style="font-size:.65rem;">FITS</button> </div> <div class="d-flex align-items-center gap-1"> <input type="checkbox" class="ctrl-auto form-check-input" id="auto-{{ cam }}-{{ station }}" checked> <label class="text-muted" style="font-size:.65rem;" for="auto-{{ cam }}-{{ station }}">Auto</label> </div> </div> </div> </div> </div> </div> </template> {% endmacro %} Loading