Refactor code editor out of main UI + PubMed FTP PDF + login token fix
- Move code editor to standalone page; remove from main index/app mode switcher - Add diff-active tab highlight and diff bar CSS to styles - Add markdown table styles for chat messages - PubMed: try NCBI FTP link before Unpaywall for PDF download - Login: also persist token to localStorage for cross-tab auth - Update skills state and config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+42
-1106
File diff suppressed because it is too large
Load Diff
@@ -57,7 +57,6 @@
|
||||
<button class="mode-btn active" id="btn-chat" onclick="setMode('chat')">채팅</button>
|
||||
<button class="mode-btn" id="btn-bgtasks" onclick="setMode('bgtasks')">작업</button>
|
||||
<button class="mode-btn" id="btn-schedule" onclick="setMode('schedule')">일정</button>
|
||||
<button class="mode-btn" id="btn-code" onclick="setMode('code')">💻 코드</button>
|
||||
</div>
|
||||
|
||||
<div class="status-bar">
|
||||
@@ -145,97 +144,6 @@
|
||||
</div>
|
||||
|
||||
<!-- CODE SIDEBAR -->
|
||||
<div id="sidebar-code" style="display:none;flex-direction:column;flex:1;min-height:0;overflow:hidden">
|
||||
<!-- 내부 탭 -->
|
||||
<div style="display:flex;border-bottom:1px solid var(--line);flex-shrink:0;background:var(--panel-2)">
|
||||
<button class="code-sb-tab active" id="csb-tab-files" onclick="setCodeSbTab('files')">파일</button>
|
||||
<button class="code-sb-tab" id="csb-tab-model" onclick="setCodeSbTab('model')">모델</button>
|
||||
<button class="code-sb-tab" id="csb-tab-settings"onclick="setCodeSbTab('settings')">설정</button>
|
||||
</div>
|
||||
|
||||
<!-- 파일 탭 -->
|
||||
<div id="csb-files" style="display:flex;flex-direction:column;flex:1;min-height:0">
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;padding:7px 10px;border-bottom:1px solid var(--line);flex-shrink:0">
|
||||
<span style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em">workspace/code/</span>
|
||||
<div style="display:flex;gap:4px">
|
||||
<button onclick="codeNewFile()" title="새 파일" style="background:none;border:none;cursor:pointer;color:var(--muted);font-size:16px;line-height:1;padding:0 3px">+</button>
|
||||
<button onclick="codeSbRefresh()" title="새로고침" style="background:none;border:none;cursor:pointer;color:var(--muted);font-size:13px;line-height:1;padding:0 3px">↺</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="csb-file-tree" style="flex:1;overflow-y:auto;padding:4px 0">
|
||||
<div style="padding:12px;color:var(--muted);font-size:11px;text-align:center">로딩 중...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 모델 탭 -->
|
||||
<div id="csb-model" style="display:none;flex-direction:column;flex:1;min-height:0;overflow-y:auto;padding:14px 12px;gap:12px">
|
||||
<div>
|
||||
<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Coder AI 모델</div>
|
||||
<select id="csb-model-select" onchange="csbModelChanged()"
|
||||
style="width:100%;font-size:12px;padding:6px 8px;background:var(--panel-2);color:var(--text);border:1px solid var(--line);border-radius:8px;cursor:pointer">
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">세션 초기화</div>
|
||||
<button onclick="codeAiClear()" style="width:100%;padding:7px;border:1px solid var(--line);background:var(--panel-2);color:var(--muted);border-radius:8px;cursor:pointer;font-size:12px">대화 기록 지우기</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 설정 탭 -->
|
||||
<div id="csb-settings" style="display:none;flex-direction:column;flex:1;min-height:0;overflow-y:auto;padding:14px 12px;gap:14px">
|
||||
<div>
|
||||
<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">코드 저장 디렉토리</div>
|
||||
<div style="display:flex;align-items:center;gap:6px">
|
||||
<input type="text" id="csb-codedir" value="code" onchange="csbCodeDirChanged(this.value)"
|
||||
style="flex:1;font-size:12px;padding:6px 8px;background:var(--panel-2);color:var(--text);border:1px solid var(--line);border-radius:8px;font-family:'IBM Plex Mono',monospace">
|
||||
<button onclick="csbCodeDirSave()" title="저장" style="background:var(--brand);color:#fff;border:none;border-radius:8px;padding:6px 10px;font-size:11px;font-weight:700;cursor:pointer;white-space:nowrap">저장</button>
|
||||
</div>
|
||||
<div style="font-size:10px;color:var(--muted);margin-top:4px">워크스페이스 하위 폴더 (기본: code)</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">폰트 크기</div>
|
||||
<div style="display:flex;align-items:center;gap:8px">
|
||||
<input type="range" id="csb-fontsize" min="10" max="22" value="14" oninput="csbFontSize(this.value)"
|
||||
style="flex:1;accent-color:var(--brand)">
|
||||
<span id="csb-fontsize-label" style="font-size:11px;color:var(--muted);width:24px;text-align:right">14</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">줄 바꿈</div>
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:12px;color:var(--text)">
|
||||
<input type="checkbox" id="csb-wordwrap" checked onchange="csbWordWrap(this.checked)"
|
||||
style="accent-color:var(--brand);width:15px;height:15px">
|
||||
자동 줄 바꿈
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">에디터 테마</div>
|
||||
<select id="csb-theme" onchange="csbTheme(this.value)"
|
||||
style="width:100%;font-size:12px;padding:6px 8px;background:var(--panel-2);color:var(--text);border:1px solid var(--line);border-radius:8px;cursor:pointer">
|
||||
<option value="vs-dark">Dark (기본)</option>
|
||||
<option value="vs">Light</option>
|
||||
<option value="hc-black">High Contrast</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">탭 크기</div>
|
||||
<select id="csb-tabsize" onchange="csbTabSize(this.value)"
|
||||
style="width:100%;font-size:12px;padding:6px 8px;background:var(--panel-2);color:var(--text);border:1px solid var(--line);border-radius:8px;cursor:pointer">
|
||||
<option value="2" selected>2칸</option>
|
||||
<option value="4">4칸</option>
|
||||
<option value="8">8칸</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">미니맵</div>
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:12px;color:var(--text)">
|
||||
<input type="checkbox" id="csb-minimap" checked onchange="csbMinimap(this.checked)"
|
||||
style="accent-color:var(--brand);width:15px;height:15px">
|
||||
미니맵 표시
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PPTX SIDEBAR -->
|
||||
<div id="sidebar-pptx" style="display:none;flex-direction:column;flex:1;min-height:0;overflow:hidden">
|
||||
@@ -437,61 +345,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Code Editor View ─────────────────────────────────────── -->
|
||||
<div id="code-view">
|
||||
<!-- File tabs bar -->
|
||||
<div class="code-tabs-bar">
|
||||
<div id="code-file-tabs" style="display:flex;align-items:stretch"></div>
|
||||
<button class="code-new-tab-btn" onclick="codeNewFile()" title="새 파일">+</button>
|
||||
<div class="code-toolbar">
|
||||
<button id="code-folder-btn" class="code-tool-btn btn-folder" onclick="toggleCodeFolderBrowser(event)" title="파일 탐색기 열기"
|
||||
style="gap:4px;display:flex;align-items:center">
|
||||
📁 <span>workspace/code/</span> ▾
|
||||
</button>
|
||||
<button class="code-tool-btn btn-ai" onclick="codeSendSelectionToAI()" title="선택 영역을 AI에게 전송">💬 AI에게 전송</button>
|
||||
<div id="code-run-dropdown" style="position:relative;display:inline-flex">
|
||||
<button class="code-tool-btn btn-run" onclick="codeRunDefault()" title="실행">
|
||||
<span id="code-run-icon">▶</span> <span id="code-run-label">실행</span>
|
||||
</button>
|
||||
<button class="code-tool-btn btn-run" onclick="codeToggleRunMenu()" title="실행 방법 선택" style="padding:6px 4px;margin-left:-1px;border-left:1px solid rgba(34,197,94,.3)">▾</button>
|
||||
<div id="code-run-menu" style="display:none;position:absolute;top:100%;right:0;z-index:99;min-width:180px;background:#1a2e1a;border:1px solid #2d5a3a;border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,.5);margin-top:2px;overflow:hidden">
|
||||
</div>
|
||||
</div>
|
||||
<button class="code-tool-btn btn-save" onclick="codeSaveFile()" title="파일 저장 (Ctrl+S)">💾 저장</button>
|
||||
<button id="code-term-toggle-btn" class="code-tool-btn" onclick="toggleCodeTerminal()"
|
||||
title="터미널 토글 (Ctrl+`)"
|
||||
style="background:#111b14;border-color:#2d5a3a;color:#6fcf8a;font-family:'IBM Plex Mono',monospace;font-size:11px">⌨ 터미널</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Editor + AI panel -->
|
||||
<div class="code-body">
|
||||
<div id="code-editor-container"></div>
|
||||
<div class="code-vresize" id="code-vresize"></div>
|
||||
<div id="code-ai-panel">
|
||||
<div class="code-ai-header">
|
||||
<span id="code-ai-title">💻 Coder AI</span>
|
||||
</div>
|
||||
<div id="code-ai-messages">
|
||||
<div class="code-ai-msg thinking">코드를 선택하고 "AI에게 전송"을 누르거나, 아래에 질문을 입력하세요.</div>
|
||||
</div>
|
||||
<div class="code-ai-input-row">
|
||||
<textarea id="code-ai-input" placeholder="AI에게 질문..." rows="1"
|
||||
onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();codeAiSend();}"></textarea>
|
||||
<button class="code-tool-btn primary" onclick="codeAiSend()" style="flex-shrink:0;align-self:flex-end">전송</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Code editor terminal panel -->
|
||||
<div id="code-term-panel" style="display:none;flex-direction:column;flex-shrink:0;height:260px;min-height:80px;border-top:2px solid #1a4a35;background:#0c0c0c;position:relative">
|
||||
<div id="code-term-drag" style="position:absolute;top:0;left:0;right:0;height:5px;cursor:ns-resize;z-index:2;transition:background .15s"></div>
|
||||
<div style="display:flex;align-items:center;height:28px;background:#0d1f1a;border-bottom:1px solid #1a4a35;flex-shrink:0;overflow-x:auto;padding-left:2px">
|
||||
<div id="code-term-tabs" style="display:flex;align-items:stretch;height:100%;flex:1"></div>
|
||||
<button onclick="addCodeTermShell()" title="새 쉘" style="background:none;border:none;color:#3d6b4f;cursor:pointer;font-size:16px;padding:0 8px;line-height:1;flex-shrink:0">+</button>
|
||||
<button onclick="toggleCodeTerminal()" title="닫기" style="background:none;border:none;color:#3d6b4f;cursor:pointer;font-size:14px;padding:0 8px;line-height:1;flex-shrink:0">×</button>
|
||||
</div>
|
||||
<div id="code-term-container" style="flex:1;min-height:0;position:relative;overflow:hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Task Modal -->
|
||||
@@ -2006,19 +1859,6 @@
|
||||
<script src="app.js"></script>
|
||||
|
||||
<!-- Code folder browser dropdown (fixed, rendered outside any overflow:hidden container) -->
|
||||
<div id="code-folder-dropdown" style="display:none;position:fixed;z-index:9999;
|
||||
background:var(--panel);border:1px solid rgba(34,197,94,0.4);border-radius:10px;
|
||||
min-width:260px;max-height:320px;overflow-y:auto;
|
||||
box-shadow:0 8px 32px rgba(0,0,0,0.35);padding:4px 0">
|
||||
<div style="padding:6px 12px;font-size:10px;font-weight:700;color:rgba(34,197,94,0.9);
|
||||
border-bottom:1px solid rgba(34,197,94,0.2);display:flex;justify-content:space-between;align-items:center">
|
||||
<span>📁 workspace/code/</span>
|
||||
<span onclick="codeRefreshFolder()" style="cursor:pointer;opacity:0.7;font-size:13px" title="새로고침">↺</span>
|
||||
</div>
|
||||
<div id="code-folder-list" style="padding:2px 0">
|
||||
<div style="padding:8px 12px;color:var(--muted);font-size:11px">로딩 중...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="image-zoom-modal" onclick="closeImageZoom()">
|
||||
<img id="image-zoom-img" src="" alt="Zoomed image" />
|
||||
|
||||
+1
-1
@@ -419,7 +419,7 @@
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok && data.success) {
|
||||
if (data.token) { try { sessionStorage.setItem('smallclaw_token', data.token); } catch {} }
|
||||
if (data.token) { try { sessionStorage.setItem('smallclaw_token', data.token); localStorage.setItem('smallclaw_token', data.token); } catch {} }
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
showError(data.error || '인증 실패');
|
||||
|
||||
+230
-11
@@ -761,6 +761,14 @@
|
||||
.msg-role { font-size: 11px; color: #4f617b; font-weight: 700; margin-bottom: 6px; }
|
||||
.msg-content { line-height: 1.75; font-size: 14px; white-space: pre-wrap; word-break: break-word; color: #17243b; }
|
||||
.msg.user .msg-content { color: #0f3d8a; }
|
||||
.md-table-wrap { overflow-x: auto; margin: 10px 0; border-radius: 8px; border: 1px solid var(--line); white-space: normal; }
|
||||
.md-table { border-collapse: collapse; width: 100%; font-size: 13px; }
|
||||
.md-table th { background: var(--brand); color: #fff; padding: 8px 14px; text-align: left; font-weight: 600; white-space: nowrap; }
|
||||
.md-table td { padding: 7px 14px; border-bottom: 1px solid var(--line); vertical-align: top; }
|
||||
.md-table tr:last-child td { border-bottom: none; }
|
||||
.md-table tbody tr:nth-child(even) td { background: var(--panel-2); }
|
||||
.md-table tbody tr:hover td { background: rgba(22,104,227,0.05); }
|
||||
:root[data-theme="dark"] .md-table tbody tr:hover td { background: rgba(76,141,255,0.08); }
|
||||
.md-img { max-width: 100%; max-height: 400px; border-radius: 8px; cursor: pointer; margin: 8px 0; display: block; }
|
||||
.md-img:hover { opacity: 0.9; }
|
||||
.md-img-err { display: inline-block; color: var(--muted, #888); font-size: 13px; padding: 4px 8px; background: var(--surface2, #f0f0f0); border-radius: 6px; margin: 4px 0; }
|
||||
@@ -2376,6 +2384,7 @@
|
||||
}
|
||||
.code-file-tab.active { color: #c8e6c0; background: #112b20; border-top-color: #22c55e; }
|
||||
.code-file-tab:hover:not(.active) { color: #a5d6a7; background: #0f2318; }
|
||||
.code-file-tab.diff-active { border-top-color: #f0ad4e; background: #2a2510; color: #f0d060; }
|
||||
.code-file-tab-close { font-size: 13px; color: #3d6b4f; margin-left:4px; opacity:0; transition: opacity .15s; }
|
||||
.code-file-tab:hover .code-file-tab-close, .code-file-tab.active .code-file-tab-close { opacity: 1; }
|
||||
.code-file-tab-close:hover { color: #f87171; }
|
||||
@@ -2428,12 +2437,122 @@
|
||||
flex: 1; overflow-y: auto; padding: 10px 12px;
|
||||
display: flex; flex-direction: column; gap: 8px;
|
||||
}
|
||||
/* ── Code Diff Bar ──────────────────────────────────────────────────── */
|
||||
#code-diff-bar {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 6px 12px; background: #1a2e1a; border-bottom: 1px solid #2d5a3a;
|
||||
font-size: 13px; font-family: 'IBM Plex Mono', monospace; color: #a7d8b8;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
#code-diff-label { margin-right: auto; }
|
||||
#code-diff-accept {
|
||||
background: #28a745; color: #fff; border: none; border-radius: 4px;
|
||||
padding: 4px 12px; font-size: 12px; font-weight: 700; cursor: pointer;
|
||||
transition: background .15s;
|
||||
}
|
||||
#code-diff-accept:hover { background: #218838; }
|
||||
#code-diff-reject {
|
||||
background: #dc3545; color: #fff; border: none; border-radius: 4px;
|
||||
padding: 4px 12px; font-size: 12px; font-weight: 700; cursor: pointer;
|
||||
transition: background .15s;
|
||||
}
|
||||
#code-diff-reject:hover { background: #c82333; }
|
||||
#code-diff-container { display: none; } /* Legacy - no longer used */
|
||||
|
||||
.code-ai-msg { font-size: 12px; line-height: 1.55; }
|
||||
.code-ai-msg.user { color: var(--brand); font-weight: 600; }
|
||||
.code-ai-msg.assistant { color: var(--text); white-space: pre-wrap; }
|
||||
.code-ai-msg.thinking { color: var(--muted); font-style: italic; font-size: 11px; }
|
||||
.code-ai-msg.user {
|
||||
color: #fff; font-weight: 600;
|
||||
background: var(--brand); border-radius: 12px 12px 4px 12px;
|
||||
padding: 6px 12px; align-self: flex-end; max-width: 85%;
|
||||
margin-left: auto;
|
||||
}
|
||||
.code-ai-msg.assistant {
|
||||
color: var(--text); white-space: pre-wrap;
|
||||
background: var(--panel); border-radius: 12px 12px 12px 4px;
|
||||
padding: 8px 12px; max-width: 90%;
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
.code-ai-msg.thinking {
|
||||
color: var(--muted); font-style: italic; font-size: 11px;
|
||||
background: var(--panel-2); border-radius: 8px;
|
||||
padding: 6px 12px; max-width: 70%;
|
||||
}
|
||||
.code-ai-msg.system {
|
||||
color: var(--muted); font-size: 11px; text-align: center;
|
||||
background: transparent; padding: 4px 0;
|
||||
}
|
||||
.code-ai-plan {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
margin: 4px 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: #93c5fd;
|
||||
}
|
||||
.code-ai-tool-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 10px;
|
||||
margin: 3px 0;
|
||||
border-left: 3px solid var(--brand, #7c3aed);
|
||||
border-radius: 4px;
|
||||
background: var(--panel-2, #1e1e2e);
|
||||
font-size: 11px;
|
||||
font-family: 'IBM Plex Mono', monospace;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.code-ai-tool-icon { font-size: 13px; flex-shrink: 0; }
|
||||
.code-ai-tool-name { font-weight: 600; color: var(--text, #e2e8f0); }
|
||||
.code-ai-tool-result {
|
||||
font-size: 11px;
|
||||
color: var(--muted, #94a3b8);
|
||||
margin-left: 4px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 300px;
|
||||
}
|
||||
.code-ai-tech-selector {
|
||||
display: flex;
|
||||
gap: 3px;
|
||||
}
|
||||
.code-ai-tech-btn {
|
||||
background: none;
|
||||
border: 1px solid var(--line, #333);
|
||||
border-radius: 6px;
|
||||
padding: 2px 8px;
|
||||
font-size: 11px;
|
||||
color: var(--muted, #888);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.code-ai-tech-btn.active {
|
||||
border-color: var(--brand, #7c3aed);
|
||||
color: var(--brand2, #a07840);
|
||||
background: rgba(124, 92, 254, 0.1);
|
||||
}
|
||||
.code-ai-tech-btn:hover {
|
||||
border-color: var(--brand, #7c3aed);
|
||||
color: var(--brand2, #a07840);
|
||||
}
|
||||
.code-ai-msg.streaming {
|
||||
position: relative;
|
||||
}
|
||||
.code-ai-msg.streaming::after {
|
||||
content: ''; display: inline-block; width: 6px; height: 14px;
|
||||
background: var(--brand); border-radius: 2px; margin-left: 2px;
|
||||
animation: ai-cursor-blink 0.8s steps(2) infinite;
|
||||
}
|
||||
@keyframes ai-cursor-blink {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
}
|
||||
.code-ai-code-block {
|
||||
margin: 6px 0; border: 1px solid var(--line); border-radius: 6px; overflow: hidden;
|
||||
margin: 6px 0; border: 1px solid var(--line); border-radius: 6px; overflow: visible;
|
||||
}
|
||||
.code-ai-code-header {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
@@ -2441,8 +2560,8 @@
|
||||
color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em;
|
||||
}
|
||||
.code-ai-open-btn {
|
||||
background: var(--brand); color: #fff; border: none; border-radius: 4px;
|
||||
padding: 2px 8px; font-size: 10px; font-weight: 700; cursor: pointer; transition: opacity .15s;
|
||||
display: block; margin: 0 auto 8px; background: var(--brand); color: #fff; border: none; border-radius: 4px;
|
||||
padding: 4px 12px; font-size: 11px; font-weight: 700; cursor: pointer; transition: opacity .15s;
|
||||
}
|
||||
.code-ai-open-btn:hover { opacity: 0.8; }
|
||||
.code-sb-tab {
|
||||
@@ -2484,15 +2603,28 @@
|
||||
font-family: 'IBM Plex Mono', 'Fira Code', monospace; line-height: 1.5;
|
||||
background: var(--panel); white-space: pre; color: var(--text);
|
||||
}
|
||||
.diff-block { margin: 0; padding: 4px 0; overflow-x: auto; font-size: 11px;
|
||||
font-family: 'IBM Plex Mono', 'Fira Code', monospace; line-height: 1.6;
|
||||
background: var(--panel); }
|
||||
.diff-line { display: block; padding: 0 8px; white-space: pre; }
|
||||
.diff-line-add { background: rgba(34,197,94,0.13); color: #4ade80; }
|
||||
.diff-line-del { background: rgba(248,113,113,0.13); color: #f87171; }
|
||||
.diff-line-hunk { color: #60a5fa; background: rgba(96,165,250,0.07); }
|
||||
.diff-line-file { color: var(--muted); font-style: italic; }
|
||||
.code-ai-input-row {
|
||||
display: flex; gap: 6px; padding: 8px; border-top: 1px solid var(--line); flex-shrink: 0;
|
||||
display: flex; gap: 6px; padding: 8px 10px; border-top: 1px solid var(--line); flex-shrink: 0;
|
||||
background: var(--panel);
|
||||
}
|
||||
#code-ai-input {
|
||||
flex: 1; resize: none; border: 1px solid var(--line); border-radius: 8px;
|
||||
padding: 6px 10px; font-size: 12px; font-family: inherit;
|
||||
background: var(--panel-2); color: var(--text); min-height: 36px; max-height: 100px;
|
||||
flex: 1; resize: none; border: 1.5px solid var(--line); border-radius: 16px;
|
||||
padding: 8px 14px; font-size: 12px; font-family: inherit;
|
||||
background: var(--bg); color: var(--text); min-height: 38px; max-height: 100px;
|
||||
transition: border-color .2s, box-shadow .2s;
|
||||
}
|
||||
#code-ai-input:focus {
|
||||
outline: none; border-color: var(--brand);
|
||||
box-shadow: 0 0 0 3px rgba(22,104,227,0.15);
|
||||
}
|
||||
#code-ai-input:focus { outline: none; border-color: var(--brand); }
|
||||
|
||||
/* ── Chat inline terminal ─────────────────────────────────────── */
|
||||
#chat-terminal-panel {
|
||||
@@ -2538,3 +2670,90 @@
|
||||
.ct-tab-close { font-size: 13px; color: #555; margin-left: 3px; line-height: 1; }
|
||||
.ct-tab-close:hover { color: #e74856; }
|
||||
#chat-term-container { position: relative; flex: 1; min-height: 0; overflow: hidden; }
|
||||
|
||||
/* ── Inline Diff Decorations (VSCode Copilot style) ──────────────────── */
|
||||
.inline-diff-added {
|
||||
background: rgba(40, 167, 69, 0.15) !important;
|
||||
border-left: 3px solid #28a745 !important;
|
||||
}
|
||||
.inline-diff-glyph-added {
|
||||
background: #28a745 !important;
|
||||
width: 4px !important;
|
||||
margin-left: 3px;
|
||||
}
|
||||
.inline-diff-deleted-zone {
|
||||
background: rgba(220, 53, 69, 0.08);
|
||||
border-left: 3px solid #dc3545;
|
||||
font-family: 'IBM Plex Mono', 'Fira Code', 'Courier New', monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
overflow: hidden;
|
||||
}
|
||||
.inline-diff-deleted-line {
|
||||
color: #dc3545;
|
||||
text-decoration: line-through;
|
||||
opacity: 0.7;
|
||||
padding: 0 8px;
|
||||
white-space: pre;
|
||||
}
|
||||
.inline-diff-hunk-actions {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
z-index: 20;
|
||||
padding: 3px 6px;
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
||||
pointer-events: auto;
|
||||
}
|
||||
.inline-diff-accept-btn {
|
||||
background: #28a745; color: #fff; border: none; border-radius: 4px;
|
||||
padding: 2px 8px; font-size: 11px; font-weight: 700; cursor: pointer;
|
||||
transition: background .15s; line-height: 1.4;
|
||||
}
|
||||
.inline-diff-accept-btn:hover { background: #218838; }
|
||||
.inline-diff-reject-btn {
|
||||
background: #dc3545; color: #fff; border: none; border-radius: 4px;
|
||||
padding: 2px 8px; font-size: 11px; font-weight: 700; cursor: pointer;
|
||||
transition: background .15s; line-height: 1.4;
|
||||
}
|
||||
.inline-diff-reject-btn:hover { background: #c82333; }
|
||||
|
||||
/* ── Streaming Diff (real-time editing indicator) ──────────────────────── */
|
||||
.inline-diff-streaming-change {
|
||||
background: rgba(255, 193, 7, 0.12) !important;
|
||||
border-left: 3px solid #ffc107 !important;
|
||||
}
|
||||
.inline-diff-glyph-change {
|
||||
background: #ffc107 !important;
|
||||
width: 4px !important;
|
||||
margin-left: 3px;
|
||||
}
|
||||
.inline-diff-streaming-delete {
|
||||
background: rgba(220, 53, 69, 0.10) !important;
|
||||
border-left: 3px solid #dc3545 !important;
|
||||
text-decoration: line-through;
|
||||
opacity: 0.75;
|
||||
}
|
||||
.inline-diff-glyph-delete {
|
||||
background: #dc3545 !important;
|
||||
width: 4px !important;
|
||||
margin-left: 3px;
|
||||
}
|
||||
.inline-diff-streaming-add-marker {
|
||||
border-left: 3px solid #28a745 !important;
|
||||
}
|
||||
|
||||
/* ── Tab Change Indicators ───────────────────────────────────────────── */
|
||||
.code-tab-dot {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
min-width: 16px; height: 16px; border-radius: 8px;
|
||||
background: #f0ad4e; color: #000; font-size: 9px; font-weight: 800;
|
||||
padding: 0 3px; margin-right: 4px; line-height: 1;
|
||||
}
|
||||
|
||||
/* ── AI Mode Selector ────────────────────────────────────────────────── */
|
||||
|
||||
@keyframes pulse-red { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:0.8;transform:scale(1.04)} }
|
||||
|
||||
Reference in New Issue
Block a user