sam-coder-cli 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2098 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.AIAssistantViewProvider = void 0;
27
- const vscode = __importStar(require("vscode"));
28
- const fetch_polyfill_1 = require("./fetch-polyfill");
29
- const agentUtils_1 = require("./agentUtils");
30
- // Default API key to use if none is provided in settings
31
- const DEFAULT_API_KEY = 'fw_3ZM5QnSBpeAvHmRG6qB1FWCm';
32
- const translations = {
33
- 'en': {
34
- welcomeMessage: "Hello! I'm Samantha Coder, your AI assistant. Ask me anything or request help with your code. I can analyze and modify files in your workspace.",
35
- workingMessage: "🤖 I'm working on this task...",
36
- taskCompletedMessage: "✅ Task completed.",
37
- errorMessage: "Sorry, I encountered an error. Please check your API key and connection.",
38
- inputPlaceholder: "Ask something...",
39
- sendButton: "Send",
40
- clearButton: "Clear",
41
- changeLanguageButton: "PT-BR",
42
- loadingText: "Working on it..."
43
- },
44
- 'pt-br': {
45
- welcomeMessage: "Olá! Eu sou Samantha Coder, sua assistente de IA. Pergunte qualquer coisa ou peça ajuda com seu código. Posso analisar e modificar arquivos no seu espaço de trabalho.",
46
- workingMessage: "🤖 Estou trabalhando nesta tarefa...",
47
- taskCompletedMessage: "✅ Tarefa concluída.",
48
- errorMessage: "Desculpe, encontrei um erro. Por favor, verifique sua chave de API e conexão.",
49
- inputPlaceholder: "Pergunte algo...",
50
- sendButton: "Enviar",
51
- clearButton: "Limpar",
52
- changeLanguageButton: "EN",
53
- loadingText: "Trabalhando nisso..."
54
- }
55
- };
56
- class AIAssistantViewProvider {
57
- constructor(_extensionUri) {
58
- this._extensionUri = _extensionUri;
59
- this._chats = [];
60
- this._currentChatId = null;
61
- this._isProcessingAgentActions = false;
62
- this._lastEditedFile = null;
63
- this._lastEditTime = 0;
64
- this._language = 'en';
65
- this._conversationHistory = {}; // Store state snapshots
66
- this._trackedFiles = new Set();
67
- this._abortController = null; // For canceling fetch requests
68
- this._isGeneratingResponse = false; // Flag to track if a response is being generated
69
- this._agentUtils = new agentUtils_1.AgentUtils();
70
- // Load language setting
71
- const config = vscode.workspace.getConfiguration('aiAssistant');
72
- this._language = config.get('language') || 'en';
73
- // Track language setting changes
74
- vscode.workspace.onDidChangeConfiguration(e => {
75
- if (e.affectsConfiguration('aiAssistant.language')) {
76
- const config = vscode.workspace.getConfiguration('aiAssistant');
77
- this._language = config.get('language') || 'en';
78
- this._updateWebviewContent();
79
- }
80
- });
81
- // Track file edits
82
- vscode.workspace.onDidChangeTextDocument(e => {
83
- this._lastEditedFile = e.document.uri.fsPath;
84
- this._lastEditTime = Date.now();
85
- this._trackedFiles.add(e.document.uri.fsPath);
86
- });
87
- // Track file system changes
88
- vscode.workspace.onDidCreateFiles(e => {
89
- e.files.forEach(uri => {
90
- this._trackedFiles.add(uri.fsPath);
91
- });
92
- });
93
- // Initialize with default chat
94
- this._ensureDefaultChat();
95
- }
96
- // Ensure there's at least one chat
97
- _ensureDefaultChat() {
98
- if (this._chats.length === 0) {
99
- this._createNewChat();
100
- }
101
- }
102
- // Create a new chat
103
- _createNewChat(title) {
104
- const id = Date.now().toString();
105
- const now = new Date().toISOString();
106
- const newChat = {
107
- id,
108
- title: title || `Chat ${this._chats.length + 1}`,
109
- messages: [],
110
- createdAt: now,
111
- updatedAt: now
112
- };
113
- this._chats.push(newChat);
114
- this._currentChatId = id;
115
- // Update the webview if it exists
116
- if (this._view) {
117
- this._postMessageToWebview({
118
- type: 'updateChats',
119
- chats: this._chats,
120
- currentChatId: this._currentChatId
121
- });
122
- }
123
- return id;
124
- }
125
- // Get the current chat
126
- _getCurrentChat() {
127
- // Ensure we have at least one chat
128
- this._ensureDefaultChat();
129
- if (!this._currentChatId && this._chats.length > 0) {
130
- this._currentChatId = this._chats[0].id;
131
- }
132
- return this._chats.find(chat => chat.id === this._currentChatId);
133
- }
134
- // Filter out system messages for display
135
- _getMessagesForDisplay(messages) {
136
- return messages.filter(msg => msg.role !== 'system');
137
- }
138
- // Switch to a different chat
139
- _switchChat(chatId) {
140
- const chat = this._chats.find(c => c.id === chatId);
141
- if (chat) {
142
- this._currentChatId = chatId;
143
- // Update the webview with visible messages only
144
- this._postMessageToWebview({
145
- type: 'switchChat',
146
- chatId,
147
- messages: this._getMessagesForDisplay(chat.messages)
148
- });
149
- // Update chat list to reflect new active chat
150
- this._postMessageToWebview({
151
- type: 'updateChats',
152
- chats: this._chats,
153
- currentChatId: this._currentChatId
154
- });
155
- }
156
- }
157
- // Rename a chat
158
- _renameChat(chatId, newTitle) {
159
- const chat = this._chats.find(c => c.id === chatId);
160
- if (chat) {
161
- chat.title = newTitle;
162
- chat.updatedAt = new Date().toISOString();
163
- // Update the webview
164
- this._postMessageToWebview({
165
- type: 'updateChats',
166
- chats: this._chats,
167
- currentChatId: this._currentChatId
168
- });
169
- }
170
- }
171
- // Delete a chat
172
- _deleteChat(chatId) {
173
- const index = this._chats.findIndex(c => c.id === chatId);
174
- if (index !== -1) {
175
- // Store reference to the chat we're deleting
176
- const deletedChat = this._chats[index];
177
- // Remove the chat
178
- this._chats.splice(index, 1);
179
- // Remove all state snapshots associated with this chat's messages
180
- if (deletedChat && deletedChat.messages) {
181
- deletedChat.messages.forEach(msg => {
182
- if (msg.timestamp && this._conversationHistory[msg.timestamp]) {
183
- delete this._conversationHistory[msg.timestamp];
184
- }
185
- });
186
- }
187
- // If we deleted the current chat, switch to another one or create a new one
188
- if (this._currentChatId === chatId) {
189
- if (this._chats.length > 0) {
190
- this._currentChatId = this._chats[0].id;
191
- // Get the new current chat
192
- const newCurrentChat = this._getCurrentChat();
193
- // Update the webview with the new chat's messages
194
- if (newCurrentChat && this._view) {
195
- this._postMessageToWebview({
196
- type: 'switchChat',
197
- chatId: this._currentChatId,
198
- messages: this._getMessagesForDisplay(newCurrentChat.messages)
199
- });
200
- }
201
- }
202
- else {
203
- // Create a new chat if no chats remain
204
- this._createNewChat();
205
- // Get the new chat
206
- const newChat = this._getCurrentChat();
207
- // Update the webview with the new chat
208
- if (newChat && this._view) {
209
- this._postMessageToWebview({
210
- type: 'switchChat',
211
- chatId: this._currentChatId,
212
- messages: this._getMessagesForDisplay(newChat.messages)
213
- });
214
- }
215
- }
216
- }
217
- // Update the webview's chat list
218
- this._postMessageToWebview({
219
- type: 'updateChats',
220
- chats: this._chats,
221
- currentChatId: this._currentChatId
222
- });
223
- }
224
- }
225
- _updateSystemMessage(chat) {
226
- // Remove existing system message
227
- chat.messages = chat.messages.filter(msg => msg.role !== 'system');
228
- // Get workspace info
229
- const workspaceFolders = vscode.workspace.workspaceFolders || [];
230
- const workspaceInfo = workspaceFolders.map(folder => folder.uri.fsPath).join(', ');
231
- // Add updated system message
232
- chat.messages.push({
233
- role: 'system',
234
- content: `You are a VS Code AI Assistant with agency capabilities. You can perform actions on the user's workspace.
235
-
236
- ENVIRONMENT CONTEXT:
237
- - OS: ${process.platform}
238
- - Workspace: ${workspaceInfo || 'No workspace open'}
239
- - Last edited file: ${this._lastEditedFile || 'None'}
240
- ${this._lastEditedFile ? `- Last edit: ${new Date(this._lastEditTime).toLocaleTimeString()}` : ''}
241
-
242
- When you need to perform actions, respond with JSON in the following format:
243
- \`\`\`json
244
- {
245
- "thoughts": "Your reasoning about what needs to be done",
246
- "actions": [
247
- {
248
- "type": "read|write|search|command|analyze|execute|stop",
249
- "data": { ... action specific data ... }
250
- }
251
- ]
252
- }
253
- \`\`\`
254
-
255
- Action types and their data:
256
- - read: { "path": "relative/or/absolute/path" }
257
- - write: { "path": "relative/or/absolute/path", "content": "file content" }
258
- - search: { "type": "files", "pattern": "glob pattern" } or { "type": "text", "text": "search text" }
259
- - command: { "command": "command string to execute in terminal" }
260
- - execute: { "language": "js|python|bash|...", "code": "code to execute" }
261
- - analyze: { "code": "code to analyze", "question": "what you want to analyze" }
262
- - browse: { "query": "search query", "numResults": 5 } (free web search using DuckDuckGo, optional numResults)
263
- - edit: {
264
- "path": "relative/or/absolute/path",
265
- "edits": {
266
- "operations": [
267
- { "type": "replace", "startLine": 10, "endLine": 15, "newText": "new code here" },
268
- { "type": "replace", "pattern": "oldFunction\\(\\)", "replacement": "newFunction()", "flags": "g" },
269
- { "type": "insert", "line": 20, "text": "new line of code here" },
270
- { "type": "insert", "position": "start", "text": "// Header comment" },
271
- { "type": "insert", "position": "end", "text": "// Footer comment" },
272
- { "type": "delete", "startLine": 25, "endLine": 30 }
273
- ]
274
- }
275
- } (edit specific parts of an existing file)
276
- - stop: {} (use this to indicate you're done with the task and no more actions are needed)
277
-
278
- CONTEXTUAL UNDERSTANDING:
279
- Before every response, I will automatically gather information about the user's current context, including:
280
- - Currently open files
281
- - Current editor selection
282
- - Recent edits
283
- - Terminal output (if available)
284
-
285
- By default, you will continue to take actions in a loop until you decide to stop with the 'stop' action type.
286
- Always wrap your JSON in markdown code blocks with the json language specifier.
287
- When executing code or commands that might be potentially harmful, explain what the code does before executing it.
288
- `,
289
- timestamp: new Date().toISOString()
290
- });
291
- }
292
- async gatherUserContext() {
293
- try {
294
- let context = "";
295
- // Get active editor and document info
296
- const editor = vscode.window.activeTextEditor;
297
- if (editor) {
298
- const document = editor.document;
299
- const selection = editor.selection;
300
- const selectedText = document.getText(selection);
301
- context += `ACTIVE EDITOR:\n`;
302
- context += `- File: ${document.uri.fsPath}\n`;
303
- context += `- Language: ${document.languageId}\n`;
304
- if (!selection.isEmpty) {
305
- context += `- Selection (Lines ${selection.start.line + 1}-${selection.end.line + 1}):\n`;
306
- context += "```\n" + selectedText + "\n```\n";
307
- }
308
- else {
309
- const lineCount = document.lineCount;
310
- const visibleRange = editor.visibleRanges[0];
311
- context += `- Visible Range: Lines ${visibleRange.start.line + 1}-${visibleRange.end.line + 1} of ${lineCount}\n`;
312
- // Get a few lines around the cursor to provide context
313
- const cursorPos = selection.active;
314
- const startLine = Math.max(0, cursorPos.line - 3);
315
- const endLine = Math.min(lineCount - 1, cursorPos.line + 3);
316
- const contextLines = [];
317
- for (let i = startLine; i <= endLine; i++) {
318
- const line = document.lineAt(i);
319
- contextLines.push(`${i === cursorPos.line ? '> ' : ' '}${line.text}`);
320
- }
321
- context += `- Code around cursor (Line ${cursorPos.line + 1}):\n`;
322
- context += "```\n" + contextLines.join('\n') + "\n```\n";
323
- }
324
- }
325
- else {
326
- context += "No active editor\n";
327
- }
328
- // Get information about open editors
329
- const openEditors = vscode.window.visibleTextEditors.map(editor => {
330
- return {
331
- path: editor.document.uri.fsPath,
332
- viewColumn: editor.viewColumn
333
- };
334
- });
335
- if (openEditors.length > 0) {
336
- context += `\nOPEN EDITORS:\n`;
337
- openEditors.forEach(editor => {
338
- context += `- ${editor.path}\n`;
339
- });
340
- }
341
- // Recent edits
342
- if (this._lastEditedFile) {
343
- context += `\nRECENT EDITS:\n`;
344
- context += `- Last modified: ${this._lastEditedFile} at ${new Date(this._lastEditTime).toLocaleTimeString()}\n`;
345
- }
346
- // Terminal output
347
- try {
348
- const terminalOutput = await this._agentUtils.getTerminalOutput();
349
- if (terminalOutput) {
350
- context += `\nTERMINAL:\n${terminalOutput}\n`;
351
- }
352
- }
353
- catch (error) {
354
- console.error('Error getting terminal output:', error);
355
- }
356
- // Current extensions
357
- try {
358
- const extensions = vscode.extensions.all
359
- .filter(ext => ext.isActive && !ext.packageJSON.isBuiltin)
360
- .map(ext => `${ext.packageJSON.displayName || ext.packageJSON.name} (${ext.packageJSON.version})`);
361
- if (extensions.length > 0) {
362
- context += `\nACTIVE EXTENSIONS (top 5):\n`;
363
- extensions.slice(0, 5).forEach(ext => {
364
- context += `- ${ext}\n`;
365
- });
366
- }
367
- }
368
- catch (error) {
369
- console.error('Error getting extensions:', error);
370
- }
371
- return context;
372
- }
373
- catch (error) {
374
- console.error('Error gathering context:', error);
375
- return "Error gathering context";
376
- }
377
- }
378
- ensureWebviewIsVisible() {
379
- try {
380
- if (!this._view) {
381
- // Try to show the view if not already visible
382
- vscode.commands.executeCommand('workbench.view.extension.ai-assistant-view');
383
- vscode.commands.executeCommand('aiAssistantView.focus');
384
- }
385
- else {
386
- // If view exists but might not be visible, try to show it
387
- this._view.show(true);
388
- }
389
- }
390
- catch (error) {
391
- console.error('Error ensuring webview is visible:', error);
392
- }
393
- }
394
- resolveWebviewView(webviewView, _context, _token) {
395
- this._view = webviewView;
396
- if (!this._extensionUri) {
397
- console.error('Extension URI is undefined in resolveWebviewView');
398
- return;
399
- }
400
- // Ensure default chat exists
401
- this._ensureDefaultChat();
402
- webviewView.webview.options = {
403
- enableScripts: true,
404
- localResourceRoots: [
405
- vscode.Uri.joinPath(this._extensionUri, 'media')
406
- ]
407
- };
408
- webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
409
- webviewView.webview.onDidReceiveMessage(async (data) => {
410
- switch (data.type) {
411
- case 'sendQuery':
412
- await this.sendQueryToAI(data.value);
413
- break;
414
- case 'clearConversation':
415
- this.clearConversation();
416
- break;
417
- case 'toggleLanguage':
418
- this.toggleLanguage();
419
- break;
420
- case 'restoreState':
421
- await this.restoreToState(data.messageId);
422
- break;
423
- case 'createNewChat':
424
- this._createNewChat(data.title);
425
- break;
426
- case 'switchChat':
427
- this._switchChat(data.chatId);
428
- break;
429
- case 'renameChat':
430
- this._renameChat(data.chatId, data.newTitle);
431
- break;
432
- case 'deleteChat':
433
- this._deleteChat(data.chatId);
434
- break;
435
- case 'getConfiguration':
436
- this._getConfiguration();
437
- break;
438
- case 'saveConfiguration':
439
- this._saveConfiguration(data.modelName, data.apiBaseUrl, data.apiKey, data.maxTokens);
440
- break;
441
- }
442
- });
443
- // Ensure a default chat exists
444
- this._ensureDefaultChat();
445
- // Send initial chat data to the webview
446
- this._postMessageToWebview({
447
- type: 'updateChats',
448
- chats: this._chats,
449
- currentChatId: this._currentChatId
450
- });
451
- // Get current chat messages (if any)
452
- const currentChat = this._getCurrentChat();
453
- if (currentChat) {
454
- this._postMessageToWebview({
455
- type: 'restoreConversation',
456
- messages: this._getMessagesForDisplay(currentChat.messages)
457
- });
458
- }
459
- // Show the view by focusing on it
460
- if (!webviewView.visible) {
461
- webviewView.show();
462
- }
463
- }
464
- async sendQueryToAI(query) {
465
- if (!this._view) {
466
- this.ensureWebviewIsVisible();
467
- return;
468
- }
469
- // Don't start a new query if we're already generating a response
470
- if (this._isGeneratingResponse) {
471
- vscode.window.showInformationMessage('Already processing a request. Please wait or clear the conversation.');
472
- return;
473
- }
474
- // Get the current chat or create a new one if none exists
475
- let currentChat = this._getCurrentChat();
476
- if (!currentChat) {
477
- this._createNewChat();
478
- currentChat = this._getCurrentChat();
479
- }
480
- if (!currentChat) {
481
- console.error('Failed to get or create a chat');
482
- return;
483
- }
484
- // Add user message to conversation
485
- const userMessage = {
486
- role: 'user',
487
- content: query,
488
- timestamp: new Date().toISOString()
489
- };
490
- currentChat.messages.push(userMessage);
491
- currentChat.updatedAt = new Date().toISOString();
492
- // Take a snapshot of the current state before processing
493
- await this._takeStateSnapshot(userMessage.timestamp);
494
- // Update the webview with the new message
495
- await this._postMessageToWebview({
496
- type: 'addMessage',
497
- message: userMessage
498
- });
499
- try {
500
- // Gather current context before sending query
501
- const userContext = await this.gatherUserContext();
502
- // Update the system message with fresh context
503
- this._updateSystemMessage(currentChat);
504
- // Update the system message to include a timestamp
505
- currentChat.messages.push({
506
- role: 'system',
507
- content: `Current user context:\n${userContext}`,
508
- timestamp: new Date().toISOString()
509
- });
510
- // Show loading indicator
511
- await this._postMessageToWebview({ type: 'setLoading', isLoading: true });
512
- // Get AI response
513
- await this._getAIResponse(currentChat);
514
- }
515
- catch (error) {
516
- console.error('Error in sendQueryToAI:', error);
517
- vscode.window.showErrorMessage(`Error: ${error instanceof Error ? error.message : String(error)}`);
518
- await this._postMessageToWebview({
519
- type: 'addMessage',
520
- message: {
521
- role: 'assistant',
522
- content: translations[this._language].errorMessage,
523
- timestamp: new Date().toISOString()
524
- }
525
- });
526
- }
527
- finally {
528
- // Hide loading indicator
529
- await this._postMessageToWebview({ type: 'setLoading', isLoading: false });
530
- }
531
- }
532
- // Helper method to safely post messages to the webview
533
- async _postMessageToWebview(message) {
534
- if (this._view?.webview) {
535
- try {
536
- await this._view.webview.postMessage(message);
537
- }
538
- catch (error) {
539
- console.error('Error posting message to webview:', error);
540
- }
541
- }
542
- }
543
- async _getAIResponse(chat) {
544
- try {
545
- if (!this._view) {
546
- return;
547
- }
548
- // Set the generating flag
549
- this._isGeneratingResponse = true;
550
- // Create a new AbortController for this request
551
- this._abortController = new AbortController();
552
- // Get API key from settings or use default
553
- const config = vscode.workspace.getConfiguration('aiAssistant');
554
- let apiKey = config.get('apiKey');
555
- // If no API key in settings, use the default one
556
- if (!apiKey || apiKey.trim() === '') {
557
- apiKey = DEFAULT_API_KEY;
558
- }
559
- const model = config.get('model') || "accounts/fireworks/models/deepseek-v3-0324";
560
- const apiBaseUrl = config.get('apiBaseUrl') || "https://api.fireworks.ai/inference/v1";
561
- const maxTokens = config.get('maxTokens') || 120000;
562
- // Strip out timestamp field from messages before sending to API
563
- const apiMessages = chat.messages.map(msg => ({
564
- role: msg.role,
565
- content: msg.content
566
- }));
567
- // Create request options
568
- const options = {
569
- method: "POST",
570
- headers: {
571
- "Accept": "application/json",
572
- "Content-Type": "application/json",
573
- "Authorization": `Bearer ${apiKey}`
574
- },
575
- body: JSON.stringify({
576
- model: model,
577
- max_tokens: maxTokens,
578
- top_p: 1,
579
- top_k: 40,
580
- presence_penalty: 0,
581
- frequency_penalty: 0,
582
- temperature: 0.6,
583
- messages: apiMessages
584
- })
585
- };
586
- // Add abort signal if available
587
- if (this._abortController) {
588
- options.signal = this._abortController.signal;
589
- }
590
- const response = await (0, fetch_polyfill_1.fetch)(`${apiBaseUrl}/chat/completions`, options);
591
- // Check if the request was aborted
592
- if (this._abortController && this._abortController.signal.aborted) {
593
- this._isGeneratingResponse = false;
594
- return;
595
- }
596
- if (!response.ok) {
597
- const errorData = await response.json().catch(() => null);
598
- let errorMessage = `API request failed: ${response.statusText}`;
599
- if (errorData?.error?.message) {
600
- errorMessage = `API Error: ${errorData.error.message}`;
601
- }
602
- else if (response.status === 401) {
603
- errorMessage = 'Invalid API key. Please check your API key in settings.';
604
- }
605
- else if (response.status === 429) {
606
- errorMessage = 'Rate limit exceeded. Please try again later.';
607
- }
608
- else if (response.status >= 500) {
609
- errorMessage = 'Server error. Please try again later.';
610
- }
611
- throw new Error(errorMessage);
612
- }
613
- const data = await response.json();
614
- if (!data?.choices?.[0]?.message?.content) {
615
- throw new Error('Invalid response format from API');
616
- }
617
- const assistantMessage = data.choices[0].message.content;
618
- // Update the agent working message to include a timestamp
619
- const timestamp = new Date().toISOString();
620
- await this._postMessageToWebview({
621
- type: 'addMessage',
622
- message: {
623
- role: 'assistant',
624
- content: 'Thinking...',
625
- timestamp
626
- }
627
- });
628
- // Update the agent response message to include a timestamp
629
- chat.messages.push({
630
- role: 'assistant',
631
- content: assistantMessage,
632
- timestamp
633
- });
634
- // Update UI with agent response
635
- await this._postMessageToWebview({
636
- type: 'addMessage',
637
- message: {
638
- role: 'assistant',
639
- content: assistantMessage,
640
- timestamp
641
- }
642
- });
643
- // Check if the message contains agent actions
644
- await this._processAgentActions(assistantMessage, chat);
645
- }
646
- catch (error) {
647
- console.error('Error calling Fireworks API:', error);
648
- // Show a more specific error message in the chat
649
- const errorMessage = error instanceof Error ? error.message : String(error);
650
- await this._postMessageToWebview({
651
- type: 'addMessage',
652
- message: {
653
- role: 'assistant',
654
- content: `❌ ${errorMessage}`,
655
- timestamp: new Date().toISOString()
656
- }
657
- });
658
- throw error; // Rethrow to be handled by caller
659
- }
660
- finally {
661
- this._isGeneratingResponse = false;
662
- if (this._view) {
663
- this._view.webview.postMessage({ type: 'setLoading', isLoading: false });
664
- }
665
- }
666
- }
667
- _updateWebviewContent() {
668
- if (this._view) {
669
- this._view.webview.html = this._getHtmlForWebview(this._view.webview);
670
- // Update welcome message
671
- this._postMessageToWebview({
672
- type: 'updateLanguage',
673
- translations: translations[this._language]
674
- });
675
- }
676
- }
677
- async _processAgentActions(message, chat) {
678
- // If already processing actions, don't start another process
679
- if (this._isProcessingAgentActions) {
680
- return;
681
- }
682
- try {
683
- this._isProcessingAgentActions = true;
684
- // First try to extract JSON from markdown code blocks
685
- const jsonMatch = message.match(/```json\s*([\s\S]*?)\s*```/);
686
- if (jsonMatch) {
687
- // Process JSON from code block
688
- const jsonContent = jsonMatch[1].trim();
689
- this._agentUtils.log(`Extracted JSON from markdown: ${jsonContent ? jsonContent.substring(0, 100) + '...' : 'empty content'}`);
690
- try {
691
- const actionData = JSON.parse(jsonContent);
692
- this._agentUtils.log(`Successfully parsed JSON from code block`);
693
- // Check if processing was cancelled
694
- if (!this._isProcessingAgentActions) {
695
- return;
696
- }
697
- await this._processJsonActions(actionData, chat);
698
- }
699
- catch (parseError) {
700
- console.error('Error parsing JSON from code block:', parseError);
701
- this._agentUtils.log(`Error parsing JSON from code block: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
702
- }
703
- }
704
- else {
705
- // Try extracting without code block markers
706
- try {
707
- const jsonData = JSON.parse(message);
708
- if (jsonData && jsonData.actions && Array.isArray(jsonData.actions)) {
709
- this._agentUtils.log(`Found JSON without code block markers`);
710
- // Check if processing was cancelled
711
- if (!this._isProcessingAgentActions) {
712
- return;
713
- }
714
- await this._processJsonActions(jsonData, chat);
715
- }
716
- else {
717
- this._agentUtils.log(`Parsed JSON but no valid actions array found`);
718
- }
719
- }
720
- catch (e) {
721
- // Not valid JSON
722
- this._agentUtils.log(`No JSON code blocks found and content is not valid JSON`);
723
- }
724
- }
725
- }
726
- catch (error) {
727
- console.error('Error processing agent actions:', error);
728
- this._agentUtils.log(`Error processing agent actions: ${error instanceof Error ? error.message : String(error)}`);
729
- if (this._view) {
730
- this._view.webview.postMessage({
731
- type: 'addMessage',
732
- message: {
733
- role: 'assistant',
734
- content: `❌ ${error instanceof Error ? error.message : String(error)}`,
735
- timestamp: new Date().toISOString()
736
- }
737
- });
738
- }
739
- }
740
- finally {
741
- this._isProcessingAgentActions = false;
742
- if (this._view) {
743
- this._view.webview.postMessage({ type: 'setLoading', isLoading: false });
744
- }
745
- }
746
- }
747
- async _processJsonActions(actionData, chat) {
748
- if (!actionData.actions || !Array.isArray(actionData.actions)) {
749
- this._agentUtils.log(`No valid actions array found in JSON`);
750
- return; // No valid actions array
751
- }
752
- // Check if there's a "stop" action which should halt the agent after executing all actions
753
- const hasStopAction = actionData.actions.some((action) => action.type === 'stop');
754
- // Show that the agent is working
755
- this._agentUtils.showOutputChannel();
756
- this._agentUtils.log(`Processing ${actionData.actions.length} agent actions...`);
757
- this._agentUtils.log(`Thoughts: ${actionData.thoughts || 'No thoughts provided'}`);
758
- // Log each action for debugging
759
- actionData.actions.forEach((action, index) => {
760
- const actionDataStr = action.data ?
761
- (typeof action.data === 'string' ?
762
- action.data.substring(0, 150) :
763
- JSON.stringify(action.data || {}).substring(0, 150)) :
764
- 'undefined';
765
- this._agentUtils.log(`Action ${index + 1}: type=${action.type}, data=${actionDataStr}${actionDataStr && actionDataStr.length > 149 ? '...' : ''}`);
766
- });
767
- if (this._view) {
768
- this._view.webview.postMessage({
769
- type: 'addMessage',
770
- message: {
771
- role: 'assistant',
772
- content: translations[this._language].workingMessage,
773
- timestamp: new Date().toISOString()
774
- }
775
- });
776
- this._view.webview.postMessage({ type: 'setLoading', isLoading: true });
777
- }
778
- // Execute actions
779
- try {
780
- // Check if processing was cancelled before executing actions
781
- if (!this._isProcessingAgentActions) {
782
- return;
783
- }
784
- const actionResults = await this._agentUtils.executeActions(actionData.actions);
785
- // Check again if processing was cancelled after executing actions
786
- if (!this._isProcessingAgentActions) {
787
- return;
788
- }
789
- this._agentUtils.log(`Actions executed successfully`);
790
- // Log results for debugging
791
- actionResults.forEach((result, index) => {
792
- let resultStr = 'undefined';
793
- if (result.result) {
794
- if (typeof result.result === 'string') {
795
- resultStr = result.result.substring(0, 100);
796
- }
797
- else {
798
- try {
799
- resultStr = JSON.stringify(result.result).substring(0, 100);
800
- }
801
- catch (e) {
802
- resultStr = '[Object cannot be stringified]';
803
- }
804
- }
805
- }
806
- this._agentUtils.log(`Result ${index + 1}: ${resultStr}${resultStr.length >= 100 ? '...' : ''}`);
807
- });
808
- // If this batch included a stop action, stop after all actions complete
809
- if (hasStopAction) {
810
- this._agentUtils.log(`Stop action detected, halting after execution`);
811
- if (this._view) {
812
- this._view.webview.postMessage({
813
- type: 'addMessage',
814
- message: {
815
- role: 'assistant',
816
- content: translations[this._language].taskCompletedMessage,
817
- timestamp: new Date().toISOString()
818
- }
819
- });
820
- }
821
- return;
822
- }
823
- // Check if processing was cancelled before continuing
824
- if (!this._isProcessingAgentActions) {
825
- return;
826
- }
827
- // Format results to send back to AI
828
- const resultsForAI = JSON.stringify(actionResults, null, 2);
829
- // Update context after actions completed
830
- const updatedContext = await this.gatherUserContext();
831
- // First add context as a system message
832
- chat.messages.push({
833
- role: 'system',
834
- content: `Updated context after actions:\n${updatedContext}`,
835
- timestamp: new Date().toISOString()
836
- });
837
- // Add action results as a system message to the conversation (not shown in UI)
838
- chat.messages.push({
839
- role: 'system',
840
- content: `The assistant has completed the actions. Here are the results:\n\`\`\`json\n${resultsForAI}\n\`\`\`\n
841
- Based on these results, determine what to do next. You can:
842
- 1. Continue with more actions by returning a new JSON with "actions" array
843
- 2. Stop the iteration by including an action with "type": "stop" if the task is completed
844
- 3. Provide a final response to the user with your findings
845
-
846
- Please analyze these results and respond appropriately.`,
847
- timestamp: new Date().toISOString()
848
- });
849
- // Check if processing was cancelled before calling API again
850
- if (!this._isProcessingAgentActions) {
851
- return;
852
- }
853
- // Call API again with updated conversation
854
- await this._getAIResponse(chat);
855
- }
856
- catch (error) {
857
- console.error('Error executing actions:', error);
858
- this._agentUtils.log(`Error executing actions: ${error instanceof Error ? error.message : String(error)}`);
859
- throw error;
860
- }
861
- }
862
- clearConversation() {
863
- // Abort any ongoing API request
864
- if (this._abortController && this._isGeneratingResponse) {
865
- this._abortController.abort();
866
- this._abortController = null;
867
- this._isGeneratingResponse = false;
868
- }
869
- // Stop any agent actions processing
870
- this._isProcessingAgentActions = false;
871
- // Get the current chat
872
- const currentChat = this._getCurrentChat();
873
- if (!currentChat) {
874
- return;
875
- }
876
- // Keep system message but clear the rest
877
- currentChat.messages = currentChat.messages.filter(msg => msg.role === 'system');
878
- // Update the webview
879
- this._postMessageToWebview({ type: 'clearConversation' });
880
- // Hide loading indicator if it's showing
881
- this._postMessageToWebview({ type: 'setLoading', isLoading: false });
882
- }
883
- _getHtmlForWebview(webview) {
884
- // Create URLs for images
885
- let mediaPath = '';
886
- let infinityIconPath = '';
887
- if (this._extensionUri) {
888
- try {
889
- mediaPath = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media'));
890
- infinityIconPath = `${mediaPath}/infinity-icon.svg`;
891
- }
892
- catch (error) {
893
- console.error('Error creating webview URIs:', error);
894
- }
895
- }
896
- else {
897
- console.error('Extension URI is undefined');
898
- }
899
- return `<!DOCTYPE html>
900
- <html lang="${this._language}">
901
- <head>
902
- <meta charset="UTF-8">
903
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
904
- <title>AI Assistant</title>
905
- <style>
906
- :root {
907
- --primary-color: #ff3333;
908
- --primary-gradient: linear-gradient(135deg, #ff3333, #cc0000);
909
- --secondary-gradient: linear-gradient(135deg, #ff6666, #ff3333);
910
- --accent-color: #ff6666;
911
- --text-on-primary: white;
912
- --message-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
913
- --hover-transform: translateY(-2px);
914
- }
915
-
916
- body {
917
- font-family: var(--vscode-font-family);
918
- padding: 0;
919
- margin: 0;
920
- color: var(--vscode-foreground);
921
- background-color: var(--vscode-editor-background);
922
- background-image:
923
- radial-gradient(circle at 0% 0%, rgba(255, 51, 51, 0.03) 0%, transparent 50%),
924
- radial-gradient(circle at 100% 100%, rgba(255, 102, 102, 0.03) 0%, transparent 50%);
925
- }
926
-
927
- .container {
928
- display: flex;
929
- flex-direction: column;
930
- height: 100vh;
931
- max-width: 100%;
932
- box-sizing: border-box;
933
- }
934
-
935
- .header {
936
- background: var(--primary-gradient);
937
- color: var(--text-on-primary);
938
- padding: 12px 16px;
939
- display: flex;
940
- justify-content: space-between;
941
- align-items: center;
942
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
943
- position: relative;
944
- overflow: hidden;
945
- }
946
-
947
- .header::before {
948
- content: '';
949
- position: absolute;
950
- top: 0;
951
- left: 0;
952
- right: 0;
953
- bottom: 0;
954
- background: linear-gradient(45deg, transparent 0%, rgba(255, 255, 255, 0.1) 50%, transparent 100%);
955
- animation: shimmer 3s infinite;
956
- }
957
-
958
- @keyframes shimmer {
959
- 0% { transform: translateX(-100%); }
960
- 100% { transform: translateX(100%); }
961
- }
962
-
963
- .header-title {
964
- display: flex;
965
- align-items: center;
966
- font-size: 16px;
967
- font-weight: bold;
968
- position: relative;
969
- z-index: 1;
970
- }
971
-
972
- .header-actions {
973
- display: flex;
974
- gap: 8px;
975
- }
976
-
977
- .profile-icon {
978
- width: 32px;
979
- height: 32px;
980
- border-radius: 50%;
981
- background: var(--secondary-gradient);
982
- margin-right: 12px;
983
- padding: 4px;
984
- box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.2);
985
- display: flex;
986
- align-items: center;
987
- justify-content: center;
988
- transition: transform 0.3s ease;
989
- }
990
-
991
- .profile-icon:hover {
992
- transform: scale(1.1);
993
- }
994
-
995
- .profile-icon img {
996
- width: 24px;
997
- height: 24px;
998
- filter: brightness(0) invert(1);
999
- }
1000
-
1001
- .conversation {
1002
- flex: 1;
1003
- overflow-y: auto;
1004
- padding: 20px;
1005
- background-color: var(--vscode-editor-background);
1006
- background-image:
1007
- radial-gradient(circle at 10% 20%, rgba(255, 51, 51, 0.03) 0%, transparent 20%),
1008
- radial-gradient(circle at 90% 80%, rgba(255, 102, 102, 0.03) 0%, transparent 20%);
1009
- }
1010
-
1011
- .message {
1012
- margin-bottom: 20px;
1013
- padding: 14px 18px;
1014
- border-radius: 12px;
1015
- max-width: 85%;
1016
- word-wrap: break-word;
1017
- animation: fadeIn 0.4s ease;
1018
- box-shadow: var(--message-shadow);
1019
- transition: transform 0.2s ease, box-shadow 0.2s ease;
1020
- }
1021
-
1022
- .message:hover {
1023
- transform: var(--hover-transform);
1024
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
1025
- }
1026
-
1027
- @keyframes fadeIn {
1028
- from {
1029
- opacity: 0;
1030
- transform: translateY(20px);
1031
- }
1032
- to {
1033
- opacity: 1;
1034
- transform: translateY(0);
1035
- }
1036
- }
1037
-
1038
- .user {
1039
- background: var(--primary-gradient);
1040
- color: var(--text-on-primary);
1041
- align-self: flex-end;
1042
- margin-left: auto;
1043
- border-top-right-radius: 4px;
1044
- }
1045
-
1046
- .assistant {
1047
- background-color: var(--vscode-editor-inactiveSelectionBackground);
1048
- color: var(--vscode-foreground);
1049
- align-self: flex-start;
1050
- margin-right: auto;
1051
- border-top-left-radius: 4px;
1052
- position: relative;
1053
- padding-left: 24px;
1054
- }
1055
-
1056
- .assistant::before {
1057
- content: '';
1058
- display: block;
1059
- position: absolute;
1060
- left: 0;
1061
- top: 0;
1062
- bottom: 0;
1063
- width: 4px;
1064
- background: var(--primary-gradient);
1065
- border-radius: 4px 0 0 4px;
1066
- }
1067
-
1068
- .input-area {
1069
- display: flex;
1070
- padding: 16px;
1071
- border-top: 1px solid var(--vscode-panel-border);
1072
- background-color: var(--vscode-editor-background);
1073
- position: relative;
1074
- }
1075
-
1076
- #query-input {
1077
- flex: 1;
1078
- padding: 14px;
1079
- border: 2px solid var(--vscode-input-border);
1080
- background-color: var(--vscode-input-background);
1081
- color: var(--vscode-input-foreground);
1082
- border-radius: 12px;
1083
- outline: none;
1084
- transition: all 0.3s ease;
1085
- font-size: 14px;
1086
- }
1087
-
1088
- #query-input:focus {
1089
- border-color: var(--primary-color);
1090
- box-shadow: 0 0 0 3px rgba(255, 51, 51, 0.1);
1091
- }
1092
-
1093
- .send-button {
1094
- margin-left: 10px;
1095
- padding: 10px 16px;
1096
- background: var(--primary-gradient);
1097
- color: var(--text-on-primary);
1098
- border: none;
1099
- border-radius: 12px;
1100
- cursor: pointer;
1101
- font-weight: bold;
1102
- transition: all 0.3s ease;
1103
- box-shadow: 0 2px 6px rgba(255, 51, 51, 0.3);
1104
- font-size: 13px;
1105
- position: relative;
1106
- overflow: hidden;
1107
- }
1108
-
1109
- .send-button::before {
1110
- content: '';
1111
- position: absolute;
1112
- top: 0;
1113
- left: 0;
1114
- width: 100%;
1115
- height: 100%;
1116
- background: linear-gradient(45deg, transparent 0%, rgba(255, 255, 255, 0.2) 50%, transparent 100%);
1117
- transform: translateX(-100%);
1118
- transition: transform 0.6s ease;
1119
- }
1120
-
1121
- .send-button:hover {
1122
- transform: var(--hover-transform);
1123
- box-shadow: 0 4px 12px rgba(255, 51, 51, 0.4);
1124
- }
1125
-
1126
- .send-button:hover::before {
1127
- transform: translateX(100%);
1128
- }
1129
-
1130
- .clear-button {
1131
- margin-left: 8px;
1132
- padding: 10px 16px;
1133
- background-color: transparent;
1134
- color: var(--vscode-errorForeground);
1135
- border: 2px solid var(--vscode-errorForeground);
1136
- border-radius: 12px;
1137
- cursor: pointer;
1138
- transition: all 0.3s ease;
1139
- font-size: 13px;
1140
- font-weight: 500;
1141
- }
1142
-
1143
- .clear-button:hover {
1144
- background-color: var(--vscode-errorForeground);
1145
- color: white;
1146
- transform: var(--hover-transform);
1147
- }
1148
-
1149
- .lang-button {
1150
- background-color: rgba(255, 255, 255, 0.1);
1151
- color: var(--text-on-primary);
1152
- border: 1px solid rgba(255, 255, 255, 0.2);
1153
- border-radius: 6px;
1154
- cursor: pointer;
1155
- padding: 6px 12px;
1156
- font-size: 12px;
1157
- transition: all 0.3s ease;
1158
- backdrop-filter: blur(4px);
1159
- }
1160
-
1161
- .config-button {
1162
- background-color: rgba(255, 255, 255, 0.1);
1163
- color: var(--text-on-primary);
1164
- border: 1px solid rgba(255, 255, 255, 0.2);
1165
- border-radius: 6px;
1166
- cursor: pointer;
1167
- padding: 6px 12px;
1168
- font-size: 12px;
1169
- transition: all 0.3s ease;
1170
- backdrop-filter: blur(4px);
1171
- }
1172
-
1173
- .lang-button:hover, .config-button:hover {
1174
- background-color: rgba(255, 255, 255, 0.2);
1175
- transform: var(--hover-transform);
1176
- }
1177
-
1178
- .config-modal {
1179
- display: none;
1180
- position: fixed;
1181
- top: 0;
1182
- left: 0;
1183
- width: 100%;
1184
- height: 100%;
1185
- background-color: rgba(0, 0, 0, 0.5);
1186
- z-index: 1000;
1187
- justify-content: center;
1188
- align-items: center;
1189
- }
1190
-
1191
- .config-modal-content {
1192
- background-color: var(--vscode-editor-background);
1193
- border-radius: 8px;
1194
- padding: 24px;
1195
- width: 90%;
1196
- max-width: 450px;
1197
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
1198
- margin: 0 auto;
1199
- }
1200
-
1201
- .config-form {
1202
- display: flex;
1203
- flex-direction: column;
1204
- align-items: stretch;
1205
- width: 100%;
1206
- }
1207
-
1208
- .config-modal-header {
1209
- display: flex;
1210
- justify-content: space-between;
1211
- align-items: center;
1212
- margin-bottom: 20px;
1213
- }
1214
-
1215
- .config-modal-title {
1216
- font-size: 18px;
1217
- font-weight: bold;
1218
- }
1219
-
1220
- .config-modal-close {
1221
- background: none;
1222
- border: none;
1223
- font-size: 20px;
1224
- cursor: pointer;
1225
- color: var(--vscode-foreground);
1226
- }
1227
-
1228
- .config-form-group {
1229
- margin-bottom: 18px;
1230
- width: 100%;
1231
- }
1232
-
1233
- .config-label {
1234
- display: block;
1235
- margin-bottom: 8px;
1236
- font-weight: 500;
1237
- }
1238
-
1239
- .config-input {
1240
- width: 100%;
1241
- padding: 10px 12px;
1242
- border-radius: 6px;
1243
- border: 1px solid var(--vscode-input-border);
1244
- background-color: var(--vscode-input-background);
1245
- color: var(--vscode-input-foreground);
1246
- box-sizing: border-box;
1247
- }
1248
-
1249
- .config-save-button {
1250
- background: var(--primary-gradient);
1251
- color: var(--text-on-primary);
1252
- border: none;
1253
- border-radius: 6px;
1254
- padding: 10px 16px;
1255
- font-weight: bold;
1256
- cursor: pointer;
1257
- margin-top: 12px;
1258
- align-self: center;
1259
- width: 80%;
1260
- }
1261
-
1262
- .loading {
1263
- display: none;
1264
- text-align: center;
1265
- margin: 24px 0;
1266
- padding: 24px;
1267
- border-radius: 12px;
1268
- background-color: rgba(0, 0, 0, 0.03);
1269
- animation: pulse 2s infinite;
1270
- }
1271
-
1272
- @keyframes pulse {
1273
- 0% { opacity: 0.6; }
1274
- 50% { opacity: 1; }
1275
- 100% { opacity: 0.6; }
1276
- }
1277
-
1278
- .spinner {
1279
- display: inline-block;
1280
- position: relative;
1281
- width: 48px;
1282
- height: 48px;
1283
- }
1284
-
1285
- .spinner::before {
1286
- content: "";
1287
- display: block;
1288
- position: absolute;
1289
- top: 0;
1290
- left: 0;
1291
- width: 100%;
1292
- height: 100%;
1293
- border-radius: 50%;
1294
- border: 4px solid rgba(255, 51, 51, 0.1);
1295
- border-top-color: var(--primary-color);
1296
- animation: spin 1s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite;
1297
- }
1298
-
1299
- @keyframes spin {
1300
- to { transform: rotate(360deg); }
1301
- }
1302
-
1303
- .loading-text {
1304
- margin-top: 12px;
1305
- color: var(--primary-color);
1306
- font-weight: 500;
1307
- font-size: 14px;
1308
- }
1309
-
1310
- pre {
1311
- background-color: var(--vscode-textCodeBlock-background);
1312
- padding: 16px;
1313
- border-radius: 12px;
1314
- overflow-x: auto;
1315
- border-left: 4px solid var(--primary-color);
1316
- margin: 12px 0;
1317
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
1318
- }
1319
-
1320
- code {
1321
- font-family: var(--vscode-editor-font-family);
1322
- font-size: 13px;
1323
- }
1324
-
1325
- .agent-working {
1326
- font-style: italic;
1327
- opacity: 0.9;
1328
- background-color: rgba(255, 51, 51, 0.05);
1329
- border-left: 4px solid var(--primary-color);
1330
- padding: 16px;
1331
- border-radius: 12px;
1332
- margin: 12px 0;
1333
- animation: pulse 2s infinite;
1334
- }
1335
-
1336
- .message-actions {
1337
- display: flex;
1338
- justify-content: flex-end;
1339
- margin-top: 5px;
1340
- }
1341
-
1342
- .restore-button {
1343
- background-color: var(--vscode-button-secondaryBackground);
1344
- color: var(--vscode-button-secondaryForeground);
1345
- border: none;
1346
- border-radius: 3px;
1347
- padding: 2px 8px;
1348
- font-size: 11px;
1349
- cursor: pointer;
1350
- margin-left: 5px;
1351
- display: flex;
1352
- align-items: center;
1353
- transition: all 0.2s ease;
1354
- }
1355
-
1356
- .restore-button:hover {
1357
- background-color: var(--vscode-button-secondaryHoverBackground);
1358
- transform: translateY(-1px);
1359
- }
1360
-
1361
- .restore-button::before {
1362
- content: "↺";
1363
- margin-right: 4px;
1364
- font-size: 12px;
1365
- }
1366
-
1367
- /* Chat sidebar styles */
1368
- .app-container {
1369
- display: flex;
1370
- height: 100vh;
1371
- width: 100%;
1372
- }
1373
-
1374
- .chat-sidebar {
1375
- width: 250px;
1376
- background-color: var(--vscode-sideBar-background);
1377
- border-right: 1px solid var(--vscode-sideBar-border);
1378
- display: flex;
1379
- flex-direction: column;
1380
- overflow: hidden;
1381
- }
1382
-
1383
- .chat-sidebar-header {
1384
- padding: 10px;
1385
- display: flex;
1386
- justify-content: space-between;
1387
- align-items: center;
1388
- border-bottom: 1px solid var(--vscode-sideBar-border);
1389
- }
1390
-
1391
- .new-chat-button {
1392
- background-color: var(--primary-color);
1393
- color: var(--text-on-primary);
1394
- border: none;
1395
- border-radius: 3px;
1396
- padding: 5px 10px;
1397
- cursor: pointer;
1398
- font-size: 12px;
1399
- display: flex;
1400
- align-items: center;
1401
- }
1402
-
1403
- .new-chat-button:hover {
1404
- background-color: #cc0000;
1405
- }
1406
-
1407
- .new-chat-button svg {
1408
- margin-right: 5px;
1409
- width: 14px;
1410
- height: 14px;
1411
- }
1412
-
1413
- .chat-list {
1414
- flex: 1;
1415
- overflow-y: auto;
1416
- padding: 5px;
1417
- }
1418
-
1419
- .chat-item {
1420
- padding: 8px 10px;
1421
- margin-bottom: 2px;
1422
- border-radius: 4px;
1423
- cursor: pointer;
1424
- display: flex;
1425
- align-items: center;
1426
- justify-content: space-between;
1427
- transition: background-color 0.2s;
1428
- }
1429
-
1430
- .chat-item:hover {
1431
- background-color: var(--vscode-list-hoverBackground);
1432
- }
1433
-
1434
- .chat-item.active {
1435
- background-color: var(--primary-color);
1436
- color: var(--text-on-primary);
1437
- }
1438
-
1439
- .chat-title {
1440
- flex: 1;
1441
- white-space: nowrap;
1442
- overflow: hidden;
1443
- text-overflow: ellipsis;
1444
- font-size: 13px;
1445
- }
1446
-
1447
- .chat-actions {
1448
- display: none;
1449
- margin-left: 5px;
1450
- }
1451
-
1452
- .chat-item:hover .chat-actions {
1453
- display: flex;
1454
- }
1455
-
1456
- .chat-action-button {
1457
- background: none;
1458
- border: none;
1459
- color: var(--vscode-foreground);
1460
- cursor: pointer;
1461
- padding: 2px 5px;
1462
- font-size: 12px;
1463
- opacity: 0.7;
1464
- }
1465
-
1466
- .chat-action-button:hover {
1467
- opacity: 1;
1468
- }
1469
-
1470
- .main-content {
1471
- flex: 1;
1472
- display: flex;
1473
- flex-direction: column;
1474
- overflow: hidden;
1475
- }
1476
- </style>
1477
- </head>
1478
- <body>
1479
- <div class="app-container">
1480
- <div class="chat-sidebar">
1481
- <div class="chat-sidebar-header">
1482
- <h3>Chats</h3>
1483
- <button class="new-chat-button" id="new-chat-button">
1484
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1485
- <line x1="12" y1="5" x2="12" y2="19"></line>
1486
- <line x1="5" y1="12" x2="19" y2="12"></line>
1487
- </svg>
1488
- New Chat
1489
- </button>
1490
- </div>
1491
- <div class="chat-list" id="chat-list">
1492
- <!-- Chat items will be added here dynamically -->
1493
- </div>
1494
- </div>
1495
-
1496
- <div class="main-content">
1497
- <div class="header">
1498
- <div class="header-title">
1499
- <div class="profile-icon">
1500
- <img src="${infinityIconPath}" alt="AI" />
1501
- </div>
1502
- <span>Samantha Coder</span>
1503
- </div>
1504
- <div class="header-actions">
1505
- <button class="config-button" id="config-button">⚙️ Config</button>
1506
- <button class="lang-button" id="lang-button">${translations[this._language].changeLanguageButton}</button>
1507
- </div>
1508
- </div>
1509
-
1510
- <div class="config-modal" id="config-modal">
1511
- <div class="config-modal-content">
1512
- <div class="config-modal-header">
1513
- <div class="config-modal-title">Configuration</div>
1514
- <button class="config-modal-close" id="config-modal-close">&times;</button>
1515
- </div>
1516
- <div class="config-form">
1517
- <div class="config-form-group">
1518
- <label class="config-label" for="model-input">Model Name</label>
1519
- <input type="text" id="model-input" class="config-input" placeholder="Enter model name">
1520
- </div>
1521
- <div class="config-form-group">
1522
- <label class="config-label" for="api-base-url-input">API Base URL</label>
1523
- <input type="text" id="api-base-url-input" class="config-input" placeholder="Enter API base URL">
1524
- </div>
1525
- <div class="config-form-group">
1526
- <label class="config-label" for="api-key-input">API Key (optional)</label>
1527
- <input type="password" id="api-key-input" class="config-input" placeholder="Enter API key">
1528
- </div>
1529
- <div class="config-form-group">
1530
- <label class="config-label" for="max-tokens-input">Max Tokens</label>
1531
- <input type="number" id="max-tokens-input" class="config-input" placeholder="Enter max tokens">
1532
- </div>
1533
- <button class="config-save-button" id="config-save-button">Save Configuration</button>
1534
- </div>
1535
- </div>
1536
- </div>
1537
-
1538
- <div class="conversation" id="conversation">
1539
- <div class="message assistant">
1540
- ${translations[this._language].welcomeMessage}
1541
- </div>
1542
- </div>
1543
-
1544
- <div class="loading" id="loading">
1545
- <div class="spinner"></div>
1546
- <div class="loading-text">${translations[this._language].loadingText}</div>
1547
- </div>
1548
-
1549
- <div class="input-area">
1550
- <input type="text" id="query-input" placeholder="${translations[this._language].inputPlaceholder}" />
1551
- <button class="send-button" id="send-button">${translations[this._language].sendButton}</button>
1552
- <button class="clear-button" id="clear-button">${translations[this._language].clearButton}</button>
1553
- </div>
1554
- </div>
1555
- </div>
1556
-
1557
- <script>
1558
- (function() {
1559
- const vscode = acquireVsCodeApi();
1560
- const conversationEl = document.getElementById('conversation');
1561
- const queryInput = document.getElementById('query-input');
1562
- const sendButton = document.getElementById('send-button');
1563
- const clearButton = document.getElementById('clear-button');
1564
- const langButton = document.getElementById('lang-button');
1565
- const loadingEl = document.getElementById('loading');
1566
- const chatListEl = document.getElementById('chat-list');
1567
- const newChatButton = document.getElementById('new-chat-button');
1568
- const configButton = document.getElementById('config-button');
1569
- const configModal = document.getElementById('config-modal');
1570
- const configModalClose = document.getElementById('config-modal-close');
1571
- const configSaveButton = document.getElementById('config-save-button');
1572
- const modelInput = document.getElementById('model-input');
1573
- const apiBaseUrlInput = document.getElementById('api-base-url-input');
1574
- const apiKeyInput = document.getElementById('api-key-input');
1575
- const maxTokensInput = document.getElementById('max-tokens-input');
1576
-
1577
- // Current translations
1578
- let currentTranslations = ${JSON.stringify(translations[this._language])};
1579
-
1580
- // Current chat ID
1581
- let currentChatId = null;
1582
-
1583
- // Format messages with markdown-like syntax
1584
- function formatMessage(text) {
1585
- // Handle code blocks
1586
- text = text.replace(/\`\`\`([^\`]+)\`\`\`/g, '<pre><code>$1</code></pre>');
1587
-
1588
- // Handle inline code
1589
- text = text.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
1590
-
1591
- // Handle line breaks
1592
- text = text.replace(/\\n/g, '<br>');
1593
-
1594
- return text;
1595
- }
1596
-
1597
- function addMessageToUI(message) {
1598
- const messageEl = document.createElement('div');
1599
- messageEl.className = \`message \${message.role}\`;
1600
- messageEl.dataset.timestamp = message.timestamp;
1601
-
1602
- // Add agent-working class if it's the agent working message
1603
- if (message.role === 'assistant' && message.content.includes(currentTranslations.workingMessage)) {
1604
- messageEl.classList.add('agent-working');
1605
- }
1606
-
1607
- const contentDiv = document.createElement('div');
1608
- contentDiv.className = 'message-content';
1609
- contentDiv.innerHTML = formatMessage(message.content);
1610
-
1611
- // Simply add content to the message for all message types
1612
- messageEl.appendChild(contentDiv);
1613
-
1614
- conversationEl.appendChild(messageEl);
1615
- conversationEl.scrollTop = conversationEl.scrollHeight;
1616
- }
1617
-
1618
- // Update UI text elements based on language
1619
- function updateUILanguage(trans) {
1620
- currentTranslations = trans;
1621
- document.documentElement.lang = currentTranslations.changeLanguageButton === 'EN' ? 'pt-br' : 'en';
1622
-
1623
- // Update placeholders and button texts
1624
- queryInput.placeholder = currentTranslations.inputPlaceholder;
1625
- sendButton.textContent = currentTranslations.sendButton;
1626
- clearButton.textContent = currentTranslations.clearButton;
1627
- langButton.textContent = currentTranslations.changeLanguageButton;
1628
-
1629
- // Update loading text
1630
- document.querySelector('.loading-text').textContent = currentTranslations.loadingText;
1631
- }
1632
-
1633
- // Handle sending query
1634
- function sendQuery() {
1635
- const query = queryInput.value.trim();
1636
- if (query) {
1637
- vscode.postMessage({
1638
- type: 'sendQuery',
1639
- value: query
1640
- });
1641
- queryInput.value = '';
1642
- }
1643
- }
1644
-
1645
- // Create a chat item element
1646
- function createChatItem(chat) {
1647
- const chatItem = document.createElement('div');
1648
- chatItem.className = 'chat-item';
1649
- if (chat.id === currentChatId) {
1650
- chatItem.classList.add('active');
1651
- }
1652
- chatItem.dataset.chatId = chat.id;
1653
-
1654
- const titleSpan = document.createElement('span');
1655
- titleSpan.className = 'chat-title';
1656
- titleSpan.textContent = chat.title;
1657
-
1658
- const actionsDiv = document.createElement('div');
1659
- actionsDiv.className = 'chat-actions';
1660
-
1661
- const renameButton = document.createElement('button');
1662
- renameButton.className = 'chat-action-button';
1663
- renameButton.innerHTML = '✏️';
1664
- renameButton.title = 'Rename';
1665
- renameButton.onclick = (e) => {
1666
- e.stopPropagation();
1667
- const newTitle = prompt('Enter new chat title:', chat.title);
1668
- if (newTitle && newTitle.trim() !== '') {
1669
- vscode.postMessage({
1670
- type: 'renameChat',
1671
- chatId: chat.id,
1672
- newTitle: newTitle.trim()
1673
- });
1674
- }
1675
- };
1676
-
1677
- const deleteButton = document.createElement('button');
1678
- deleteButton.className = 'chat-action-button';
1679
- deleteButton.innerHTML = '🗑️';
1680
- deleteButton.title = 'Delete';
1681
- deleteButton.onclick = (e) => {
1682
- e.stopPropagation();
1683
- // Prevent deleting the only chat
1684
- const chatCount = document.querySelectorAll('.chat-item').length;
1685
- if (chatCount <= 1) {
1686
- alert('Cannot delete the only chat. Create a new chat first.');
1687
- return;
1688
- }
1689
-
1690
- if (confirm('Are you sure you want to delete this chat?')) {
1691
- vscode.postMessage({
1692
- type: 'deleteChat',
1693
- chatId: chat.id
1694
- });
1695
- }
1696
- };
1697
-
1698
- actionsDiv.appendChild(renameButton);
1699
- actionsDiv.appendChild(deleteButton);
1700
-
1701
- chatItem.appendChild(titleSpan);
1702
- chatItem.appendChild(actionsDiv);
1703
-
1704
- chatItem.onclick = () => {
1705
- // Don't switch if already active
1706
- if (chat.id !== currentChatId) {
1707
- vscode.postMessage({
1708
- type: 'switchChat',
1709
- chatId: chat.id
1710
- });
1711
- }
1712
- };
1713
-
1714
- return chatItem;
1715
- }
1716
-
1717
- // Update the chat list
1718
- function updateChatList(chats, activeChatId) {
1719
- chatListEl.innerHTML = '';
1720
- currentChatId = activeChatId;
1721
-
1722
- chats.forEach(chat => {
1723
- chatListEl.appendChild(createChatItem(chat));
1724
- });
1725
- }
1726
-
1727
- // Event listeners
1728
- sendButton.addEventListener('click', sendQuery);
1729
-
1730
- queryInput.addEventListener('keydown', (e) => {
1731
- if (e.key === 'Enter') {
1732
- sendQuery();
1733
- }
1734
- });
1735
-
1736
- clearButton.addEventListener('click', () => {
1737
- vscode.postMessage({
1738
- type: 'clearConversation'
1739
- });
1740
- });
1741
-
1742
- langButton.addEventListener('click', () => {
1743
- vscode.postMessage({
1744
- type: 'toggleLanguage'
1745
- });
1746
- });
1747
-
1748
- newChatButton.addEventListener('click', () => {
1749
- const title = prompt('Enter chat title:', 'New Chat');
1750
- if (title && title.trim() !== '') {
1751
- vscode.postMessage({
1752
- type: 'createNewChat',
1753
- title: title.trim()
1754
- });
1755
- } else {
1756
- vscode.postMessage({
1757
- type: 'createNewChat'
1758
- });
1759
- }
1760
- });
1761
-
1762
- // Config button event
1763
- configButton.addEventListener('click', () => {
1764
- // Request current configuration values
1765
- vscode.postMessage({
1766
- type: 'getConfiguration'
1767
- });
1768
-
1769
- // Show modal
1770
- configModal.style.display = 'flex';
1771
- });
1772
-
1773
- // Close modal when clicking close button
1774
- configModalClose.addEventListener('click', () => {
1775
- configModal.style.display = 'none';
1776
- });
1777
-
1778
- // Close modal when clicking outside the modal content
1779
- configModal.addEventListener('click', (e) => {
1780
- if (e.target === configModal) {
1781
- configModal.style.display = 'none';
1782
- }
1783
- });
1784
-
1785
- // Save configuration
1786
- configSaveButton.addEventListener('click', () => {
1787
- const modelName = modelInput.value.trim();
1788
- const apiBaseUrl = apiBaseUrlInput.value.trim();
1789
- const apiKey = apiKeyInput.value.trim();
1790
- const maxTokens = maxTokensInput.value.trim();
1791
-
1792
- vscode.postMessage({
1793
- type: 'saveConfiguration',
1794
- modelName: modelName,
1795
- apiBaseUrl: apiBaseUrl,
1796
- apiKey: apiKey,
1797
- maxTokens: maxTokens
1798
- });
1799
-
1800
- configModal.style.display = 'none';
1801
- });
1802
-
1803
- // Handle messages from the extension
1804
- window.addEventListener('message', event => {
1805
- const message = event.data;
1806
- switch (message.type) {
1807
- case 'addMessage':
1808
- addMessageToUI(message.message);
1809
- break;
1810
- case 'clearConversation':
1811
- conversationEl.innerHTML = '';
1812
- addMessageToUI({
1813
- role: 'assistant',
1814
- content: currentTranslations.welcomeMessage,
1815
- timestamp: new Date().toISOString()
1816
- });
1817
- break;
1818
- case 'setLoading':
1819
- loadingEl.style.display = message.isLoading ? 'block' : 'none';
1820
- break;
1821
- case 'updateLanguage':
1822
- updateUILanguage(message.translations);
1823
- break;
1824
- case 'restoreConversation':
1825
- conversationEl.innerHTML = '';
1826
- if (Array.isArray(message.messages)) {
1827
- message.messages.forEach(msg => {
1828
- addMessageToUI(msg);
1829
- });
1830
- }
1831
- break;
1832
- case 'updateChats':
1833
- updateChatList(message.chats, message.currentChatId);
1834
- break;
1835
- case 'switchChat':
1836
- currentChatId = message.chatId;
1837
- conversationEl.innerHTML = '';
1838
- if (Array.isArray(message.messages)) {
1839
- message.messages.forEach(msg => {
1840
- addMessageToUI(msg);
1841
- });
1842
- }
1843
- break;
1844
- case 'configuration':
1845
- // Update configuration form with values from extension
1846
- modelInput.value = message.model || '';
1847
- apiBaseUrlInput.value = message.apiBaseUrl || '';
1848
- apiKeyInput.value = message.apiKey || '';
1849
- maxTokensInput.value = message.maxTokens || '';
1850
- break;
1851
- }
1852
- });
1853
- })();
1854
- </script>
1855
- </body>
1856
- </html>`;
1857
- }
1858
- getAgentUtils() {
1859
- return this._agentUtils;
1860
- }
1861
- toggleLanguage() {
1862
- // Toggle between English and Portuguese
1863
- this._language = this._language === 'en' ? 'pt-br' : 'en';
1864
- // Update the configuration
1865
- const config = vscode.workspace.getConfiguration('aiAssistant');
1866
- config.update('language', this._language, true);
1867
- // Update the webview content while preserving chat history
1868
- if (this._view) {
1869
- this._view.webview.html = this._getHtmlForWebview(this._view.webview);
1870
- // Send current chat state to webview
1871
- this._postMessageToWebview({
1872
- type: 'updateLanguage',
1873
- translations: translations[this._language]
1874
- });
1875
- // Restore current chat messages
1876
- const currentChat = this._getCurrentChat();
1877
- if (currentChat) {
1878
- this._postMessageToWebview({
1879
- type: 'restoreConversation',
1880
- messages: this._getMessagesForDisplay(currentChat.messages)
1881
- });
1882
- }
1883
- // Update chat list
1884
- this._postMessageToWebview({
1885
- type: 'updateChats',
1886
- chats: this._chats,
1887
- currentChatId: this._currentChatId
1888
- });
1889
- }
1890
- }
1891
- // Take a snapshot of the current state
1892
- async _takeStateSnapshot(messageId) {
1893
- try {
1894
- // Create a snapshot even if no editor is active
1895
- const snapshot = {
1896
- files: {},
1897
- timestamp: new Date().toISOString(),
1898
- chatId: this._currentChatId,
1899
- trackedFiles: [...this._trackedFiles] // Store the set of tracked files at this point
1900
- };
1901
- // Track open editors and their content
1902
- for (const editor of vscode.window.visibleTextEditors) {
1903
- const document = editor.document;
1904
- snapshot.files[document.uri.fsPath] = {
1905
- exists: true,
1906
- content: document.getText(),
1907
- version: document.version
1908
- };
1909
- }
1910
- // Store the snapshot
1911
- this._conversationHistory[messageId] = snapshot;
1912
- // If there's an active editor, mark its file as the primary focus
1913
- const activeEditor = vscode.window.activeTextEditor;
1914
- if (activeEditor) {
1915
- const document = activeEditor.document;
1916
- if (snapshot.files[document.uri.fsPath]) {
1917
- snapshot.primaryFile = document.uri.fsPath;
1918
- }
1919
- }
1920
- // Reset the tracked files set after taking a snapshot
1921
- this._trackedFiles.clear();
1922
- // Re-add the currently open files to the tracked set
1923
- vscode.window.visibleTextEditors.forEach(editor => {
1924
- this._trackedFiles.add(editor.document.uri.fsPath);
1925
- });
1926
- }
1927
- catch (error) {
1928
- console.error('Error taking state snapshot:', error);
1929
- }
1930
- }
1931
- // Restore to the state before a specific message
1932
- async restoreToState(messageId) {
1933
- try {
1934
- const snapshot = this._conversationHistory[messageId];
1935
- if (!snapshot) {
1936
- vscode.window.showErrorMessage('No previous state found for this message');
1937
- return;
1938
- }
1939
- // Get the current chat
1940
- const currentChat = this._getCurrentChat();
1941
- if (!currentChat) {
1942
- return;
1943
- }
1944
- // Find the message in the conversation
1945
- const messageIndex = currentChat.messages.findIndex(msg => msg.timestamp === messageId);
1946
- if (messageIndex === -1) {
1947
- vscode.window.showErrorMessage('Message not found in conversation');
1948
- return;
1949
- }
1950
- // Get a list of files that were created or modified after this snapshot
1951
- const filesCreatedAfter = new Set(this._trackedFiles);
1952
- // If the snapshot has trackedFiles, remove them from the current set
1953
- if (snapshot.trackedFiles) {
1954
- snapshot.trackedFiles.forEach(file => {
1955
- filesCreatedAfter.delete(file);
1956
- });
1957
- }
1958
- // Check if there are files that were created after this message
1959
- if (filesCreatedAfter.size > 0) {
1960
- // Ask user if they want to delete newly created files
1961
- const deleteNewFiles = await vscode.window.showInformationMessage(`${filesCreatedAfter.size} file(s) were created after this message. Delete them?`, 'Yes', 'No');
1962
- if (deleteNewFiles === 'Yes') {
1963
- // Delete files created after the snapshot
1964
- const deletedCount = await this._deleteFiles(filesCreatedAfter);
1965
- if (deletedCount > 0) {
1966
- vscode.window.showInformationMessage(`Deleted ${deletedCount} file(s) created after the message`);
1967
- }
1968
- }
1969
- }
1970
- // Implement restoration logic
1971
- if (snapshot.files) {
1972
- // Keep track of successful restorations
1973
- let restoredCount = 0;
1974
- let primaryFileOpened = false;
1975
- // Process each file in the snapshot
1976
- for (const [filePath, fileData] of Object.entries(snapshot.files)) {
1977
- try {
1978
- // Restore file content if it existed at the time of snapshot
1979
- if (fileData.exists && fileData.content) {
1980
- await this._agentUtils.writeFile(filePath, fileData.content);
1981
- restoredCount++;
1982
- // Open the primary file (the one that was active when snapshot was taken)
1983
- if (snapshot.primaryFile === filePath && !primaryFileOpened) {
1984
- const uri = vscode.Uri.file(filePath);
1985
- const document = await vscode.workspace.openTextDocument(uri);
1986
- await vscode.window.showTextDocument(document);
1987
- primaryFileOpened = true;
1988
- }
1989
- }
1990
- }
1991
- catch (fileError) {
1992
- console.error(`Error restoring file ${filePath}:`, fileError);
1993
- }
1994
- }
1995
- // If we have a primary file but couldn't open it, try opening the first restored file
1996
- if (!primaryFileOpened && restoredCount > 0 && snapshot.primaryFile) {
1997
- try {
1998
- const uri = vscode.Uri.file(snapshot.primaryFile);
1999
- const document = await vscode.workspace.openTextDocument(uri);
2000
- await vscode.window.showTextDocument(document);
2001
- }
2002
- catch (error) {
2003
- console.error('Error opening primary file:', error);
2004
- }
2005
- }
2006
- // Show restoration success message
2007
- if (restoredCount > 0) {
2008
- vscode.window.showInformationMessage(`Restored ${restoredCount} file(s) to state before message`);
2009
- }
2010
- else {
2011
- // If no files were in the snapshot, this was likely the start of the conversation
2012
- vscode.window.showInformationMessage('Restored to initial state before any changes');
2013
- }
2014
- }
2015
- // Remove all messages after this one from the conversation
2016
- currentChat.messages = currentChat.messages.slice(0, messageIndex + 1);
2017
- currentChat.updatedAt = new Date().toISOString();
2018
- // Update the webview to reflect the changes
2019
- await this._postMessageToWebview({
2020
- type: 'restoreConversation',
2021
- messages: this._getMessagesForDisplay(currentChat.messages)
2022
- });
2023
- // Reset the tracked files to the state at the time of the snapshot
2024
- this._trackedFiles.clear();
2025
- if (snapshot.trackedFiles) {
2026
- snapshot.trackedFiles.forEach(file => {
2027
- this._trackedFiles.add(file);
2028
- });
2029
- }
2030
- }
2031
- catch (error) {
2032
- console.error('Error restoring state:', error);
2033
- vscode.window.showErrorMessage(`Failed to restore state: ${error instanceof Error ? error.message : String(error)}`);
2034
- }
2035
- }
2036
- // Helper method to delete multiple files
2037
- async _deleteFiles(filesToDelete) {
2038
- let deletedCount = 0;
2039
- for (const filePath of filesToDelete) {
2040
- try {
2041
- const uri = vscode.Uri.file(filePath);
2042
- // Check if the file exists before attempting to delete
2043
- try {
2044
- await vscode.workspace.fs.stat(uri);
2045
- // File exists, delete it
2046
- await vscode.workspace.fs.delete(uri, { useTrash: false });
2047
- deletedCount++;
2048
- }
2049
- catch (statError) {
2050
- // File doesn't exist, skip it
2051
- console.log(`File ${filePath} doesn't exist, skipping deletion`);
2052
- }
2053
- }
2054
- catch (error) {
2055
- console.error(`Error deleting file ${filePath}:`, error);
2056
- }
2057
- }
2058
- return deletedCount;
2059
- }
2060
- async _getConfiguration() {
2061
- if (!this._view) {
2062
- return;
2063
- }
2064
- const config = vscode.workspace.getConfiguration('aiAssistant');
2065
- const model = config.get('model');
2066
- const apiKey = config.get('apiKey');
2067
- const apiBaseUrl = config.get('apiBaseUrl');
2068
- const maxTokens = config.get('maxTokens');
2069
- this._postMessageToWebview({
2070
- type: 'configuration',
2071
- model,
2072
- apiKey,
2073
- apiBaseUrl,
2074
- maxTokens
2075
- });
2076
- }
2077
- async _saveConfiguration(modelName, apiBaseUrl, apiKey, maxTokens) {
2078
- const config = vscode.workspace.getConfiguration('aiAssistant');
2079
- if (modelName) {
2080
- await config.update('model', modelName, vscode.ConfigurationTarget.Global);
2081
- }
2082
- if (apiBaseUrl) {
2083
- await config.update('apiBaseUrl', apiBaseUrl, vscode.ConfigurationTarget.Global);
2084
- }
2085
- if (apiKey) {
2086
- await config.update('apiKey', apiKey, vscode.ConfigurationTarget.Global);
2087
- }
2088
- if (maxTokens) {
2089
- const maxTokensNum = parseInt(maxTokens, 10);
2090
- if (!isNaN(maxTokensNum) && maxTokensNum > 0) {
2091
- await config.update('maxTokens', maxTokensNum, vscode.ConfigurationTarget.Global);
2092
- }
2093
- }
2094
- vscode.window.showInformationMessage('AI Assistant configuration updated');
2095
- }
2096
- }
2097
- exports.AIAssistantViewProvider = AIAssistantViewProvider;
2098
- //# sourceMappingURL=aiAssistantViewProvider.js.map