Add Android build target and responsive/drawer UI for phones and tablets

Restructure src-tauri into lib+bin (Tauri 2's mobile requirement — Android
embeds the app as a JNI cdylib, which needs a [lib] target with
tauri::mobile_entry_point) and scaffold gen/android/ via `cargo tauri
android init`. Builds and packages cleanly to a debug APK/AAB.

pcd_viewer.html: the 284px docked sidebar becomes a slide-in drawer under
768px viewport width (toggled from the rail, closes on backdrop tap), the
rail sheds its subtitle and secondary telemetry chips under 480px, and the
camera panel caps its width to the viewport. Also disables native pinch-zoom
(the canvas has its own via OrbitControls) and sets overscroll-behavior:none
for a full-bleed touch surface.
This commit is contained in:
Dongubak
2026-08-23 15:02:40 +09:00
parent 75d54de763
commit 8730d5cbaf
46 changed files with 5581 additions and 5 deletions
+50 -2
View File
@@ -2,7 +2,10 @@
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<!-- user-scalable=no: the canvas has its own pinch-to-zoom (OrbitControls) —
letting the OS page-zoom fight it over the same two-finger gesture is worse
than losing native page zoom, which this app has no text layout to need anyway. -->
<title>PCD Viewer — 포인트 클라우드 계측</title>
<script>
// Applied synchronously, before first paint, so a stored "light" preference
@@ -53,7 +56,7 @@ try {
--measure:#17181a;
}
*{ box-sizing:border-box; }
html,body{ margin:0; height:100%; overflow:hidden; background:var(--bg); color:var(--txt);
html,body{ margin:0; height:100%; overflow:hidden; overscroll-behavior:none; background:var(--bg); color:var(--txt);
font-family:var(--mono); -webkit-font-smoothing:antialiased; }
#app{ position:fixed; inset:0; }
canvas{ display:block; }
@@ -66,6 +69,13 @@ try {
display:flex; align-items:center; justify-content:space-between; padding:0 16px; gap:16px;
}
#rail .mark{ display:flex; align-items:baseline; gap:11px; min-width:0; }
/* drawer toggle — only shown under the small-screen media query below */
#panelToggle{
display:none; flex:none; background:transparent; color:var(--txt); border:1px solid var(--line-strong);
padding:5px 10px; font:inherit; font-size:9.5px; letter-spacing:0.12em; cursor:pointer;
}
#panelToggle:hover{ background:var(--hover-strong); }
#panelBackdrop{ display:none; }
#rail h1{ font-size:11.5px; font-weight:500; margin:0; letter-spacing:0.22em; text-transform:uppercase; }
#rail .tag{ font-size:9.5px; color:var(--faint); letter-spacing:0.14em; white-space:nowrap; }
#rail .rail-right{ display:flex; align-items:center; gap:16px; flex:none; }
@@ -237,6 +247,30 @@ try {
color:var(--txt); font-size:12px; letter-spacing:0.06em; text-align:center; }
#drop .frame .s{ display:block; margin-top:9px; font-size:10px; color:var(--faint); letter-spacing:0.24em; }
/* ── small screens (phones; tablets stay on the fixed-sidebar layout above) —
the instrument column becomes a drawer instead of eating a third of the
width, since it's a fixed 284px regardless of viewport size ── */
@media (max-width:768px){
#panelToggle{ display:block; }
#panel{
width:min(284px, 84vw);
transform:translateX(-100%);
transition:transform .18s ease;
}
#panel.open{ transform:translateX(0); }
#panelBackdrop{
display:block; position:fixed; top:42px; left:0; right:0; bottom:0; z-index:9;
background:var(--overlay); opacity:0; pointer-events:none; transition:opacity .18s ease;
}
#panelBackdrop.show{ opacity:1; pointer-events:auto; }
#cam{ width:min(340px, 92vw); }
}
@media (max-width:480px){
#rail{ padding:0 10px; gap:8px; }
#rail .tag{ display:none; } /* subtitle — first to go */
#rail .tele .chip:nth-child(2), #rail .tele .chip:nth-child(3){ display:none; } /* keep LIVE only */
}
@media (prefers-reduced-motion:reduce){ *{ transition:none !important; } }
</style>
</head>
@@ -261,6 +295,7 @@ try {
<div id="rail">
<div class="mark">
<button id="panelToggle" aria-label="패널 열기/닫기" aria-expanded="false" aria-controls="panel">MENU</button>
<h1>PCD Viewer</h1>
<span class="tag">FAST-LIVO2&nbsp;·&nbsp;POINT&nbsp;CLOUD&nbsp;METROLOGY</span>
</div>
@@ -277,6 +312,8 @@ try {
</div>
</div>
<div id="panelBackdrop"></div>
<div id="panel">
<div class="sect">
<div class="lab">LIVE · FAST-LIVO2</div>
@@ -576,6 +613,17 @@ function applyTheme(name) {
}
themeButtons.forEach(b => b.onclick = () => applyTheme(b.dataset.theme));
// ─── small-screen drawer: the instrument column overlays instead of docking ──
const panelEl = $('panel'), panelToggleEl = $('panelToggle'), panelBackdropEl = $('panelBackdrop');
function setPanelOpen(open) {
panelEl.classList.toggle('open', open);
panelBackdropEl.classList.toggle('show', open);
panelToggleEl.setAttribute('aria-expanded', String(open));
}
panelToggleEl.onclick = () => setPanelOpen(!panelEl.classList.contains('open'));
panelBackdropEl.onclick = () => setPanelOpen(false);
matchMedia('(max-width:768px)').addEventListener('change', e => { if (!e.matches) setPanelOpen(false); });
// segmented mode control
document.querySelectorAll('#modeSeg button').forEach(b => {
b.onclick = () => setMode(b.dataset.mode);