feat: Add Export to MD feature, persistent agent selection, and fix export bug (v2.2.67)
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
<img src="assets/icon.png" width="120" alt="Connect AI Logo" />
|
<img src="assets/icon.png" width="120" alt="Connect AI Logo" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1 align="center">Connect AI v2 (P-Reinforce)</h1>
|
<h1 align="center">G1nation (P-Reinforce)</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<strong>100% Local · 100% Offline · Autonomous Knowledge Engine</strong><br/>
|
<strong>100% Local · 100% Offline · Autonomous Knowledge Engine</strong><br/>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://img.shields.io/badge/version-2.1.30-blue" alt="version" />
|
<img src="https://img.shields.io/badge/version-2.2.66-blue" alt="version" />
|
||||||
<img src="https://img.shields.io/badge/license-MIT-green" alt="license" />
|
<img src="https://img.shields.io/badge/license-MIT-green" alt="license" />
|
||||||
<img src="https://img.shields.io/badge/integration-Agent_University-purple" alt="integration" />
|
<img src="https://img.shields.io/badge/integration-Agent_University-purple" alt="integration" />
|
||||||
<img src="https://img.shields.io/badge/engine-Ollama%20%7C%20LM%20Studio-orange" alt="engine" />
|
<img src="https://img.shields.io/badge/engine-Ollama%20%7C%20LM%20Studio-orange" alt="engine" />
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
## 🌟 Overview: The P-Reinforce Architecture
|
## 🌟 Overview: The P-Reinforce Architecture
|
||||||
|
|
||||||
Connect AI v2.1.30은 단순한 코딩 에이전트를 넘어섭니다. **P-Reinforce 아키텍처**를 기반으로 설계된 이 에이전트는 사용자의 모든 정보와 지시를 받아들여 **스스로 의미를 분석하고, 폴더를 생성하고, 마크다운 위키 파일로 정리하여 클라우드에 자동 백업**하는 자율 지식 정원사(Autonomous Gardener)입니다.
|
G1nation v2.2.66은 단순한 코딩 에이전트를 넘어섭니다. **P-Reinforce 아키텍처**를 기반으로 설계된 이 에이전트는 사용자의 모든 정보와 지시를 받아들여 **스스로 의미를 분석하고, 폴더를 생성하고, 마크다운 위키 파일로 정리하여 클라우드에 자동 백업**하는 자율 지식 정원사(Autonomous Gardener)입니다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -37,8 +37,11 @@ Agent University 웹 플랫폼과 실시간으로 통신합니다.
|
|||||||
로컬 PC에서 파일 생성이 일어나는 순간, 에이전트가 스스로 GitHub 저장소에 `git add`, `commit`, `push`를 수행합니다.
|
로컬 PC에서 파일 생성이 일어나는 순간, 에이전트가 스스로 GitHub 저장소에 `git add`, `commit`, `push`를 수행합니다.
|
||||||
마스터는 이제 지루한 푸시 커맨드를 입력할 필요가 없습니다.
|
마스터는 이제 지루한 푸시 커맨드를 입력할 필요가 없습니다.
|
||||||
|
|
||||||
### 4. 🔗 설치형 모델 자동 감지 (Dynamic Model Detection)
|
### 4. 💾 결과물 내보내기 (Export to MD)
|
||||||
Ollama 또는 LM Studio에 설치된 모델을 내부 API(`v1/models`)를 호출하여 자동 감지하고, UI의 스위치 보드(드롭다운)에 연결합니다. 어떤 모델을 쓸지 번거롭게 입력하지 마십시오.
|
AI의 답변 결과를 클릭 한 번으로 마크다운(.md) 파일로 즉시 저장할 수 있습니다. 지식 베이스 구축이 더욱 빨라집니다.
|
||||||
|
|
||||||
|
### 5. 🔗 설치형 모델 자동 감지 (Dynamic Model Detection)
|
||||||
|
Ollama 또는 LM Studio에 설치된 모델을 내부 API(`v1/models`)를 호출하여 자동 감지하고, UI의 스위치 보드(드롭다운)에 연결합니다.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -61,7 +64,7 @@ Ollama 또는 LM Studio에 설치된 모델을 내부 API(`v1/models`)를 호출
|
|||||||
|
|
||||||
### A.U 멤버십 유저 (Recommended)
|
### A.U 멤버십 유저 (Recommended)
|
||||||
1. 상단 탭의 [Releases](https://github.com/wonseokjung/connect-ai/releases) 메뉴로 진입.
|
1. 상단 탭의 [Releases](https://github.com/wonseokjung/connect-ai/releases) 메뉴로 진입.
|
||||||
2. 최신 `v2.1.30.vsix` 파일을 다운로드.
|
2. 최신 `v2.2.66.vsix` 파일을 다운로드.
|
||||||
3. VS Code 에서 `Cmd+Shift+P` → **Extensions: Install from VSIX** → 다운받은 파일 선택
|
3. VS Code 에서 `Cmd+Shift+P` → **Extensions: Install from VSIX** → 다운받은 파일 선택
|
||||||
|
|
||||||
### 개발자 빌드 (Build from Source)
|
### 개발자 빌드 (Build from Source)
|
||||||
@@ -75,23 +78,6 @@ npx vsce package
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚙️ Engine Setup (엔진 설정 방법)
|
|
||||||
|
|
||||||
### ✅ LM Studio (Apple Silicon, Windows) - 권장
|
|
||||||
1. [lmstudio.ai](https://lmstudio.ai/) 에서 설치
|
|
||||||
2. Gemma 3, Llama 3 또는 Qwen Coder 등 원하는 모델 로드
|
|
||||||
3. **Developer 탭(좌측 `<>` 메뉴)** 진입 후 **Start Server** 클릭
|
|
||||||
4. Connect AI의 ⚙️ 채팅방 설정에서 엔진을 "LM Studio"로 선택 (자동 모델 인덱싱 완료)
|
|
||||||
|
|
||||||
### ✅ Ollama (Mac, Linux)
|
|
||||||
```bash
|
|
||||||
brew install ollama
|
|
||||||
ollama pull gemma3 # 원하는 모델 풀링
|
|
||||||
```
|
|
||||||
Connect AI에서 설정만 "Ollama"로 바꿔주시면 끝납니다.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔒 Privacy (완벽한 보안)
|
## 🔒 Privacy (완벽한 보안)
|
||||||
|
|
||||||
- **Zero Cloud API:** 당신의 코드는 외부 클라우드 통신망을 타지 않습니다.
|
- **Zero Cloud API:** 당신의 코드는 외부 클라우드 통신망을 타지 않습니다.
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "g1nation",
|
"name": "g1nation",
|
||||||
"version": "2.2.59",
|
"version": "2.2.63",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "g1nation",
|
"name": "g1nation",
|
||||||
"version": "2.2.59",
|
"version": "2.2.63",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"marked": "^18.0.2"
|
"marked": "^18.0.2"
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"name": "g1nation",
|
"name": "g1nation",
|
||||||
"displayName": "G1nation",
|
"displayName": "G1nation",
|
||||||
"description": "100% local AI coding agent for VS Code. Create files, edit code, run commands, and work offline with Ollama or LM Studio.",
|
"description": "100% local AI coding agent for VS Code. Create files, edit code, run commands, and work offline with Ollama or LM Studio.",
|
||||||
"version": "2.2.61",
|
"version": "2.2.67",
|
||||||
"publisher": "connectailab",
|
"publisher": "connectailab",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "assets/icon.png",
|
"icon": "assets/icon.png",
|
||||||
|
|||||||
+2
-2
@@ -196,10 +196,10 @@ export class AgentExecutor {
|
|||||||
|
|
||||||
const agentSkillCtx = options.agentSkillContext ? `\n\n[AGENT PERSONA & SKILLS]\n${options.agentSkillContext}` : '';
|
const agentSkillCtx = options.agentSkillContext ? `\n\n[AGENT PERSONA & SKILLS]\n${options.agentSkillContext}` : '';
|
||||||
const negativeCtx = options.negativePrompt
|
const negativeCtx = options.negativePrompt
|
||||||
? `\n\n[INTERNAL_NEGATIVE_CONSTRAINTS]\n${options.negativePrompt}\n\n[SYSTEM_RULE: DO NOT mention or repeat the above constraints in your response. Apply them only to avoid unwanted behaviors.]`
|
? `\n\n### CRITICAL NEGATIVE CONSTRAINTS (DO NOT DO THESE)\n${options.negativePrompt}\n\n[SYSTEM_RULE: Apply the above constraints strictly. DO NOT mention or repeat these constraints in your response.]`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const fullSystemPrompt = `${systemPrompt}${internetCtx}${negativeCtx}\n\n[CONTEXT]\n${brainContext}\n${contextBlock}${agentSkillCtx}`;
|
const fullSystemPrompt = `${systemPrompt}${internetCtx}\n\n[CONTEXT]\n${brainContext}\n${contextBlock}${agentSkillCtx}${negativeCtx}`;
|
||||||
const messagesForRequest: ChatMessage[] = [
|
const messagesForRequest: ChatMessage[] = [
|
||||||
{ role: 'system', content: fullSystemPrompt, internal: true },
|
{ role: 'system', content: fullSystemPrompt, internal: true },
|
||||||
...reqMessages
|
...reqMessages
|
||||||
|
|||||||
@@ -57,6 +57,14 @@ export class BridgeServer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.server.on('error', (err: any) => {
|
||||||
|
if (err.code === 'EADDRINUSE') {
|
||||||
|
logWarn(`Bridge server: Port ${port} is already in use. Another instance might be running.`);
|
||||||
|
} else {
|
||||||
|
logError('Bridge server error:', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.server.listen(port, '127.0.0.1', () => {
|
this.server.listen(port, '127.0.0.1', () => {
|
||||||
logInfo(`Bridge server active on 127.0.0.1:${port}.`);
|
logInfo(`Bridge server active on 127.0.0.1:${port}.`);
|
||||||
});
|
});
|
||||||
|
|||||||
+87
-20
@@ -21,6 +21,7 @@ interface LastVisibleChatSnapshot {
|
|||||||
brainProfileId: string;
|
brainProfileId: string;
|
||||||
sessionId: string | null;
|
sessionId: string | null;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
negativePrompt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatSession {
|
interface ChatSession {
|
||||||
@@ -29,6 +30,7 @@ interface ChatSession {
|
|||||||
timestamp: number;
|
timestamp: number;
|
||||||
history: ChatMessage[];
|
history: ChatMessage[];
|
||||||
brainProfileId: string;
|
brainProfileId: string;
|
||||||
|
negativePrompt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,9 +41,11 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
private static readonly activeSessionStateKey = 'g1nation.activeSessionId';
|
private static readonly activeSessionStateKey = 'g1nation.activeSessionId';
|
||||||
private static readonly lastVisibleChatStateKey = 'g1nation.lastVisibleChat';
|
private static readonly lastVisibleChatStateKey = 'g1nation.lastVisibleChat';
|
||||||
private static readonly blankChatStateKey = 'g1nation.blankChatActive';
|
private static readonly blankChatStateKey = 'g1nation.blankChatActive';
|
||||||
|
private static readonly lastAgentStateKey = 'g1nation.lastAgentPath';
|
||||||
private _view?: vscode.WebviewView;
|
private _view?: vscode.WebviewView;
|
||||||
public brainEnabled = true;
|
public brainEnabled = true;
|
||||||
private _currentSessionBrainId: string | null = null;
|
private _currentSessionBrainId: string | null = null;
|
||||||
|
private _currentNegativePrompt: string = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _extensionUri: vscode.Uri,
|
private readonly _extensionUri: vscode.Uri,
|
||||||
@@ -134,11 +138,23 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
await this._sendAgentContent(data.path);
|
await this._sendAgentContent(data.path);
|
||||||
break;
|
break;
|
||||||
case 'updateAgent':
|
case 'updateAgent':
|
||||||
await this._updateAgent(data.path, data.content);
|
await this._updateAgent(data.path, data.content, data.negativePrompt);
|
||||||
break;
|
break;
|
||||||
case 'refreshModels':
|
case 'refreshModels':
|
||||||
await this._sendModels();
|
await this._sendModels();
|
||||||
break;
|
break;
|
||||||
|
case 'exportResponse':
|
||||||
|
const workspacePath = vscode.workspace.workspaceFolders?.[0].uri.fsPath || '';
|
||||||
|
const defaultPath = path.join(workspacePath, 'g1_response.md');
|
||||||
|
const uri = await vscode.window.showSaveDialog({
|
||||||
|
defaultUri: vscode.Uri.file(defaultPath),
|
||||||
|
filters: { 'Markdown': ['md'] }
|
||||||
|
});
|
||||||
|
if (uri) {
|
||||||
|
await vscode.workspace.fs.writeFile(uri, Buffer.from(data.text, 'utf8'));
|
||||||
|
vscode.window.showInformationMessage(`✅ Exported to ${path.basename(uri.fsPath)}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -172,9 +188,14 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
if (snapshot?.history?.length) {
|
if (snapshot?.history?.length) {
|
||||||
this._currentSessionId = snapshot.sessionId || null;
|
this._currentSessionId = snapshot.sessionId || null;
|
||||||
this._currentSessionBrainId = snapshot.brainProfileId || getActiveBrainProfile().id;
|
this._currentSessionBrainId = snapshot.brainProfileId || getActiveBrainProfile().id;
|
||||||
|
this._currentNegativePrompt = snapshot.negativePrompt || '';
|
||||||
await this._setActiveBrainProfile(this._currentSessionBrainId, true);
|
await this._setActiveBrainProfile(this._currentSessionBrainId, true);
|
||||||
this._agent.setHistory(snapshot.history);
|
this._agent.setHistory(snapshot.history);
|
||||||
this._view.webview.postMessage({ type: 'restoreHistory', value: snapshot.history });
|
this._view.webview.postMessage({
|
||||||
|
type: 'restoreHistory',
|
||||||
|
value: snapshot.history,
|
||||||
|
negativePrompt: this._currentNegativePrompt
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +209,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
history,
|
history,
|
||||||
brainProfileId: this._currentSessionBrainId || getActiveBrainProfile().id,
|
brainProfileId: this._currentSessionBrainId || getActiveBrainProfile().id,
|
||||||
sessionId: this._currentSessionId,
|
sessionId: this._currentSessionId,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now(),
|
||||||
|
negativePrompt: this._currentNegativePrompt
|
||||||
};
|
};
|
||||||
await this._context.globalState.update(SidebarChatProvider.lastVisibleChatStateKey, snapshot);
|
await this._context.globalState.update(SidebarChatProvider.lastVisibleChatStateKey, snapshot);
|
||||||
}
|
}
|
||||||
@@ -209,7 +231,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
title,
|
title,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
history,
|
history,
|
||||||
brainProfileId
|
brainProfileId,
|
||||||
|
negativePrompt: this._currentNegativePrompt
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const idx = sessions.findIndex(s => s.id === this._currentSessionId);
|
const idx = sessions.findIndex(s => s.id === this._currentSessionId);
|
||||||
@@ -217,6 +240,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
sessions[idx].history = history;
|
sessions[idx].history = history;
|
||||||
sessions[idx].timestamp = Date.now();
|
sessions[idx].timestamp = Date.now();
|
||||||
sessions[idx].brainProfileId = brainProfileId;
|
sessions[idx].brainProfileId = brainProfileId;
|
||||||
|
sessions[idx].negativePrompt = this._currentNegativePrompt;
|
||||||
if (!sessions[idx].title || sessions[idx].title === 'New Chat') {
|
if (!sessions[idx].title || sessions[idx].title === 'New Chat') {
|
||||||
sessions[idx].title = title;
|
sessions[idx].title = title;
|
||||||
}
|
}
|
||||||
@@ -226,7 +250,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
title,
|
title,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
history,
|
history,
|
||||||
brainProfileId
|
brainProfileId,
|
||||||
|
negativePrompt: this._currentNegativePrompt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,6 +298,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
|
|
||||||
this._agent.stop();
|
this._agent.stop();
|
||||||
this._currentSessionId = id;
|
this._currentSessionId = id;
|
||||||
|
this._currentNegativePrompt = session.negativePrompt || '';
|
||||||
const sessionBrainId = session.brainProfileId || getActiveBrainProfile().id;
|
const sessionBrainId = session.brainProfileId || getActiveBrainProfile().id;
|
||||||
await this._setActiveBrainProfile(sessionBrainId, true);
|
await this._setActiveBrainProfile(sessionBrainId, true);
|
||||||
this._agent.setHistory(history);
|
this._agent.setHistory(history);
|
||||||
@@ -284,7 +310,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
value: {
|
value: {
|
||||||
id,
|
id,
|
||||||
title: session.title || 'Chat Session',
|
title: session.title || 'Chat Session',
|
||||||
history
|
history,
|
||||||
|
negativePrompt: this._currentNegativePrompt
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!skipSessionListRefresh) {
|
if (!skipSessionListRefresh) {
|
||||||
@@ -338,7 +365,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
title: String(session.title || fallbackTitle),
|
title: String(session.title || fallbackTitle),
|
||||||
timestamp: typeof session.timestamp === 'number' ? session.timestamp : Date.now(),
|
timestamp: typeof session.timestamp === 'number' ? session.timestamp : Date.now(),
|
||||||
history,
|
history,
|
||||||
brainProfileId: String(session.brainProfileId || getActiveBrainProfile().id)
|
brainProfileId: String(session.brainProfileId || getActiveBrainProfile().id),
|
||||||
|
negativePrompt: String(session.negativePrompt || '')
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((session): session is ChatSession => !!session)
|
.filter((session): session is ChatSession => !!session)
|
||||||
@@ -599,7 +627,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._view.webview.postMessage({ type: 'agentsList', value: agents });
|
const lastPath = this._context.globalState.get<string>(SidebarChatProvider.lastAgentStateKey, 'none');
|
||||||
|
this._view.webview.postMessage({ type: 'agentsList', value: agents, selected: lastPath });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _createAgent() {
|
private async _createAgent() {
|
||||||
@@ -632,14 +661,23 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
if (!this._view || !agentPath || agentPath === 'none') return;
|
if (!this._view || !agentPath || agentPath === 'none') return;
|
||||||
if (fs.existsSync(agentPath)) {
|
if (fs.existsSync(agentPath)) {
|
||||||
const content = fs.readFileSync(agentPath, 'utf8');
|
const content = fs.readFileSync(agentPath, 'utf8');
|
||||||
this._view.webview.postMessage({ type: 'agentContent', value: content });
|
const negativePrompt = this._context.globalState.get<string>(`negativePrompt:${agentPath}`, '');
|
||||||
|
this._view.webview.postMessage({
|
||||||
|
type: 'agentContent',
|
||||||
|
value: content,
|
||||||
|
negativePrompt: negativePrompt
|
||||||
|
});
|
||||||
|
await this._context.globalState.update(SidebarChatProvider.lastAgentStateKey, agentPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _updateAgent(agentPath: string, content: string) {
|
private async _updateAgent(agentPath: string, content: string, negativePrompt?: string) {
|
||||||
if (!agentPath || agentPath === 'none') return;
|
if (!agentPath || agentPath === 'none') return;
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(agentPath, content, 'utf8');
|
fs.writeFileSync(agentPath, content, 'utf8');
|
||||||
|
if (negativePrompt !== undefined) {
|
||||||
|
await this._context.globalState.update(`negativePrompt:${agentPath}`, negativePrompt);
|
||||||
|
}
|
||||||
vscode.window.showInformationMessage('Agent skill updated successfully.');
|
vscode.window.showInformationMessage('Agent skill updated successfully.');
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
vscode.window.showErrorMessage(`Failed to update agent: ${err.message}`);
|
vscode.window.showErrorMessage(`Failed to update agent: ${err.message}`);
|
||||||
@@ -650,6 +688,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
if (!this._view) return;
|
if (!this._view) return;
|
||||||
|
|
||||||
const { value, model, internet, files, agentFile, negativePrompt } = data;
|
const { value, model, internet, files, agentFile, negativePrompt } = data;
|
||||||
|
this._currentNegativePrompt = negativePrompt || '';
|
||||||
this._currentSessionBrainId = getActiveBrainProfile().id;
|
this._currentSessionBrainId = getActiveBrainProfile().id;
|
||||||
|
|
||||||
let agentSkillContext = undefined;
|
let agentSkillContext = undefined;
|
||||||
@@ -899,13 +938,16 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
.markdown-body code { font-family: 'SF Mono', monospace; font-size: 11.5px; background: rgba(175, 184, 193, 0.2); padding: 0.2em 0.4em; border-radius: 4px; }
|
.markdown-body code { font-family: 'SF Mono', monospace; font-size: 11.5px; background: rgba(175, 184, 193, 0.2); padding: 0.2em 0.4em; border-radius: 4px; }
|
||||||
|
|
||||||
/* --- UI Elements --- */
|
/* --- UI Elements --- */
|
||||||
.copy-btn {
|
.msg-actions {
|
||||||
position: absolute; top: 0; right: 0; background: var(--bg-secondary); border: 1px solid var(--border);
|
position: absolute; bottom: -12px; right: 0; display: flex; gap: 4px; opacity: 0; transition: 0.2s; z-index: 20;
|
||||||
color: var(--text-dim); padding: 4px 10px; border-radius: 6px; font-size: 10px; cursor: pointer; opacity: 0; transition: 0.2s;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
}
|
||||||
.msg:hover .copy-btn { opacity: 1; }
|
.msg:hover .msg-actions { opacity: 1; }
|
||||||
.copy-btn:hover { color: var(--text-bright); border-color: var(--accent); background: var(--accent-glow); }
|
.action-btn {
|
||||||
|
background: var(--bg-secondary); border: 1px solid var(--border);
|
||||||
|
color: var(--text-dim); padding: 4px 10px; border-radius: 6px; font-size: 10px; cursor: pointer; transition: 0.2s;
|
||||||
|
display: flex; align-items: center; gap: 4px;
|
||||||
|
}
|
||||||
|
.action-btn:hover { color: var(--text-bright); border-color: var(--accent); background: var(--accent-glow); }
|
||||||
|
|
||||||
.icon-btn {
|
.icon-btn {
|
||||||
background: var(--surface); border: 1px solid var(--border); color: var(--text-dim); width: 28px; height: 28px;
|
background: var(--surface); border: 1px solid var(--border); color: var(--text-dim); width: 28px; height: 28px;
|
||||||
@@ -1091,6 +1133,10 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
document.body.removeChild(textarea);
|
document.body.removeChild(textarea);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function exportToMD(text) {
|
||||||
|
vscode.postMessage({ type: 'exportResponse', text: text });
|
||||||
|
}
|
||||||
|
|
||||||
function addMsg(text, role) {
|
function addMsg(text, role) {
|
||||||
const isUser = role === 'user';
|
const isUser = role === 'user';
|
||||||
const msgEl = document.createElement('div');
|
const msgEl = document.createElement('div');
|
||||||
@@ -1110,12 +1156,22 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
body.innerHTML = fmt(text);
|
body.innerHTML = fmt(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const actions = document.createElement('div');
|
||||||
|
actions.className = 'msg-actions';
|
||||||
|
|
||||||
const copyBtn = document.createElement('button');
|
const copyBtn = document.createElement('button');
|
||||||
copyBtn.className = 'copy-btn'; copyBtn.innerText = '📋 Copy';
|
copyBtn.className = 'action-btn'; copyBtn.innerText = '📋 Copy';
|
||||||
copyBtn.onclick = (e) => { e.stopPropagation(); copyToClipboard(msgEl._raw, copyBtn); };
|
copyBtn.onclick = (e) => { e.stopPropagation(); copyToClipboard(msgEl._raw, copyBtn); };
|
||||||
msgEl.appendChild(copyBtn);
|
|
||||||
|
const exportBtn = document.createElement('button');
|
||||||
|
exportBtn.className = 'action-btn'; exportBtn.innerText = '💾 Export';
|
||||||
|
exportBtn.onclick = (e) => { e.stopPropagation(); exportToMD(msgEl._raw); };
|
||||||
|
|
||||||
|
actions.appendChild(copyBtn);
|
||||||
|
actions.appendChild(exportBtn);
|
||||||
|
|
||||||
msgEl.appendChild(head); msgEl.appendChild(body);
|
msgEl.appendChild(head); msgEl.appendChild(body);
|
||||||
|
msgEl.appendChild(actions);
|
||||||
chat.appendChild(msgEl); chat.scrollTop = chat.scrollHeight;
|
chat.appendChild(msgEl); chat.scrollTop = chat.scrollHeight;
|
||||||
return { body, msgEl };
|
return { body, msgEl };
|
||||||
}
|
}
|
||||||
@@ -1143,11 +1199,16 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
break;
|
break;
|
||||||
case 'restoreHistory':
|
case 'restoreHistory':
|
||||||
case 'sessionLoaded':
|
case 'sessionLoaded':
|
||||||
const history = msg.type === 'sessionLoaded' ? msg.value.history : msg.value;
|
const historyData = msg.type === 'sessionLoaded' ? msg.value : msg;
|
||||||
|
const history = Array.isArray(historyData.history) ? historyData.history : (Array.isArray(historyData) ? historyData : []);
|
||||||
|
|
||||||
if (history && history.length > 0) {
|
if (history && history.length > 0) {
|
||||||
chat.innerHTML = '';
|
chat.innerHTML = '';
|
||||||
history.forEach(m => addMsg(m.content, m.role === 'assistant' ? 'assistant' : 'user'));
|
history.forEach(m => addMsg(m.content, m.role === 'assistant' ? 'assistant' : 'user'));
|
||||||
}
|
}
|
||||||
|
if (historyData.negativePrompt !== undefined) {
|
||||||
|
negativePrompt.value = historyData.negativePrompt;
|
||||||
|
}
|
||||||
historyOverlay.classList.remove('visible');
|
historyOverlay.classList.remove('visible');
|
||||||
break;
|
break;
|
||||||
case 'clearChat':
|
case 'clearChat':
|
||||||
@@ -1196,11 +1257,16 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
agentSel.innerHTML = '<option value="none">No Agent</option>';
|
agentSel.innerHTML = '<option value="none">No Agent</option>';
|
||||||
msg.value.forEach(a => {
|
msg.value.forEach(a => {
|
||||||
const o = document.createElement('option'); o.value = a.path; o.innerText = a.name;
|
const o = document.createElement('option'); o.value = a.path; o.innerText = a.name;
|
||||||
|
if (a.path === msg.selected) o.selected = true;
|
||||||
agentSel.appendChild(o);
|
agentSel.appendChild(o);
|
||||||
});
|
});
|
||||||
|
if (msg.selected && msg.selected !== 'none') {
|
||||||
|
vscode.postMessage({ type: 'getAgentContent', path: msg.selected });
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'agentContent':
|
case 'agentContent':
|
||||||
agentPrompt.value = msg.value;
|
agentPrompt.value = msg.value;
|
||||||
|
negativePrompt.value = msg.negativePrompt || '';
|
||||||
break;
|
break;
|
||||||
case 'error':
|
case 'error':
|
||||||
thinkingBar.classList.remove('active'); sendBtn.disabled = false;
|
thinkingBar.classList.remove('active'); sendBtn.disabled = false;
|
||||||
@@ -1304,7 +1370,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
|||||||
vscode.postMessage({
|
vscode.postMessage({
|
||||||
type: 'updateAgent',
|
type: 'updateAgent',
|
||||||
path: agentSel.value,
|
path: agentSel.value,
|
||||||
content: agentPrompt.value
|
content: agentPrompt.value,
|
||||||
|
negativePrompt: negativePrompt.value.trim()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user