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
+75
View File
@@ -173,10 +173,57 @@
/* Override the main-app inline display:none / flex on the shared sidebar markup */
#sidebar-code { display: flex !important; }
#code-view { flex: 1; min-width: 0; }
/* ── Mobile ── */
@media(max-width:1024px){
.code-page-header{flex-wrap:wrap;padding:6px 10px;gap:5px;}
.code-page-header h1{font-size:13px;}
.code-page-header > div:last-child{display:none;}
/* 좌측 파일 패널 → 왼쪽 오버레이 */
.code-sidebar-wrap{
position:fixed;top:0;left:0;bottom:0;z-index:500;
width:82vw;max-width:300px;
transform:translateX(-100%);transition:transform .25s ease;
box-shadow:4px 0 24px rgba(0,0,0,.4);
}
.code-sidebar-wrap.mob-open{transform:translateX(0);}
/* 우측 AI 패널 → 오른쪽 오버레이 */
#code-ai-panel{
position:fixed !important;top:0;right:0;bottom:0;z-index:500;
width:92vw;max-width:400px;
transform:translateX(100%);transition:transform .25s ease;
box-shadow:-4px 0 28px rgba(0,0,0,.5);
grid-area:unset !important;
}
#code-ai-panel.mob-open{transform:translateX(0);}
/* 에디터뷰 grid를 에디터 단독으로 */
#code-view.active{
grid-template-columns:1fr !important;
grid-template-rows:1fr auto !important;
grid-template-areas:"editor" "term" !important;
}
.code-vresize{display:none;}
#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 var(--line);height:52px;}
.code-layout{padding-bottom:52px;}
input,select,textarea{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="mobCloseAll()"></div>
<div id="mob-tabbar" style="display:none">
<button class="mob-tab" id="mobtab-files" onclick="mobToggle('files')"><span style="font-size:18px">📁</span>파일</button>
<button class="mob-tab active" id="mobtab-editor" onclick="mobCloseAll()"><span style="font-size:18px">💻</span>에디터</button>
<button class="mob-tab" id="mobtab-ai" onclick="mobToggle('ai')"><span style="font-size:18px">🤖</span>AI</button>
</div>
<div class="code-page-header">
<span style="font-size:16px">💻</span>
<h1>코드 에디터</h1>
@@ -591,6 +638,34 @@
</div>
<script src="code.js?v=20260604s"></script>
<script>
function mobCloseAll(){
document.querySelector('.code-sidebar-wrap').classList.remove('mob-open');
document.getElementById('code-ai-panel').classList.remove('mob-open');
document.getElementById('mob-backdrop').classList.remove('show');
['files','ai'].forEach(t=>document.getElementById('mobtab-'+t)?.classList.remove('active'));
document.getElementById('mobtab-editor').classList.add('active');
}
function mobToggle(which){
if(window.innerWidth>640)return;
const filesOpen=document.querySelector('.code-sidebar-wrap').classList.contains('mob-open');
const aiOpen=document.getElementById('code-ai-panel').classList.contains('mob-open');
if(which==='files'&&filesOpen){mobCloseAll();return;}
if(which==='ai'&&aiOpen){mobCloseAll();return;}
mobCloseAll();
if(which==='files'){
document.querySelector('.code-sidebar-wrap').classList.add('mob-open');
document.getElementById('mob-backdrop').classList.add('show');
document.getElementById('mobtab-files').classList.add('active');
document.getElementById('mobtab-editor').classList.remove('active');
} else {
document.getElementById('code-ai-panel').classList.add('mob-open');
document.getElementById('mob-backdrop').classList.add('show');
document.getElementById('mobtab-ai').classList.add('active');
document.getElementById('mobtab-editor').classList.remove('active');
}
}
</script>
<div id="csb-ctx"></div>
</body>
</html>