feat: claude-app Anthropic 백엔드 추가 + 코드 에디터 하단 패널 리사이즈/테마 통일

- claude-app에 Anthropic 백엔드 추가 (Ollama/LM Studio에 이어 3번째). Messages
  API(system 필드, tool_use/tool_result 블록) 변환 로직 구현, 브라우저 직접
  호출(anthropic-dangerous-direct-browser-access), API 키는 브라우저 로컬
  저장만 하고 서버로는 전송하지 않음
- 코드 에디터 터미널 패널에도 클로드 앱과 동일한 하단 패널 높이 제한(전체
  높이의 1/2~7/10) 적용, 공용 헬퍼(_bottomPanelHeightBounds)로 정리
- 터미널 배경색을 클로드 앱 Ubuntu 테마 색(#4B1D45)으로 통일

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
kim
2026-07-04 00:18:10 +09:00
co-authored by Claude Sonnet 5
parent 504830693e
commit 830ae12bac
4 changed files with 111 additions and 15 deletions
+11 -7
View File
@@ -757,7 +757,7 @@ function createXtermShell(container, shellId) {
sub.style.cssText = 'position:absolute;inset:0';
container.appendChild(sub);
const term = new Terminal({
theme: { background:'#0C0C0C', foreground:'#CCCCCC', cursor:'#22c55e',
theme: { background:'#4B1D45', foreground:'#CCCCCC', cursor:'#22c55e',
selectionBackground:'rgba(34,197,94,0.3)', red:'#E74856', green:'#16C60C',
yellow:'#C19C00', blue:'#3B78FF', cyan:'#61D6D6', white:'#CCCCCC',
brightBlack:'#767676', brightGreen:'#69FF69', brightWhite:'#FFFFFF' },
@@ -882,6 +882,9 @@ function toggleCodeTerminal() {
if (codeTermOpen) {
panel.style.display = 'flex';
if (btn) btn.classList.add('active');
const { min, max } = _bottomPanelHeightBounds();
const cur = panel.offsetHeight;
if (cur < min || cur > max) panel.style.height = Math.round((min + max) / 2) + 'px';
if (!codeTermShells.length) { setTimeout(() => { if (companionConnected) addLocalShell(); else addCodeTermShell(); }, 60); }
else { setTimeout(() => { const s = codeTermShells.find(s => s.id === activeCodeTermShellId); if (s?.fitAddon) s.fitAddon.fit(); if (s?.term) { s.term.focus(); const inst = xtermInstances.get(s.id); if (inst?.ptyReady) { if (localShellIds.has(s.id)) { if (companionConnected && companionWs) companionWs.send(JSON.stringify({ type: 'input', shellId: s.id, data: companionPlatform === 'windows' ? 'cls\r' : 'clear\r' })); } else { termWsSend({ type: 'pty_input', shellId: s.id, data: 'clear\r' }); } } } }, 50); }
} else {
@@ -950,8 +953,8 @@ function emuPanelNewTab() {
if (_emuPanelUrl) window.open(_emuPanelUrl, '_blank');
}
// 클로드 앱 패널 높이는 항상 전체 높이의 1/2~7/10 사이로 제한한다.
function _claudePanelBounds() {
// 하단 패널(클로드 앱, 터미널 등) 높이는 항상 전체 높이의 1/2~7/10 사이로 제한한다.
function _bottomPanelHeightBounds() {
const total = document.getElementById('code-view')?.clientHeight || window.innerHeight;
return { min: total * 0.5, max: total * 0.7 };
}
@@ -967,7 +970,7 @@ function toggleClaudeAppPanel() {
if (_claudePanelOpen) {
const iframe = document.getElementById('code-claude-iframe');
if (iframe && !iframe.getAttribute('src')) iframe.src = '/claude-app.html';
const { min, max } = _claudePanelBounds();
const { min, max } = _bottomPanelHeightBounds();
const cur = panel.offsetHeight;
if (cur < min || cur > max) panel.style.height = Math.round((min + max) / 2) + 'px';
}
@@ -982,7 +985,7 @@ document.addEventListener('DOMContentLoaded', function() {
drag.addEventListener('mouseover', () => drag.style.background = 'rgba(189,147,249,.4)');
drag.addEventListener('mousedown', e => {
startY = e.clientY; startH = panel.offsetHeight; e.preventDefault();
const { min, max } = _claudePanelBounds();
const { min, max } = _bottomPanelHeightBounds();
// 드래그 중 마우스가 iframe 위로 지나가면 부모 document가 mousemove/mouseup을 못 받아
// 드래그가 안 풀리므로, 드래그하는 동안은 iframe이 마우스 이벤트를 가로채지 않게 한다.
const iframe = document.getElementById('code-claude-iframe');
@@ -1278,15 +1281,16 @@ document.addEventListener('DOMContentLoaded', function() {
const panel = document.getElementById('code-term-panel');
if (!drag || !panel) return;
let startY=0, startH=0;
drag.addEventListener('mouseover', ()=>drag.style.background='rgba(34,197,94,.4)');
drag.addEventListener('mousedown', e => {
startY=e.clientY; startH=panel.offsetHeight; e.preventDefault();
const { min, max } = _bottomPanelHeightBounds();
const onMove = e2 => {
const dy = startY - e2.clientY;
panel.style.height = Math.max(80, Math.min(window.innerHeight*.6, startH+dy)) + 'px';
panel.style.height = Math.max(min, Math.min(max, startH+dy)) + 'px';
const s = codeTermShells.find(s => s.id === activeCodeTermShellId); if (s?.fitAddon) s.fitAddon.fit();
};
const onUp = () => { document.removeEventListener('mousemove',onMove); document.removeEventListener('mouseup',onUp); drag.style.background=''; };
drag.addEventListener('mouseover', ()=>drag.style.background='rgba(34,197,94,.4)');
document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp);
});
});