129 lines
5.8 KiB
TypeScript
129 lines
5.8 KiB
TypeScript
import * as vscode from 'vscode';
|
|
import * as path from 'path';
|
|
import { SidebarChatProvider } from '../sidebarProvider';
|
|
import { getActiveBrainProfile, logInfo } from '../utils';
|
|
import { pickConfigTarget } from '../lib/paths';
|
|
|
|
/**
|
|
* Handles chat-domain messages: prompts, model selection, sessions, streaming control,
|
|
* generic webview transport (export, settings, addMessage), action approvals, and the
|
|
* cross-cutting `ready` bootstrap.
|
|
*
|
|
* Returns true when the message was handled by this domain, false otherwise — the
|
|
* caller chains domain handlers until one accepts the message.
|
|
*/
|
|
export async function handleChatMessage(provider: SidebarChatProvider, data: any): Promise<boolean> {
|
|
switch (data.type) {
|
|
case 'prompt':
|
|
case 'promptWithFile':
|
|
provider._lmStudio?.activity.bump();
|
|
await provider._context.globalState.update(SidebarChatProvider.blankChatStateKey, false);
|
|
await provider._handlePrompt(data);
|
|
await provider._autoWriteChronicleAfterPrompt();
|
|
await provider._saveCurrentSession();
|
|
return true;
|
|
case 'activity':
|
|
provider._lmStudio?.activity.bump();
|
|
return true;
|
|
case 'ready':
|
|
await provider._sendBrainStatus();
|
|
await provider._sendBrainProfiles();
|
|
await provider._sendSessionList();
|
|
await provider._sendModels();
|
|
await provider._sendChronicleProjects();
|
|
await provider._restoreActiveSessionIntoView();
|
|
await provider._sendReadyStatus();
|
|
return true;
|
|
case 'getReadyStatus':
|
|
await provider._sendReadyStatus();
|
|
return true;
|
|
case 'createLessonFromConversation':
|
|
await vscode.commands.executeCommand('g1nation.lesson.fromConversation');
|
|
return true;
|
|
case 'manageLessons':
|
|
await vscode.commands.executeCommand('g1nation.lesson.manage');
|
|
return true;
|
|
case 'getModels':
|
|
await provider._sendModels();
|
|
return true;
|
|
case 'getSessions':
|
|
await provider._sendSessionList();
|
|
return true;
|
|
case 'newChat':
|
|
provider._currentSessionId = null;
|
|
provider._currentSessionBrainId = getActiveBrainProfile().id;
|
|
provider._agent.resetConversation();
|
|
await provider._context.globalState.update(SidebarChatProvider.activeSessionStateKey, null);
|
|
await provider._context.globalState.update(SidebarChatProvider.lastVisibleChatStateKey, null);
|
|
await provider._context.globalState.update(SidebarChatProvider.blankChatStateKey, true);
|
|
provider.clearChat();
|
|
await provider._sendBrainStatus();
|
|
return true;
|
|
case 'stopGeneration':
|
|
provider._agent.stop();
|
|
return true;
|
|
case 'loadSession':
|
|
await provider._loadSession(data.id);
|
|
return true;
|
|
case 'deleteSession':
|
|
await provider._deleteSession(data.id);
|
|
return true;
|
|
case 'openSettings':
|
|
// Route the sidebar gear button to Astra's own settings webview.
|
|
// Falls back to VS Code Settings if the view hasn't registered yet
|
|
// (e.g. during the very first activation pass) and surfaces any
|
|
// unexpected error so the user isn't stuck with a silent button.
|
|
try {
|
|
await vscode.commands.executeCommand('g1nation.settings.focus');
|
|
} catch (e: any) {
|
|
logInfo('openSettings: settings.focus failed, falling back to VS Code Settings.', { error: e?.message ?? String(e) });
|
|
try {
|
|
await vscode.commands.executeCommand('workbench.action.openSettings', 'g1nation');
|
|
} catch (e2: any) {
|
|
vscode.window.showErrorMessage(`Astra Settings 열기 실패: ${e2?.message ?? e2}`);
|
|
}
|
|
}
|
|
return true;
|
|
case 'addMessage':
|
|
provider._view?.webview.postMessage({ type: 'addMessage', role: data.role, value: data.value, rationale: data.rationale });
|
|
return true;
|
|
case 'refreshModels':
|
|
await provider._sendModels(true);
|
|
return true;
|
|
case 'model': {
|
|
// Write to whichever scope already holds the value so a stale
|
|
// Workspace override doesn't shadow our Global update — that was
|
|
// the "sidebar shows e2b but Settings shows e4b" desync.
|
|
const { target } = pickConfigTarget('g1nation', 'defaultModel');
|
|
await vscode.workspace.getConfiguration('g1nation').update('defaultModel', data.value, target);
|
|
logInfo(`Default model updated to: ${data.value}`, { target });
|
|
provider._lmStudio?.lifecycle.onModelSelected(data.value);
|
|
return true;
|
|
}
|
|
case 'proactiveTrigger':
|
|
await provider._handleProactiveSuggestion(data.context);
|
|
return true;
|
|
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)}`);
|
|
}
|
|
return true;
|
|
}
|
|
case 'approveAction':
|
|
await provider._agent.approveTransaction();
|
|
return true;
|
|
case 'rejectAction':
|
|
await provider._agent.rejectTransaction();
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|