법률 사건관리 앱과 같은 패턴으로 의료 사건(진료기록) 관리 앱 추가 (routes-doctor-cases.ts + doctor-app.html + WOPI 라우트). Home Assistant를 iframe으로 띄우는 얇은 래퍼 앱 추가(ha.applecherry.net, 2026-08-01 통합 완료된 것 URL만 새로 노출). weather_openmeteo의 요청 성형/리포트 포맷팅 로직을 weather-openmeteo-format.ts로 분리(순수함수라 테스트 가능, 2026-07-29 있었던 타임존/롤링윈도우/daily-in-hourly 버그 3건 회귀 방지 테스트 포함) + geocode 재시도 테스트. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
58 lines
3.0 KiB
HTML
58 lines
3.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>🏠 Home Assistant</title>
|
|
<link rel="icon" type="image/png" sizes="64x64" href="/homeclaw_logo.png">
|
|
<link rel="stylesheet" href="../css/styles.css">
|
|
<script>try{var _t=localStorage.getItem('homeclaw_theme')||'dark';document.documentElement.setAttribute('data-theme',_t);}catch(e){}</script>
|
|
<style>
|
|
:root{--brand:#18bcf2;}
|
|
html,body{height:100%;margin:0;padding:0;}
|
|
body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;display:flex;flex-direction:column;overflow:hidden;height:100dvh;}
|
|
|
|
.hdr{display:flex;align-items:center;gap:8px;padding:6px 12px;background:var(--panel);border-bottom:1px solid var(--line);flex-shrink:0;}
|
|
.hdr h1{font-size:13px;font-weight:700;margin:0;}
|
|
.hdr-sp{flex:1;}
|
|
.hdr-btn{background:none;border:1.5px solid var(--line);border-radius:6px;padding:3px 9px;font-size:11px;color:var(--muted);cursor:pointer;font-family:inherit;transition:.15s;text-decoration:none;display:inline-flex;align-items:center;}
|
|
.hdr-btn:hover{border-color:var(--brand);color:var(--brand);}
|
|
|
|
.frame-wrap{flex:1;min-height:0;position:relative;}
|
|
.frame-wrap iframe{width:100%;height:100%;border:none;display:block;}
|
|
.err{display:none;position:absolute;inset:0;align-items:center;justify-content:center;flex-direction:column;gap:10px;background:var(--bg);color:var(--muted);font-size:13px;text-align:center;padding:20px;}
|
|
.err.show{display:flex;}
|
|
.err a{color:var(--brand);}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header class="hdr">
|
|
<a href="/" style="text-decoration:none;font-size:13px;color:var(--muted)">← 홈</a>
|
|
<h1>🏠 Home Assistant</h1>
|
|
<span class="hdr-sp"></span>
|
|
<button class="hdr-btn" onclick="reloadFrame()">↺ 새로고침</button>
|
|
<a class="hdr-btn" href="https://ha.applecherry.net/" target="_blank">↗ 새 탭</a>
|
|
<button class="hdr-btn" onclick="toggleTheme()">🌙</button>
|
|
</header>
|
|
|
|
<div class="frame-wrap">
|
|
<iframe id="ha-frame" src="https://ha.applecherry.net/" title="Home Assistant"></iframe>
|
|
<div class="err" id="ha-err">
|
|
<div>Home Assistant를 불러오지 못했습니다.</div>
|
|
<div><a href="https://ha.applecherry.net/" target="_blank">새 탭에서 직접 열기 ↗</a></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function toggleTheme(){const d=document.documentElement;const n=d.getAttribute('data-theme')==='dark'?'light':'dark';d.setAttribute('data-theme',n);try{localStorage.setItem('homeclaw_theme',n);}catch{}}
|
|
function reloadFrame(){const f=document.getElementById('ha-frame');f.src=f.src;}
|
|
// iframe onerror doesn't fire for cross-origin load failures — fall back to a
|
|
// timeout check, since a blocked/unreachable HA instance otherwise just shows
|
|
// a blank white rectangle with no clue why.
|
|
let loaded=false;
|
|
document.getElementById('ha-frame').addEventListener('load',()=>{loaded=true;});
|
|
setTimeout(()=>{ if(!loaded) document.getElementById('ha-err').classList.add('show'); },8000);
|
|
</script>
|
|
</body>
|
|
</html>
|