feat: v2.12.0 - UI/UX Refinement (Model Sync & Premium Tooltips)
This commit is contained in:
+381
-15
@@ -2,7 +2,6 @@ import * as vscode from 'vscode';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {
|
||||
getConfig,
|
||||
_getBrainDir,
|
||||
findBrainFiles,
|
||||
buildApiUrl,
|
||||
@@ -13,6 +12,7 @@ import {
|
||||
resolveEngine,
|
||||
summarizeText
|
||||
} from './utils';
|
||||
import { getConfig } from './config';
|
||||
import { AgentExecutor, ChatMessage } from './agent';
|
||||
import { BridgeInterface } from './bridge';
|
||||
|
||||
@@ -88,8 +88,12 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
await this._sendBrainProfiles();
|
||||
await this._sendSessionList();
|
||||
await this._sendModels();
|
||||
await this._sendConfig();
|
||||
await this._restoreActiveSessionIntoView();
|
||||
break;
|
||||
case 'toggleMultiAgent':
|
||||
await vscode.workspace.getConfiguration('g1nation').update('multiAgentEnabled', data.value, vscode.ConfigurationTarget.Global);
|
||||
break;
|
||||
case 'getModels':
|
||||
await this._sendModels();
|
||||
break;
|
||||
@@ -128,6 +132,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
await this.syncBrain();
|
||||
await this._sendBrainStatus();
|
||||
break;
|
||||
case 'addMessage':
|
||||
this._view?.webview.postMessage({ type: 'addMessage', role: data.role, value: data.value, rationale: data.rationale });
|
||||
break;
|
||||
case 'addBrain':
|
||||
await this._addBrainProfile();
|
||||
break;
|
||||
@@ -143,6 +150,13 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
case 'refreshModels':
|
||||
await this._sendModels();
|
||||
break;
|
||||
case 'model':
|
||||
await vscode.workspace.getConfiguration('g1nation').update('defaultModel', data.value, vscode.ConfigurationTarget.Global);
|
||||
logInfo(`Default model updated to: ${data.value}`);
|
||||
break;
|
||||
case 'proactiveTrigger':
|
||||
await this._handleProactiveSuggestion(data.context);
|
||||
break;
|
||||
case 'exportResponse':
|
||||
const workspacePath = vscode.workspace.workspaceFolders?.[0].uri.fsPath || '';
|
||||
const defaultPath = path.join(workspacePath, 'g1_response.md');
|
||||
@@ -155,6 +169,12 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
vscode.window.showInformationMessage(`✅ Exported to ${path.basename(uri.fsPath)}`);
|
||||
}
|
||||
break;
|
||||
case 'approveAction':
|
||||
await this._agent.approveTransaction();
|
||||
break;
|
||||
case 'rejectAction':
|
||||
await this._agent.rejectTransaction();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -378,6 +398,17 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
await this._context.globalState.update('chat_sessions', sessions.slice(0, 50));
|
||||
}
|
||||
|
||||
private async _sendConfig() {
|
||||
if (!this._view) return;
|
||||
const config = getConfig();
|
||||
this._view.webview.postMessage({
|
||||
type: 'configUpdate',
|
||||
value: {
|
||||
multiAgentEnabled: config.multiAgentEnabled
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async _sendBrainStatus() {
|
||||
if (!this._view) return;
|
||||
const activeBrain = getActiveBrainProfile();
|
||||
@@ -631,6 +662,29 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
this._view.webview.postMessage({ type: 'agentsList', value: agents, selected: lastPath });
|
||||
}
|
||||
|
||||
private async _handleProactiveSuggestion(context: string) {
|
||||
if (!this._view) return;
|
||||
|
||||
let suggestion = '';
|
||||
switch (context) {
|
||||
case 'settings_exploration':
|
||||
suggestion = '💡 **Tip:** 모델 설정을 최적화하여 답변 속도를 2배 이상 높일 수 있습니다. 설정에서 `Max Context Size`를 조정해보세요!';
|
||||
break;
|
||||
case 'brain_sync_exploration':
|
||||
suggestion = '🧠 **Knowledge Sync:** 최근에 수정한 파일이 지식 베이스에 반영되지 않았나요? 지금 동기화 버튼을 눌러 최신 정보를 업데이트하세요.';
|
||||
break;
|
||||
case 'agent_selection_exploration':
|
||||
suggestion = '🤖 **Agent Skills:** 특정 언어나 프레임워크에 특화된 에이전트 스킬을 선택하면 더 정확한 코드를 생성할 수 있습니다.';
|
||||
break;
|
||||
default:
|
||||
suggestion = '💡 새로운 기능을 발견하셨나요? 궁금한 점이 있다면 언제든 물어보세요!';
|
||||
}
|
||||
|
||||
this._view.webview.postMessage({ type: 'streamStart' });
|
||||
this._view.webview.postMessage({ type: 'streamChunk', value: `\n\n> [!TIP]\n> ${suggestion}\n` });
|
||||
this._view.webview.postMessage({ type: 'streamEnd' });
|
||||
}
|
||||
|
||||
private async _createAgent() {
|
||||
const name = await vscode.window.showInputBox({
|
||||
prompt: 'Name of the new Agent (e.g., frontend_expert)',
|
||||
@@ -891,8 +945,24 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
|
||||
.msg-head { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 11px; color: var(--text-dim); }
|
||||
.av { width: 22px; height: 22px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 12px; }
|
||||
.av-user { background: var(--border); color: var(--text-primary); }
|
||||
.av-ai { background: var(--accent); color: #fff; }
|
||||
.icon-btn:hover { background: var(--border); color: var(--text-bright); }
|
||||
.icon-btn.active { color: var(--accent); border-color: var(--accent); background: rgba(187, 134, 252, 0.1); }
|
||||
|
||||
/* Tooltip System */
|
||||
[data-tooltip] { position: relative; }
|
||||
[data-tooltip]::after {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute; bottom: -30px; left: 50%; transform: translateX(-50%);
|
||||
background: #333; color: #fff; padding: 4px 8px; border-radius: 4px;
|
||||
font-size: 10px; white-space: nowrap; opacity: 0; pointer-events: none;
|
||||
transition: all 0.2s ease; z-index: 100; box-shadow: 0 4px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
[data-tooltip]:hover::after { opacity: 1; bottom: -35px; }
|
||||
|
||||
.header-controls { display: flex; gap: 8px; margin-left: auto; }
|
||||
|
||||
#promptInput::placeholder { color: var(--accent); opacity: 0.6; font-weight: 500; }
|
||||
|
||||
|
||||
.msg-body {
|
||||
padding-left: 30px;
|
||||
@@ -997,11 +1067,13 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
|
||||
/* --- Overlays & Others --- */
|
||||
.thinking-bar { height: 2px; background: transparent; position: relative; overflow: hidden; margin-top: -1px; }
|
||||
.thinking-bar.active::after {
|
||||
content: ''; position: absolute; top: 0; left: -40%; width: 40%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, var(--accent), transparent); animation: think 1.5s infinite;
|
||||
.thinking-bar.active {
|
||||
display: block;
|
||||
background: linear-gradient(90deg, transparent, #2196f3, #bb86fc, transparent);
|
||||
background-size: 200% 100%;
|
||||
animation: thinking 1.5s infinite linear;
|
||||
}
|
||||
@keyframes think { 0% { left: -40%; } 100% { left: 100%; } }
|
||||
@keyframes thinking { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
||||
|
||||
.history-overlay {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8);
|
||||
@@ -1025,6 +1097,140 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
margin-bottom: 10px; cursor: pointer; transition: 0.2s;
|
||||
}
|
||||
.history-item:hover { border-color: var(--accent); background: var(--accent-glow); }
|
||||
|
||||
/* --- Approval UI --- */
|
||||
.approval-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin: 15px 0;
|
||||
padding: 16px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-bright);
|
||||
border-radius: 12px;
|
||||
animation: msgIn 0.3s ease-out;
|
||||
}
|
||||
.approval-title { font-weight: 700; color: var(--accent); font-size: 12px; margin-bottom: 4px; display: flex; align-items: center; gap: 6px; }
|
||||
.approval-btns { display: flex; gap: 10px; }
|
||||
.btn-approve { flex: 1; background: var(--success); color: white; border: none; padding: 10px; border-radius: 8px; cursor: pointer; font-weight: 700; font-size: 12px; transition: 0.2s; }
|
||||
.btn-approve:hover { filter: brightness(1.1); transform: translateY(-1px); }
|
||||
.btn-reject { flex: 1; background: var(--error); color: white; border: none; padding: 10px; border-radius: 8px; cursor: pointer; font-weight: 700; font-size: 12px; transition: 0.2s; }
|
||||
.btn-reject:hover { filter: brightness(1.1); transform: translateY(-1px); }
|
||||
|
||||
/* --- Physics & Micro-interactions --- */
|
||||
button {
|
||||
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
user-select: none;
|
||||
outline: none;
|
||||
}
|
||||
button:active {
|
||||
transform: scale(0.96);
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
/* --- Hierarchical Grouping --- */
|
||||
.input-group {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
margin-top: 10px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* --- Storytelling Stepper --- */
|
||||
.stepper-container {
|
||||
display: none;
|
||||
margin: 12px 16px;
|
||||
padding: 12px;
|
||||
background: rgba(var(--accent-rgb), 0.05);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.1);
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
.stepper-container.active { display: block; }
|
||||
.steps {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
.step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex: 1;
|
||||
}
|
||||
.step-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--text-dim);
|
||||
transition: 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.step.active .step-dot {
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 12px var(--accent);
|
||||
transform: scale(1.5);
|
||||
}
|
||||
.step.complete .step-dot {
|
||||
background: var(--success);
|
||||
box-shadow: 0 0 8px var(--success);
|
||||
}
|
||||
.step-label {
|
||||
font-size: 9px;
|
||||
color: var(--text-dim);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.step.active .step-label { color: var(--accent); }
|
||||
.step.complete .step-label { color: var(--success); }
|
||||
|
||||
/* --- Rationale View (Thought Process) --- */
|
||||
.rationale-container {
|
||||
margin: 12px 0;
|
||||
padding: 16px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-left: 3px solid var(--accent);
|
||||
border-radius: 4px 12px 12px 4px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.rationale-header {
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--accent);
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.rationale-section {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.rationale-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-weight: 700;
|
||||
color: var(--text-bright);
|
||||
margin-bottom: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.rationale-content {
|
||||
color: var(--text-dim);
|
||||
padding-left: 20px;
|
||||
}
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateX(-10px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -1043,9 +1249,10 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
<select id="agentSel" title="Select Agentic Skill"></select>
|
||||
<button class="icon-btn" id="editAgentBtn" title="Edit Agent Skill">📝</button>
|
||||
<button class="icon-btn" id="addAgentBtn" title="Create Agent">+</button>
|
||||
<button class="icon-btn" id="internetBtn" title="Internet Access">🌐</button>
|
||||
<button class="icon-btn" id="brainBtn" title="Sync Knowledge">🧠</button>
|
||||
<button class="icon-btn" id="historyBtn" title="History">📜</button>
|
||||
<button class="icon-btn" id="multiAgentBtn" data-tooltip="Multi-Agent Mode">🤖</button>
|
||||
<button class="icon-btn" id="internetBtn" data-tooltip="Internet Access">🌐</button>
|
||||
<button class="icon-btn" id="brainBtn" data-tooltip="Sync Knowledge">🧠</button>
|
||||
<button class="icon-btn" id="historyBtn" data-tooltip="View History">📜</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1059,6 +1266,15 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
|
||||
<div class="thinking-bar" id="thinkingBar"></div>
|
||||
|
||||
<div id="stepper" class="stepper-container">
|
||||
<div class="steps">
|
||||
<div class="step" id="step-analyze"><div class="step-dot"></div><div class="step-label">Analyze</div></div>
|
||||
<div class="step" id="step-plan"><div class="step-dot"></div><div class="step-label">Plan</div></div>
|
||||
<div class="step" id="step-execute"><div class="step-dot"></div><div class="step-label">Execute</div></div>
|
||||
<div class="step" id="step-verify"><div class="step-dot"></div><div class="step-label">Verify</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat" id="chat">
|
||||
<div class="welcome">
|
||||
<div class="welcome-logo">✦</div>
|
||||
@@ -1088,6 +1304,11 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
<button id="sendBtn" class="send-btn">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button class="action-btn" style="flex:1" id="inputNewChatBtn">New Chat</button>
|
||||
<button class="action-btn" style="flex:1" id="inputSyncBtn">Sync Knowledge</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="file" id="fileInput" multiple hidden accept="image/*,.txt,.md,.pdf,.csv,.json,.js,.ts,.py,.java,.rs,.go">
|
||||
</div>
|
||||
|
||||
@@ -1098,6 +1319,52 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
const sendBtn = document.getElementById('sendBtn');
|
||||
const thinkingBar = document.getElementById('thinkingBar');
|
||||
const statusLabel = document.getElementById('statusLabel');
|
||||
const stepper = document.getElementById('stepper');
|
||||
|
||||
// --- Sound Manager ---
|
||||
const Sound = {
|
||||
ctx: null,
|
||||
init() { if (!this.ctx) this.ctx = new (window.AudioContext || window.webkitAudioContext)(); },
|
||||
play(freq, type, dur) {
|
||||
try {
|
||||
this.init();
|
||||
const osc = this.ctx.createOscillator();
|
||||
const gain = this.ctx.createGain();
|
||||
osc.type = type;
|
||||
osc.frequency.setValueAtTime(freq, this.ctx.currentTime);
|
||||
gain.gain.setValueAtTime(0.05, this.ctx.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.ctx.currentTime + dur);
|
||||
osc.connect(gain);
|
||||
gain.connect(this.ctx.destination);
|
||||
osc.start();
|
||||
osc.stop(this.ctx.currentTime + dur);
|
||||
} catch(e) {}
|
||||
},
|
||||
success() { this.play(880, 'sine', 0.1); setTimeout(() => this.play(1109, 'sine', 0.15), 80); },
|
||||
warn() { this.play(440, 'triangle', 0.3); }
|
||||
};
|
||||
|
||||
function setStep(stepId, state = 'active') {
|
||||
stepper.classList.add('active');
|
||||
const step = document.getElementById('step-' + stepId);
|
||||
if (step) {
|
||||
if (state === 'active') {
|
||||
document.querySelectorAll('.step').forEach(s => s.classList.remove('active'));
|
||||
step.classList.add('active');
|
||||
} else if (state === 'complete') {
|
||||
step.classList.remove('active');
|
||||
step.classList.add('complete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetStepper() {
|
||||
stepper.classList.remove('active');
|
||||
document.querySelectorAll('.step').forEach(s => {
|
||||
s.classList.remove('active');
|
||||
s.classList.remove('complete');
|
||||
});
|
||||
}
|
||||
const modelSel = document.getElementById('modelSel');
|
||||
const brainSel = document.getElementById('brainSel');
|
||||
const historyOverlay = document.getElementById('historyOverlay');
|
||||
@@ -1133,16 +1400,45 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
|
||||
window.approve = () => {
|
||||
const box = document.querySelector('.approval-box');
|
||||
if (box) box.remove();
|
||||
vscode.postMessage({ type: 'approveAction' });
|
||||
};
|
||||
window.reject = () => {
|
||||
const box = document.querySelector('.approval-box');
|
||||
if (box) box.remove();
|
||||
vscode.postMessage({ type: 'rejectAction' });
|
||||
};
|
||||
|
||||
function exportToMD(text) {
|
||||
vscode.postMessage({ type: 'exportResponse', text: text });
|
||||
}
|
||||
|
||||
function addMsg(text, role) {
|
||||
function addMsg(text, role, rationale) {
|
||||
const isUser = role === 'user';
|
||||
const msgEl = document.createElement('div');
|
||||
msgEl.className = 'msg ' + (isUser ? 'msg-user' : 'msg-ai');
|
||||
msgEl._raw = text;
|
||||
|
||||
// If rationale exists and it's an AI message, add the Rationale View
|
||||
if (!isUser && rationale && (rationale.problem || rationale.goal || rationale.reasoning)) {
|
||||
const ratDiv = document.createElement('div');
|
||||
ratDiv.className = 'rationale-container';
|
||||
let ratHtml = '<div class="rationale-header"><span>🧠</span> Thought Process</div>';
|
||||
if (rationale.problem) {
|
||||
ratHtml += '<div class="rationale-section"><div class="rationale-label"><span>⚠️</span> Problem</div><div class="rationale-content">' + rationale.problem + '</div></div>';
|
||||
}
|
||||
if (rationale.goal) {
|
||||
ratHtml += '<div class="rationale-section"><div class="rationale-label"><span>💡</span> Goal</div><div class="rationale-content">' + rationale.goal + '</div></div>';
|
||||
}
|
||||
if (rationale.reasoning) {
|
||||
ratHtml += '<div class="rationale-section"><div class="rationale-label"><span>✅</span> Rationale</div><div class="rationale-content">' + rationale.reasoning + '</div></div>';
|
||||
}
|
||||
ratDiv.innerHTML = ratHtml;
|
||||
chat.appendChild(ratDiv);
|
||||
}
|
||||
|
||||
const head = document.createElement('div');
|
||||
head.className = 'msg-head';
|
||||
head.innerHTML = isUser ? '<div class="av av-user">U</div> You' : '<div class="av av-ai">✦</div> G1nation';
|
||||
@@ -1179,6 +1475,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
window.addEventListener('message', e => {
|
||||
const msg = e.data;
|
||||
switch(msg.type) {
|
||||
case 'addMessage':
|
||||
addMsg(msg.value, msg.role, msg.rationale);
|
||||
break;
|
||||
case 'streamStart':
|
||||
thinkingBar.classList.remove('active');
|
||||
if (document.querySelector('.welcome')) document.querySelector('.welcome').remove();
|
||||
@@ -1196,6 +1495,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
case 'streamEnd':
|
||||
if (streamBody) streamBody.classList.remove('stream-active');
|
||||
streamBody = null; sendBtn.disabled = false;
|
||||
resetStepper();
|
||||
Sound.success();
|
||||
break;
|
||||
case 'restoreHistory':
|
||||
case 'sessionLoaded':
|
||||
@@ -1204,7 +1505,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
|
||||
if (history && history.length > 0) {
|
||||
chat.innerHTML = '';
|
||||
history.forEach(m => addMsg(m.content, m.role === 'assistant' ? 'assistant' : 'user'));
|
||||
history.forEach(m => addMsg(m.content, m.role === 'assistant' ? 'assistant' : 'user', m.rationale));
|
||||
}
|
||||
if (historyData.negativePrompt !== undefined) {
|
||||
negativePrompt.value = historyData.negativePrompt;
|
||||
@@ -1224,6 +1525,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
if (m === msg.value.selected) o.selected = true;
|
||||
modelSel.appendChild(o);
|
||||
});
|
||||
if (typeof updateInputPlaceholder === 'function') updateInputPlaceholder();
|
||||
statusLabel.innerText = \`Model: \${msg.value.selected}\`;
|
||||
break;
|
||||
case 'brainProfiles':
|
||||
@@ -1251,6 +1553,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
break;
|
||||
case 'autoContinue':
|
||||
statusLabel.innerText = msg.value; thinkingBar.classList.add('active');
|
||||
if (msg.value.includes('Analyzing')) setStep('analyze');
|
||||
if (msg.value.includes('Planning')) setStep('plan');
|
||||
if (msg.value.includes('Executing')) setStep('execute');
|
||||
setTimeout(() => { thinkingBar.classList.remove('active'); }, 3000);
|
||||
break;
|
||||
case 'agentsList':
|
||||
@@ -1272,6 +1577,18 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
thinkingBar.classList.remove('active'); sendBtn.disabled = false;
|
||||
addMsg(msg.value, 'error');
|
||||
break;
|
||||
case 'requiresApproval':
|
||||
const box = document.createElement('div');
|
||||
box.className = 'approval-box';
|
||||
box.innerHTML = '<div class="approval-title"><span>🛡️</span> 작업 승인 대기 중 (Action Approval Required)</div>' +
|
||||
'<div style="font-size: 11px; color: var(--text-dim); margin-bottom: 8px;">위의 변경 사항을 프로젝트에 반영할까요?</div>' +
|
||||
'<div class="approval-btns">' +
|
||||
' <button class="btn-approve" onclick="approve()">승인 (Approve)</button>' +
|
||||
' <button class="btn-reject" onclick="reject()">롤백 (Rollback)</button>' +
|
||||
'</div>';
|
||||
chat.appendChild(box);
|
||||
chat.scrollTop = chat.scrollHeight;
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1327,16 +1644,36 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
});
|
||||
input.addEventListener('input', () => { input.style.height = 'auto'; input.style.height = input.scrollHeight + 'px'; });
|
||||
|
||||
document.getElementById('newChatBtn').onclick = () => vscode.postMessage({ type: 'newChat' });
|
||||
const startNewChat = () => { Sound.play(660, 'sine', 0.1); vscode.postMessage({ type: 'newChat' }); };
|
||||
document.getElementById('newChatBtn').onclick = startNewChat;
|
||||
document.getElementById('inputNewChatBtn').onclick = startNewChat;
|
||||
|
||||
document.getElementById('settingsBtn').onclick = () => vscode.postMessage({ type: 'openSettings' });
|
||||
document.getElementById('internetBtn').onclick = () => {
|
||||
internetEnabled = !internetEnabled; document.getElementById('internetBtn').classList.toggle('active', internetEnabled);
|
||||
};
|
||||
document.getElementById('brainBtn').onclick = () => vscode.postMessage({ type: 'syncBrain' });
|
||||
|
||||
let multiAgentEnabled = true;
|
||||
document.getElementById('multiAgentBtn').onclick = () => {
|
||||
multiAgentEnabled = !multiAgentEnabled;
|
||||
vscode.postMessage({ type: 'toggleMultiAgent', value: multiAgentEnabled });
|
||||
document.getElementById('multiAgentBtn').classList.toggle('active', multiAgentEnabled);
|
||||
};
|
||||
|
||||
const syncBrain = () => { Sound.play(550, 'sine', 0.1); vscode.postMessage({ type: 'syncBrain' }); };
|
||||
document.getElementById('brainBtn').onclick = syncBrain;
|
||||
document.getElementById('inputSyncBtn').onclick = syncBrain;
|
||||
document.getElementById('historyBtn').onclick = () => vscode.postMessage({ type: 'getSessions' });
|
||||
document.getElementById('historyBtn').addEventListener('click', () => historyOverlay.classList.add('visible'));
|
||||
document.getElementById('closeHistoryBtn').onclick = () => historyOverlay.classList.remove('visible');
|
||||
modelSel.onchange = () => vscode.postMessage({ type: 'refreshModels' });
|
||||
const updateInputPlaceholder = () => {
|
||||
promptInput.placeholder = \`Ask \${modelSel.value}...\`;
|
||||
};
|
||||
|
||||
modelSel.onchange = () => {
|
||||
vscode.postMessage({ type: 'model', value: modelSel.value });
|
||||
updateInputPlaceholder();
|
||||
};
|
||||
brainSel.onchange = () => {
|
||||
if (brainSel.value === 'new') {
|
||||
vscode.postMessage({ type: 'addBrain' });
|
||||
@@ -1345,6 +1682,16 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
}
|
||||
};
|
||||
|
||||
// Handle initial state and state updates from extension
|
||||
window.addEventListener('message', e => {
|
||||
const msg = e.data;
|
||||
if (msg.type === 'configUpdate') {
|
||||
multiAgentEnabled = msg.value.multiAgentEnabled;
|
||||
document.getElementById('multiAgentBtn').classList.toggle('active', multiAgentEnabled);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
agentSel.onchange = () => {
|
||||
if (agentSel.value !== 'none') {
|
||||
vscode.postMessage({ type: 'getAgentContent', path: agentSel.value });
|
||||
@@ -1381,6 +1728,25 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
vscode.postMessage({ type: 'getModels' });
|
||||
vscode.postMessage({ type: 'getAgents' });
|
||||
vscode.postMessage({ type: 'ready' });
|
||||
|
||||
// --- Proactive Behavioral Tracking ---
|
||||
let hoverTimer = null;
|
||||
const trackBehavior = (elementId, context) => {
|
||||
const el = document.getElementById(elementId);
|
||||
if (!el) return;
|
||||
el.addEventListener('mouseenter', () => {
|
||||
hoverTimer = setTimeout(() => {
|
||||
vscode.postMessage({ type: 'proactiveTrigger', context: context });
|
||||
}, 5000); // 5 seconds threshold
|
||||
});
|
||||
el.addEventListener('mouseleave', () => {
|
||||
if (hoverTimer) clearTimeout(hoverTimer);
|
||||
});
|
||||
};
|
||||
|
||||
trackBehavior('settingsBtn', 'settings_exploration');
|
||||
trackBehavior('brainBtn', 'brain_sync_exploration');
|
||||
trackBehavior('agentSel', 'agent_selection_exploration');
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
Reference in New Issue
Block a user