v3.2.2: 폴더블 폰 모바일 패널 토글 전면 수정 + 탐정앱 네트워크 분석 + 음성 유틸
- mobToggle 가드 640→1024: 음악·슬라이드·코드·날씨·언어·치과·ESP32·아두이노 앱 (CSS max-width:1024px인데 JS guard가 >640으로 남아 폴드 내부화면에서 토글 불가) - #mob-tabbar z-index 400/490→600, 패널 bottom 0→52px, backdrop inset 0 0 52px 0 (backdrop이 탭바를 가려 열린 상태에서 다른 탭 전환 불가 문제 해결) - 모든 innerWidth<=640 자동트리거 →<=1024 통일 (9개 앱) - 탐정앱: 네트워크 분석 패널 (IP조회/Traceroute/Nmap/DNS/WHOIS/SSL/취약점/해시/웹스캔/배너/CVE) - voice-utils.js 분리 — 서브앱 공통 음성입력+TTS 유틸 - app.js: 컨텍스트 토큰 게이지 (세션 목록에 tok/max 표시), usage 이벤트 처리 - routes-music.ts: Demucs 보컬분리 + YouTube 다운로드 잡 관리 (TTL 자동정리) - server-v2.ts: 탐정 네트워크 API (traceroute/dns/whois/ssl/nmap/vuln/hash/webscan/cve) - session.ts: resolveNumCtx export, /api/agent/session contextTokenEstimate 노출 - config.json: 카카오 채널 설정, ITS 교통 API 키 vault 참조 - styles.css: app-mic-btn / app-tts-btn 공통 스타일 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+13
-8
@@ -145,7 +145,7 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;fon
|
||||
.wt-hdr{flex-wrap:wrap;padding:5px 10px;gap:5px;}
|
||||
/* Sidebar → left overlay */
|
||||
.wt-sb{
|
||||
position:fixed;top:0;left:0;bottom:0;z-index:500;
|
||||
position:fixed;top:0;left:0;bottom:52px;z-index:500;
|
||||
width:78vw;max-width:280px;
|
||||
transform:translateX(-100%);transition:transform .25s ease;
|
||||
box-shadow:4px 0 24px rgba(0,0,0,.4);
|
||||
@@ -153,7 +153,7 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;fon
|
||||
.wt-sb.mob-open{transform:translateX(0);}
|
||||
/* Chat → right overlay */
|
||||
.wt-chat{
|
||||
position:fixed;top:0;right:0;bottom:0;z-index:500;
|
||||
position:fixed;top:0;right:0;bottom:52px;z-index:500;
|
||||
width:92vw;max-width:360px;
|
||||
transform:translateX(100%);transition:transform .25s ease;
|
||||
box-shadow:-4px 0 24px rgba(0,0,0,.4);
|
||||
@@ -162,9 +162,9 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;fon
|
||||
/* Hide resize handle */
|
||||
.wt-resize{display:none;}
|
||||
|
||||
#mob-backdrop{display:none;position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:499;}
|
||||
#mob-backdrop{display:none;position:fixed;inset:0 0 52px 0;background:rgba(0,0,0,.55);z-index:499;}
|
||||
#mob-backdrop.show{display:block;}
|
||||
#mob-tabbar{display:flex !important;position:fixed;bottom:0;left:0;right:0;z-index:490;background:var(--panel);border-top:1px solid var(--line);height:52px;}
|
||||
#mob-tabbar{display:flex !important;position:fixed;bottom:0;left:0;right:0;z-index:600;background:var(--panel);border-top:1px solid var(--line);height:52px;}
|
||||
|
||||
.wt-layout{padding-bottom:52px;}
|
||||
.wt-center{border:none;}
|
||||
@@ -176,6 +176,7 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;fon
|
||||
#mob-tabbar{display:none !important;}
|
||||
}
|
||||
</style>
|
||||
<script src="voice-utils.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -273,6 +274,7 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;fon
|
||||
<div class="wt-msgs" id="chat-msgs"></div>
|
||||
<div class="wt-input-bar">
|
||||
<textarea class="wt-textarea" id="chat-input" rows="1" placeholder="날씨, 예보, 기상 현상을 질문하세요..." onkeydown="handleKey(event)" oninput="autoResize(this)"></textarea>
|
||||
<button class="app-mic-btn" id="mic-btn" onclick="toggleMicRecording()" title="음성 입력">🎤</button>
|
||||
<button class="wt-send" id="send-btn" onclick="sendMessage()">전송</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -538,7 +540,9 @@ function addMsg(role,html){
|
||||
const msgs=document.getElementById('chat-msgs');
|
||||
const div=document.createElement('div');div.className='wt-msg '+role;
|
||||
const b=document.createElement('div');b.className='wt-bubble';b.innerHTML=html;
|
||||
div.appendChild(b);msgs.appendChild(div);msgs.scrollTop=msgs.scrollHeight;
|
||||
div.appendChild(b);
|
||||
if(role==='assistant'){const tb=document.createElement('button');tb.className='app-tts-btn';tb.title='음성으로 듣기';tb.textContent='🔊';tb.onclick=function(){playTTSText(tb,b.innerText);};div.appendChild(tb);}
|
||||
msgs.appendChild(div);msgs.scrollTop=msgs.scrollHeight;
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -575,13 +579,13 @@ function loadWindyUrl(url,ov){
|
||||
document.getElementById('windy-frame').src=url;
|
||||
}
|
||||
|
||||
function sendToChat(msg){if(window.innerWidth<=640)mobToggle('chat');addMsg('user',escHtml(msg));callChat(msg);}
|
||||
function sendToChat(msg){if(window.innerWidth<=1024)mobToggle('chat');addMsg('user',escHtml(msg));callChat(msg);}
|
||||
|
||||
async function sendMessage(){
|
||||
const input=document.getElementById('chat-input');
|
||||
const msg=input.value.trim();
|
||||
if(!msg||document.getElementById('send-btn').disabled)return;
|
||||
if(window.innerWidth<=640)mobToggle('chat');
|
||||
if(window.innerWidth<=1024)mobToggle('chat');
|
||||
input.value='';autoResize(input);
|
||||
addMsg('user',escHtml(msg));
|
||||
await callChat(msg);
|
||||
@@ -653,7 +657,7 @@ function mobCloseAll(){
|
||||
document.getElementById('mobtab-map').classList.add('active');
|
||||
}
|
||||
function mobToggle(which){
|
||||
if(window.innerWidth>640)return;
|
||||
if(window.innerWidth>1024)return;
|
||||
const sbOpen=document.querySelector('.wt-sb').classList.contains('mob-open');
|
||||
const chatOpen=document.querySelector('.wt-chat').classList.contains('mob-open');
|
||||
if(which==='sb'&&sbOpen){mobCloseAll();return;}
|
||||
@@ -681,6 +685,7 @@ function mobToggle(which){
|
||||
await Promise.all([initSessionId(),loadSkill(),loadModels()]);
|
||||
await loadHistory();
|
||||
addMsg('system','🌤️ 날씨 어시스턴트입니다. 지역 날씨, 예보, 기상 현상을 질문하세요.');
|
||||
initAppVoice(sendMessage);
|
||||
document.getElementById('chat-input').focus();
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user