v3.2.0: 카카오톡 연동 + 탐정앱 업그레이드 + 모바일 대응 강화

- 카카오톡 채널 챗봇 (오픈빌더 비동기 콜백 방식, 5초 제한 우회)
- 카카오 "나에게 보내기" OAuth 알림 (kakao-notify.ts)
- 채널 설정 UI + userMap으로 다중 사용자 계정 매핑
- 탐정앱: 탭(메모/체크리스트/타임라인), 우선순위, 검색, 보고서/고소장/접수안내 생성
- 모바일 브레이크포인트 640px → 1024px (갤럭시 폴드 대응)
- 프롬프트 파일 제한 상향 (USER.md 3000, SOUL.md/AGENTS.md 4000)
- 교통앱 마지막 선택 지역 localStorage 저장/복원
- docs/kakao-setup.md 작성

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kim
2026-06-26 11:15:27 +09:00
co-authored by Claude Sonnet 4.6
parent cea916be78
commit d74614cee1
23 changed files with 3761 additions and 122 deletions
+54
View File
@@ -81,10 +81,45 @@
@keyframes spin { to{transform:rotate(360deg)} }
.chip-badge { display:inline-block; padding:2px 8px; border-radius:4px; font-size:10px; font-weight:600; background:#1a3a1a; color:var(--green); border:1px solid #2d5a2d; }
/* ── mob-tab (inline since styles.css not loaded) ── */
.mob-tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;border:none;background:transparent;color:var(--muted);font-family:inherit;font-size:10px;font-weight:700;letter-spacing:.04em;cursor:pointer;padding:6px 4px;border-top:2px solid transparent;transition:color .15s,border-color .15s;-webkit-tap-highlight-color:transparent;}
.mob-tab.active{color:var(--accent);border-top-color:var(--accent);}
/* ── Mobile ── */
@media(max-width:1024px){
.header{flex-wrap:wrap;padding:7px 10px;gap:6px;}
.header h1{font-size:13px;}
.toolbar{gap:4px;}
.toolbar .btn{padding:5px 8px;font-size:11px;}
.left-panel{
position:fixed;top:0;left:0;bottom:0;z-index:500;
width:84vw;max-width:310px;
transform:translateX(-100%);transition:transform .25s ease;
box-shadow:4px 0 24px rgba(0,0,0,.6);
}
.left-panel.mob-open{transform:translateX(0);}
#mob-backdrop{display:none;position:fixed;inset: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 #2d2d4a;height:52px;}
.main{padding-bottom:52px;}
.serial-panel{border:none;}
input,select{font-size:16px !important;}
}
@media(min-width:1025px){
#mob-backdrop{display:none !important;}
#mob-tabbar{display:none !important;}
}
</style>
</head>
<body>
<div id="mob-backdrop" onclick="mobClosePanel()"></div>
<div id="mob-tabbar" style="display:none">
<button class="mob-tab" id="mobtab-ctrl" onclick="mobTogglePanel()"><span style="font-size:18px">⚡</span>플래시</button>
<button class="mob-tab active" id="mobtab-term" onclick="mobClosePanel()"><span style="font-size:18px">📟</span>터미널</button>
</div>
<div class="header">
<h1>⚡ ESP32 Flasher</h1>
<span class="file" id="filename">sketch.ino</span>
@@ -466,6 +501,25 @@ async function boot() {
}
boot();
// ── Mobile ──────────────────────────────────────────────────────────────────
function mobClosePanel(){
document.querySelector('.left-panel').classList.remove('mob-open');
document.getElementById('mob-backdrop').classList.remove('show');
document.getElementById('mobtab-ctrl').classList.remove('active');
document.getElementById('mobtab-term').classList.add('active');
}
function mobTogglePanel(){
if(window.innerWidth>640)return;
const open=document.querySelector('.left-panel').classList.contains('mob-open');
if(open){mobClosePanel();}
else{
document.querySelector('.left-panel').classList.add('mob-open');
document.getElementById('mob-backdrop').classList.add('show');
document.getElementById('mobtab-ctrl').classList.add('active');
document.getElementById('mobtab-term').classList.remove('active');
}
}
</script>
</body>
</html>