Files
gardentech f34817d5b6 Initial commit: scan_web integrated recording UI
FastAPI backend + vanilla JS frontend for LiDAR/camera startup, camera
settings, and rosbag recording, unifying the previous scan_gui/scan_gui_dual/
scan_gui_triple desktop tools into one web app.
2026-08-07 14:26:00 +09:00

155 lines
6.2 KiB
HTML

<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover">
<title>scan_web</title>
<link rel="stylesheet" href="/css/fonts.css">
<link rel="stylesheet" href="/css/tokens.css">
<link rel="stylesheet" href="/css/style.css">
<script type="importmap">
{
"imports": {
"three": "/vendor/three/three.module.min.js"
}
}
</script>
</head>
<body>
<div id="app">
<!-- Base layer, always full-screen: the live FAST-LIO point cloud. Empty
state shown until the first accumulated points arrive. -->
<section id="view-scan" class="scan-view">
<canvas id="pointcloud-canvas"></canvas>
<div id="scan-empty" class="scan-placeholder">
<p>포인트 없음 — 시동 후 녹화를 시작하면 지도가 쌓입니다.</p>
</div>
</section>
<!-- Fixed HUD — never scrolls, always on top of the scan view. -->
<div class="hud">
<div class="hud-top">
<div id="status-badges" class="status-badges"></div>
<div class="hud-top-right">
<div id="map-points" class="disk-free hud-disk-free"></div>
<div id="disk-free" class="disk-free hud-disk-free"></div>
</div>
</div>
<div class="hud-bottom">
<div class="hud-startup">
<button id="btn-lidar-start" class="btn btn-green btn-hud">시동</button>
<button id="btn-lidar-stop" class="btn btn-red btn-hud">정지</button>
<div id="lidar-state" class="state-line">● 정지</div>
</div>
<div class="hud-rec">
<button id="btn-rec-open" class="btn btn-red btn-hud">● 녹화</button>
<button id="btn-rec-stop" class="btn btn-gray btn-hud hidden">■ 정지</button>
<div id="rec-meter" class="rec-meter hidden"></div>
</div>
<div class="hud-actions">
<button id="btn-settings" class="btn btn-gray btn-hud btn-icon" aria-label="설정"></button>
<button id="btn-estop" class="btn-estop btn-estop-hud">E-STOP</button>
</div>
</div>
</div>
<!-- Settings overlay — camera settings / preview / recent bags, plus the
entry point into the log overlay. Floats over the scan view, closed by
default. -->
<div id="overlay-settings" class="overlay hidden">
<div class="overlay-backdrop" data-close="overlay-settings"></div>
<div class="overlay-sheet">
<div class="overlay-header">
<h2>설정</h2>
<button class="btn-close" data-close="overlay-settings"></button>
</div>
<div class="overlay-tabs">
<button class="pane-tab-btn active" data-pane="pane-camera-settings">카메라 설정</button>
<button class="pane-tab-btn" data-pane="pane-camera-preview">미리보기</button>
<button class="pane-tab-btn" data-pane="pane-lidar-test">라이다 테스트</button>
<button class="pane-tab-btn" data-pane="pane-recent-bags">최근 녹화</button>
<button id="btn-open-log" class="pane-tab-btn pane-tab-btn-log">로그</button>
</div>
<div class="overlay-body">
<div class="pane-tab active" id="pane-camera-settings">
<div class="tabs" id="camera-tabs"></div>
<div id="camera-tab-panels"></div>
</div>
<div class="pane-tab" id="pane-camera-preview">
<div id="camera-grid" class="camera-grid"></div>
</div>
<div class="pane-tab" id="pane-lidar-test">
<p class="hint">녹화 없이 FAST-LIO만 켜서 라이다 포인트 품질을 미리 확인합니다.
시작 후 몇 초간 흔들지 말고 가만히 두세요 — FAST-LIO가 정지 상태의 IMU로 중력 방향을 자동 정렬합니다.</p>
<div class="btn-row">
<button id="btn-fastlio-test-start" class="btn btn-blue">테스트 시작</button>
<button id="btn-fastlio-test-stop" class="btn btn-gray">테스트 정지</button>
</div>
<div id="fastlio-test-state" class="state-line">● 정지</div>
<h3>시점</h3>
<div class="btn-row">
<button id="btn-view-orbit" class="btn btn-blue">자유 시점</button>
<button id="btn-view-lidar" class="btn btn-gray">라이다 시점</button>
</div>
</div>
<div class="pane-tab" id="pane-recent-bags">
<ul id="bag-list" class="bag-list"></ul>
</div>
</div>
</div>
</div>
<!-- Recording modal — just enough to see/set the save path and filename
before confirming; floats over the scan view. -->
<div id="overlay-recording" class="overlay hidden">
<div class="overlay-backdrop" data-close="overlay-recording"></div>
<div class="overlay-sheet overlay-sheet-small">
<div class="overlay-header">
<h2>녹화 시작</h2>
<button class="btn-close" data-close="overlay-recording"></button>
</div>
<div class="overlay-body">
<div class="field-row">
<label>저장 경로</label>
<input type="text" id="rec-save-dir" />
<button id="btn-browse" class="btn btn-small"></button>
</div>
<div id="browse-panel" class="browse-panel hidden"></div>
<div class="field-row">
<label>파일 이름</label>
<input type="text" id="rec-filename" placeholder="scan_001" />
</div>
<div class="btn-row">
<button id="btn-rec-confirm" class="btn btn-red">● 녹화 시작</button>
</div>
<div id="rec-path" class="hint"></div>
</div>
</div>
</div>
<!-- Log overlay — opened from inside Settings, stacks above it. -->
<div id="overlay-log" class="overlay hidden">
<div class="overlay-backdrop" data-close="overlay-log"></div>
<div class="overlay-sheet">
<div class="overlay-header">
<h2>로그</h2>
<div class="log-header-actions">
<label class="log-autoscroll"><input type="checkbox" id="log-autoscroll" checked /> 자동 스크롤</label>
<button id="btn-log-clear" class="btn btn-small btn-gray">지우기</button>
<button class="btn-close" data-close="overlay-log"></button>
</div>
</div>
<div id="log-body" class="log-body"></div>
</div>
</div>
</div>
<script type="module" src="/js/app.js"></script>
</body>
</html>