promptarchitect 0.6.0
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.
- package/.vscodeignore +7 -0
- package/CHANGELOG.md +28 -0
- package/LICENSE +44 -0
- package/README.md +200 -0
- package/docs/CHAT_UI_REDESIGN_PLAN.md +371 -0
- package/images/hub-icon.svg +6 -0
- package/images/prompt-lab-icon.svg +11 -0
- package/package.json +519 -0
- package/src/agentPrompts.ts +278 -0
- package/src/agentService.ts +630 -0
- package/src/api.ts +223 -0
- package/src/authService.ts +556 -0
- package/src/chatPanel.ts +979 -0
- package/src/extension.ts +822 -0
- package/src/providers/aiChatViewProvider.ts +1023 -0
- package/src/providers/environmentTreeProvider.ts +311 -0
- package/src/providers/index.ts +9 -0
- package/src/providers/notesTreeProvider.ts +301 -0
- package/src/providers/quickAccessTreeProvider.ts +328 -0
- package/src/providers/scriptsTreeProvider.ts +324 -0
- package/src/refinerPanel.ts +620 -0
- package/src/templates.ts +61 -0
- package/src/workspaceIndexer.ts +766 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,1023 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Chat WebView Provider
|
|
3
|
+
*
|
|
4
|
+
* Provides the AI Chat panel as a webview in the sidebar.
|
|
5
|
+
* Integrates with the PromptArchitect API for chat functionality.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as vscode from 'vscode';
|
|
9
|
+
import { PromptArchitectAPI } from '../api';
|
|
10
|
+
|
|
11
|
+
interface ChatMessage {
|
|
12
|
+
id: string;
|
|
13
|
+
role: 'user' | 'assistant';
|
|
14
|
+
content: string;
|
|
15
|
+
timestamp: number;
|
|
16
|
+
refined?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class AIChatViewProvider implements vscode.WebviewViewProvider {
|
|
20
|
+
public static readonly viewType = 'promptarchitect.aiChat';
|
|
21
|
+
private _view?: vscode.WebviewView;
|
|
22
|
+
private chatHistory: ChatMessage[] = [];
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
private readonly context: vscode.ExtensionContext,
|
|
26
|
+
private readonly api: PromptArchitectAPI
|
|
27
|
+
) {}
|
|
28
|
+
|
|
29
|
+
public resolveWebviewView(
|
|
30
|
+
webviewView: vscode.WebviewView,
|
|
31
|
+
_context: vscode.WebviewViewResolveContext,
|
|
32
|
+
_token: vscode.CancellationToken
|
|
33
|
+
): void {
|
|
34
|
+
this._view = webviewView;
|
|
35
|
+
|
|
36
|
+
webviewView.webview.options = {
|
|
37
|
+
enableScripts: true,
|
|
38
|
+
localResourceRoots: [this.context.extensionUri],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
webviewView.webview.html = this.getHtmlContent();
|
|
42
|
+
|
|
43
|
+
// Handle messages from webview
|
|
44
|
+
webviewView.webview.onDidReceiveMessage(async (message) => {
|
|
45
|
+
switch (message.command) {
|
|
46
|
+
case 'sendMessage':
|
|
47
|
+
await this.handleSendMessage(message.text, message.refined);
|
|
48
|
+
break;
|
|
49
|
+
case 'refine':
|
|
50
|
+
await this.handleRefine(message.text, message.mode);
|
|
51
|
+
break;
|
|
52
|
+
case 'copyToClipboard':
|
|
53
|
+
await vscode.env.clipboard.writeText(message.text);
|
|
54
|
+
vscode.window.showInformationMessage('📋 Copied!');
|
|
55
|
+
break;
|
|
56
|
+
case 'insertInEditor':
|
|
57
|
+
await this.insertInEditor(message.text);
|
|
58
|
+
break;
|
|
59
|
+
case 'clearChat':
|
|
60
|
+
this.chatHistory = [];
|
|
61
|
+
await this.context.workspaceState.update('aiChatHistory', []);
|
|
62
|
+
this.syncHistory();
|
|
63
|
+
vscode.window.showInformationMessage('🗒️ Chat cleared');
|
|
64
|
+
break;
|
|
65
|
+
case 'addContext':
|
|
66
|
+
await this.addEditorContext();
|
|
67
|
+
break;
|
|
68
|
+
case 'attachFile':
|
|
69
|
+
await this.attachFile();
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Load any existing chat history
|
|
75
|
+
this.chatHistory = this.context.workspaceState.get('aiChatHistory', []);
|
|
76
|
+
this.syncHistory();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private async attachFile(): Promise<void> {
|
|
80
|
+
const uris = await vscode.window.showOpenDialog({
|
|
81
|
+
canSelectMany: false,
|
|
82
|
+
openLabel: 'Attach File',
|
|
83
|
+
filters: {
|
|
84
|
+
'All Files': ['*'],
|
|
85
|
+
'Code': ['ts', 'js', 'py', 'java', 'cpp', 'c', 'go', 'rs', 'rb', 'php'],
|
|
86
|
+
'Config': ['json', 'yaml', 'yml', 'toml', 'xml'],
|
|
87
|
+
'Text': ['txt', 'md', 'markdown'],
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (uris && uris.length > 0) {
|
|
92
|
+
const uri = uris[0];
|
|
93
|
+
const document = await vscode.workspace.openTextDocument(uri);
|
|
94
|
+
const content = document.getText();
|
|
95
|
+
const fileName = uri.fsPath.split(/[\\/]/).pop() || 'file';
|
|
96
|
+
const language = document.languageId;
|
|
97
|
+
|
|
98
|
+
// Limit content size
|
|
99
|
+
const maxChars = 10000;
|
|
100
|
+
const truncated = content.length > maxChars
|
|
101
|
+
? content.substring(0, maxChars) + '\n\n... [truncated]'
|
|
102
|
+
: content;
|
|
103
|
+
|
|
104
|
+
this._view?.webview.postMessage({
|
|
105
|
+
command: 'addFile',
|
|
106
|
+
fileName,
|
|
107
|
+
language,
|
|
108
|
+
content: truncated,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private async handleSendMessage(text: string, wasRefined: boolean = false): Promise<void> {
|
|
114
|
+
if (!text.trim()) return;
|
|
115
|
+
|
|
116
|
+
// Add user message
|
|
117
|
+
const userMessage: ChatMessage = {
|
|
118
|
+
id: this.generateId(),
|
|
119
|
+
role: 'user',
|
|
120
|
+
content: text,
|
|
121
|
+
timestamp: Date.now(),
|
|
122
|
+
refined: wasRefined,
|
|
123
|
+
};
|
|
124
|
+
this.chatHistory.push(userMessage);
|
|
125
|
+
this.syncHistory();
|
|
126
|
+
|
|
127
|
+
// Show loading
|
|
128
|
+
this._view?.webview.postMessage({ command: 'loading', loading: true });
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const response = await this.api.chat({
|
|
132
|
+
message: text,
|
|
133
|
+
history: this.chatHistory.slice(0, -1).map(m => ({
|
|
134
|
+
role: m.role,
|
|
135
|
+
content: m.content,
|
|
136
|
+
})),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Add assistant response
|
|
140
|
+
const assistantMessage: ChatMessage = {
|
|
141
|
+
id: this.generateId(),
|
|
142
|
+
role: 'assistant',
|
|
143
|
+
content: response.reply,
|
|
144
|
+
timestamp: Date.now(),
|
|
145
|
+
};
|
|
146
|
+
this.chatHistory.push(assistantMessage);
|
|
147
|
+
|
|
148
|
+
// Save to workspace state
|
|
149
|
+
await this.context.workspaceState.update('aiChatHistory', this.chatHistory);
|
|
150
|
+
|
|
151
|
+
this.syncHistory();
|
|
152
|
+
} catch (error) {
|
|
153
|
+
const errorMessage: ChatMessage = {
|
|
154
|
+
id: this.generateId(),
|
|
155
|
+
role: 'assistant',
|
|
156
|
+
content: `⚠️ Error: ${error}. Please try again.`,
|
|
157
|
+
timestamp: Date.now(),
|
|
158
|
+
};
|
|
159
|
+
this.chatHistory.push(errorMessage);
|
|
160
|
+
this.syncHistory();
|
|
161
|
+
} finally {
|
|
162
|
+
this._view?.webview.postMessage({ command: 'loading', loading: false });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private async handleRefine(text: string, mode: string): Promise<void> {
|
|
167
|
+
if (!text.trim()) return;
|
|
168
|
+
|
|
169
|
+
this._view?.webview.postMessage({ command: 'refining', refining: true });
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const config = vscode.workspace.getConfiguration('promptarchitect');
|
|
173
|
+
const targetModel = config.get<string>('targetModel') || 'general';
|
|
174
|
+
|
|
175
|
+
const feedbackMap: Record<string, string> = {
|
|
176
|
+
clarity: 'Make this prompt clearer and more specific.',
|
|
177
|
+
detailed: 'Add more context and details.',
|
|
178
|
+
concise: 'Make this more concise.',
|
|
179
|
+
technical: 'Make this technically precise.',
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const result = await this.api.refinePrompt({
|
|
183
|
+
prompt: text,
|
|
184
|
+
feedback: feedbackMap[mode] || feedbackMap.clarity,
|
|
185
|
+
targetModel,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
this._view?.webview.postMessage({
|
|
189
|
+
command: 'refined',
|
|
190
|
+
text: result.refinedPrompt,
|
|
191
|
+
});
|
|
192
|
+
} catch (error) {
|
|
193
|
+
vscode.window.showErrorMessage(`Refinement failed: ${error}`);
|
|
194
|
+
} finally {
|
|
195
|
+
this._view?.webview.postMessage({ command: 'refining', refining: false });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private async insertInEditor(text: string): Promise<void> {
|
|
200
|
+
const editor = vscode.window.activeTextEditor;
|
|
201
|
+
if (editor) {
|
|
202
|
+
await editor.edit((editBuilder) => {
|
|
203
|
+
if (editor.selection.isEmpty) {
|
|
204
|
+
editBuilder.insert(editor.selection.active, text);
|
|
205
|
+
} else {
|
|
206
|
+
editBuilder.replace(editor.selection, text);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
} else {
|
|
210
|
+
const doc = await vscode.workspace.openTextDocument({ content: text });
|
|
211
|
+
await vscode.window.showTextDocument(doc);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private async addEditorContext(): Promise<void> {
|
|
216
|
+
const editor = vscode.window.activeTextEditor;
|
|
217
|
+
if (!editor) {
|
|
218
|
+
vscode.window.showWarningMessage('No active editor');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let contextText = '';
|
|
223
|
+
if (!editor.selection.isEmpty) {
|
|
224
|
+
contextText = editor.document.getText(editor.selection);
|
|
225
|
+
} else {
|
|
226
|
+
const visibleRanges = editor.visibleRanges;
|
|
227
|
+
if (visibleRanges.length > 0) {
|
|
228
|
+
contextText = editor.document.getText(visibleRanges[0]);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (contextText) {
|
|
233
|
+
const fileName = editor.document.fileName.split(/[\\/]/).pop();
|
|
234
|
+
const language = editor.document.languageId;
|
|
235
|
+
|
|
236
|
+
this._view?.webview.postMessage({
|
|
237
|
+
command: 'addContext',
|
|
238
|
+
context: { fileName, language, code: contextText },
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private syncHistory(): void {
|
|
244
|
+
this._view?.webview.postMessage({
|
|
245
|
+
command: 'syncHistory',
|
|
246
|
+
history: this.chatHistory,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private generateId(): string {
|
|
251
|
+
return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
public prefillPrompt(text: string): void {
|
|
255
|
+
this._view?.webview.postMessage({
|
|
256
|
+
command: 'prefill',
|
|
257
|
+
text,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private getHtmlContent(): string {
|
|
262
|
+
return /*html*/ `
|
|
263
|
+
<!DOCTYPE html>
|
|
264
|
+
<html lang="en">
|
|
265
|
+
<head>
|
|
266
|
+
<meta charset="UTF-8">
|
|
267
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
268
|
+
<style>
|
|
269
|
+
:root {
|
|
270
|
+
--radius-sm: 4px;
|
|
271
|
+
--radius-md: 8px;
|
|
272
|
+
--radius-lg: 12px;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
276
|
+
|
|
277
|
+
html, body {
|
|
278
|
+
font-family: var(--vscode-font-family);
|
|
279
|
+
font-size: var(--vscode-font-size);
|
|
280
|
+
background: var(--vscode-sideBar-background);
|
|
281
|
+
color: var(--vscode-foreground);
|
|
282
|
+
height: 100%;
|
|
283
|
+
overflow: hidden;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
body {
|
|
287
|
+
display: flex;
|
|
288
|
+
flex-direction: column;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/* Header toolbar */
|
|
292
|
+
.header {
|
|
293
|
+
flex-shrink: 0;
|
|
294
|
+
display: flex;
|
|
295
|
+
align-items: center;
|
|
296
|
+
justify-content: space-between;
|
|
297
|
+
padding: 8px 12px;
|
|
298
|
+
border-bottom: 1px solid var(--vscode-widget-border);
|
|
299
|
+
background: var(--vscode-sideBarSectionHeader-background);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.header-title {
|
|
303
|
+
font-size: 11px;
|
|
304
|
+
font-weight: 600;
|
|
305
|
+
text-transform: uppercase;
|
|
306
|
+
letter-spacing: 0.5px;
|
|
307
|
+
opacity: 0.8;
|
|
308
|
+
display: flex;
|
|
309
|
+
align-items: center;
|
|
310
|
+
gap: 6px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.header-actions {
|
|
314
|
+
display: flex;
|
|
315
|
+
gap: 4px;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.icon-btn {
|
|
319
|
+
background: transparent;
|
|
320
|
+
border: none;
|
|
321
|
+
color: var(--vscode-foreground);
|
|
322
|
+
cursor: pointer;
|
|
323
|
+
padding: 4px;
|
|
324
|
+
border-radius: var(--radius-sm);
|
|
325
|
+
opacity: 0.7;
|
|
326
|
+
transition: all 0.2s;
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
justify-content: center;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.icon-btn:hover {
|
|
333
|
+
opacity: 1;
|
|
334
|
+
background: var(--vscode-toolbar-hoverBackground);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/* Chat container */
|
|
338
|
+
.chat-container {
|
|
339
|
+
flex: 1;
|
|
340
|
+
overflow-y: auto;
|
|
341
|
+
padding: 12px;
|
|
342
|
+
display: flex;
|
|
343
|
+
flex-direction: column;
|
|
344
|
+
gap: 12px;
|
|
345
|
+
scroll-behavior: smooth;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.empty-state {
|
|
349
|
+
flex: 1;
|
|
350
|
+
display: flex;
|
|
351
|
+
flex-direction: column;
|
|
352
|
+
align-items: center;
|
|
353
|
+
justify-content: center;
|
|
354
|
+
text-align: center;
|
|
355
|
+
opacity: 0.8;
|
|
356
|
+
padding: 20px;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.empty-state .icon { font-size: 48px; margin-bottom: 16px; opacity: 0.5; }
|
|
360
|
+
.empty-state h3 { font-size: 16px; margin-bottom: 8px; font-weight: 600; }
|
|
361
|
+
.empty-state p { font-size: 13px; line-height: 1.5; color: var(--vscode-descriptionForeground); max-width: 240px; margin-bottom: 24px; }
|
|
362
|
+
|
|
363
|
+
.quick-actions {
|
|
364
|
+
display: flex;
|
|
365
|
+
flex-wrap: wrap;
|
|
366
|
+
gap: 8px;
|
|
367
|
+
justify-content: center;
|
|
368
|
+
max-width: 300px;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.quick-action {
|
|
372
|
+
padding: 6px 12px;
|
|
373
|
+
background: var(--vscode-button-secondaryBackground);
|
|
374
|
+
color: var(--vscode-button-secondaryForeground);
|
|
375
|
+
border: 1px solid transparent;
|
|
376
|
+
border-radius: 16px;
|
|
377
|
+
font-size: 11px;
|
|
378
|
+
cursor: pointer;
|
|
379
|
+
transition: all 0.2s;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.quick-action:hover {
|
|
383
|
+
background: var(--vscode-button-secondaryHoverBackground);
|
|
384
|
+
transform: translateY(-1px);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/* Messages */
|
|
388
|
+
.message {
|
|
389
|
+
display: flex;
|
|
390
|
+
flex-direction: column;
|
|
391
|
+
gap: 4px;
|
|
392
|
+
animation: slideIn 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
@keyframes slideIn {
|
|
396
|
+
from { opacity: 0; transform: translateY(10px); }
|
|
397
|
+
to { opacity: 1; transform: translateY(0); }
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.message.user { align-items: flex-end; }
|
|
401
|
+
.message.assistant { align-items: flex-start; }
|
|
402
|
+
|
|
403
|
+
.bubble {
|
|
404
|
+
max-width: 90%;
|
|
405
|
+
padding: 10px 14px;
|
|
406
|
+
font-size: 13px;
|
|
407
|
+
line-height: 1.5;
|
|
408
|
+
position: relative;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.message.user .bubble {
|
|
412
|
+
background: var(--vscode-button-background);
|
|
413
|
+
color: var(--vscode-button-foreground);
|
|
414
|
+
border-radius: 16px 16px 4px 16px;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.message.assistant .bubble {
|
|
418
|
+
background: var(--vscode-editor-background);
|
|
419
|
+
border: 1px solid var(--vscode-widget-border);
|
|
420
|
+
border-radius: 16px 16px 16px 4px;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.bubble pre {
|
|
424
|
+
background: var(--vscode-textCodeBlock-background);
|
|
425
|
+
padding: 12px;
|
|
426
|
+
border-radius: 6px;
|
|
427
|
+
overflow-x: auto;
|
|
428
|
+
margin: 8px 0;
|
|
429
|
+
font-family: var(--vscode-editor-font-family);
|
|
430
|
+
font-size: 12px;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.bubble code {
|
|
434
|
+
background: rgba(0,0,0,0.1);
|
|
435
|
+
padding: 2px 4px;
|
|
436
|
+
border-radius: 4px;
|
|
437
|
+
font-family: var(--vscode-editor-font-family);
|
|
438
|
+
font-size: 12px;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.message.assistant .bubble code {
|
|
442
|
+
background: var(--vscode-textCodeBlock-background);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.message-meta {
|
|
446
|
+
font-size: 10px;
|
|
447
|
+
color: var(--vscode-descriptionForeground);
|
|
448
|
+
display: flex;
|
|
449
|
+
align-items: center;
|
|
450
|
+
gap: 8px;
|
|
451
|
+
padding: 0 4px;
|
|
452
|
+
min-height: 16px;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.msg-actions {
|
|
456
|
+
display: flex;
|
|
457
|
+
gap: 4px;
|
|
458
|
+
opacity: 0;
|
|
459
|
+
transition: opacity 0.2s;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.message:hover .msg-actions { opacity: 1; }
|
|
463
|
+
|
|
464
|
+
.msg-action {
|
|
465
|
+
background: transparent;
|
|
466
|
+
border: none;
|
|
467
|
+
color: var(--vscode-descriptionForeground);
|
|
468
|
+
cursor: pointer;
|
|
469
|
+
padding: 2px;
|
|
470
|
+
font-size: 14px;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.msg-action:hover { color: var(--vscode-foreground); }
|
|
474
|
+
|
|
475
|
+
/* FLOATING COMPOSER */
|
|
476
|
+
.composer {
|
|
477
|
+
flex-shrink: 0;
|
|
478
|
+
padding: 16px;
|
|
479
|
+
background: var(--vscode-sideBar-background);
|
|
480
|
+
border-top: 1px solid var(--vscode-widget-border);
|
|
481
|
+
display: flex;
|
|
482
|
+
flex-direction: column;
|
|
483
|
+
gap: 12px;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.input-container {
|
|
487
|
+
background: var(--vscode-input-background);
|
|
488
|
+
border: 1px solid var(--vscode-input-border);
|
|
489
|
+
border-radius: var(--radius-md);
|
|
490
|
+
padding: 0;
|
|
491
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
492
|
+
display: flex;
|
|
493
|
+
flex-direction: column;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.input-container:focus-within {
|
|
497
|
+
border-color: var(--vscode-focusBorder);
|
|
498
|
+
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.chat-input {
|
|
502
|
+
width: 100%;
|
|
503
|
+
min-height: 100px;
|
|
504
|
+
max-height: 300px;
|
|
505
|
+
padding: 14px 16px;
|
|
506
|
+
border: none;
|
|
507
|
+
background: transparent;
|
|
508
|
+
color: var(--vscode-input-foreground);
|
|
509
|
+
font-family: var(--vscode-font-family);
|
|
510
|
+
font-size: 13px;
|
|
511
|
+
line-height: 1.6;
|
|
512
|
+
resize: none;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.chat-input:focus { outline: none; }
|
|
516
|
+
|
|
517
|
+
.chat-input::placeholder {
|
|
518
|
+
color: var(--vscode-input-placeholderForeground);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/* Attachments Bar */
|
|
522
|
+
.attachments-bar {
|
|
523
|
+
padding: 8px 12px;
|
|
524
|
+
border-top: 1px solid rgba(128, 128, 128, 0.1);
|
|
525
|
+
display: flex;
|
|
526
|
+
flex-wrap: wrap;
|
|
527
|
+
gap: 8px;
|
|
528
|
+
align-items: center;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.attach-btn {
|
|
532
|
+
background: transparent;
|
|
533
|
+
border: none;
|
|
534
|
+
color: var(--vscode-descriptionForeground);
|
|
535
|
+
font-size: 11px;
|
|
536
|
+
cursor: pointer;
|
|
537
|
+
display: flex;
|
|
538
|
+
align-items: center;
|
|
539
|
+
gap: 4px;
|
|
540
|
+
padding: 4px 8px;
|
|
541
|
+
border-radius: 4px;
|
|
542
|
+
transition: all 0.2s;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.attach-btn:hover {
|
|
546
|
+
background: var(--vscode-toolbar-hoverBackground);
|
|
547
|
+
color: var(--vscode-foreground);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.attachment-chip {
|
|
551
|
+
background: var(--vscode-badge-background);
|
|
552
|
+
color: var(--vscode-badge-foreground);
|
|
553
|
+
font-size: 11px;
|
|
554
|
+
padding: 2px 8px;
|
|
555
|
+
border-radius: 12px;
|
|
556
|
+
display: flex;
|
|
557
|
+
align-items: center;
|
|
558
|
+
gap: 4px;
|
|
559
|
+
animation: popIn 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
@keyframes popIn {
|
|
563
|
+
from { transform: scale(0.8); opacity: 0; }
|
|
564
|
+
to { transform: scale(1); opacity: 1; }
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.chip-remove {
|
|
568
|
+
background: none;
|
|
569
|
+
border: none;
|
|
570
|
+
color: inherit;
|
|
571
|
+
cursor: pointer;
|
|
572
|
+
opacity: 0.7;
|
|
573
|
+
padding: 0;
|
|
574
|
+
font-size: 12px;
|
|
575
|
+
display: flex;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.chip-remove:hover { opacity: 1; }
|
|
579
|
+
|
|
580
|
+
/* Polish Toolbar - COLLAPSIBLE */
|
|
581
|
+
.polish-toolbar {
|
|
582
|
+
overflow: hidden;
|
|
583
|
+
transition: all 0.3s ease;
|
|
584
|
+
background: var(--vscode-editor-background);
|
|
585
|
+
border: 1px solid var(--vscode-widget-border);
|
|
586
|
+
border-radius: var(--radius-md);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
.polish-toolbar.collapsed .polish-content {
|
|
590
|
+
display: none;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.polish-header {
|
|
594
|
+
padding: 8px 12px;
|
|
595
|
+
display: flex;
|
|
596
|
+
align-items: center;
|
|
597
|
+
justify-content: space-between;
|
|
598
|
+
cursor: pointer;
|
|
599
|
+
font-size: 11px;
|
|
600
|
+
font-weight: 600;
|
|
601
|
+
user-select: none;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.polish-header:hover {
|
|
605
|
+
background: var(--vscode-toolbar-hoverBackground);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.polish-content {
|
|
609
|
+
padding: 8px 12px;
|
|
610
|
+
border-top: 1px solid var(--vscode-widget-border);
|
|
611
|
+
display: flex;
|
|
612
|
+
gap: 6px;
|
|
613
|
+
flex-wrap: wrap;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.refine-btn {
|
|
617
|
+
flex: 1;
|
|
618
|
+
min-width: 80px;
|
|
619
|
+
padding: 6px 10px;
|
|
620
|
+
background: var(--vscode-button-secondaryBackground);
|
|
621
|
+
color: var(--vscode-button-secondaryForeground);
|
|
622
|
+
border: 1px solid transparent;
|
|
623
|
+
border-radius: 4px;
|
|
624
|
+
font-size: 11px;
|
|
625
|
+
cursor: pointer;
|
|
626
|
+
display: flex;
|
|
627
|
+
align-items: center;
|
|
628
|
+
justify-content: center;
|
|
629
|
+
gap: 6px;
|
|
630
|
+
transition: all 0.2s;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
.refine-btn:hover {
|
|
634
|
+
background: var(--vscode-button-secondaryHoverBackground);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.refine-btn.active {
|
|
638
|
+
background: var(--vscode-button-background);
|
|
639
|
+
color: var(--vscode-button-foreground);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/* Execute Button */
|
|
643
|
+
.execute-btn {
|
|
644
|
+
width: 100%;
|
|
645
|
+
padding: 12px;
|
|
646
|
+
background: var(--vscode-button-background);
|
|
647
|
+
color: var(--vscode-button-foreground);
|
|
648
|
+
border: none;
|
|
649
|
+
border-radius: var(--radius-md);
|
|
650
|
+
font-size: 13px;
|
|
651
|
+
font-weight: 600;
|
|
652
|
+
cursor: pointer;
|
|
653
|
+
transition: all 0.2s;
|
|
654
|
+
display: flex;
|
|
655
|
+
align-items: center;
|
|
656
|
+
justify-content: center;
|
|
657
|
+
gap: 8px;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.execute-btn:hover {
|
|
661
|
+
background: var(--vscode-button-hoverBackground);
|
|
662
|
+
transform: translateY(-1px);
|
|
663
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.execute-btn:active {
|
|
667
|
+
transform: translateY(0);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.execute-btn:disabled {
|
|
671
|
+
opacity: 0.6;
|
|
672
|
+
cursor: not-allowed;
|
|
673
|
+
transform: none;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.keyboard-hints {
|
|
677
|
+
text-align: center;
|
|
678
|
+
font-size: 10px;
|
|
679
|
+
color: var(--vscode-descriptionForeground);
|
|
680
|
+
opacity: 0;
|
|
681
|
+
transition: opacity 0.3s;
|
|
682
|
+
margin-top: -4px;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.composer:focus-within .keyboard-hints {
|
|
686
|
+
opacity: 0.7;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.spinner {
|
|
690
|
+
width: 14px;
|
|
691
|
+
height: 14px;
|
|
692
|
+
border: 2px solid currentColor;
|
|
693
|
+
border-top-color: transparent;
|
|
694
|
+
border-radius: 50%;
|
|
695
|
+
animation: spin 0.8s linear infinite;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
699
|
+
|
|
700
|
+
/* Narrow sidebar adaptations */
|
|
701
|
+
@media (max-width: 300px) {
|
|
702
|
+
.polish-content {
|
|
703
|
+
flex-direction: column;
|
|
704
|
+
}
|
|
705
|
+
.refine-btn {
|
|
706
|
+
width: 100%;
|
|
707
|
+
justify-content: flex-start;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
</style>
|
|
711
|
+
</head>
|
|
712
|
+
<body>
|
|
713
|
+
<div class="header">
|
|
714
|
+
<div class="header-title">
|
|
715
|
+
<span>⚡ Architect</span>
|
|
716
|
+
</div>
|
|
717
|
+
<div class="header-actions">
|
|
718
|
+
<button class="icon-btn" id="newChatBtn" title="New Chat">🗒️</button>
|
|
719
|
+
<button class="icon-btn" id="exportBtn" title="Export Chat">📤</button>
|
|
720
|
+
</div>
|
|
721
|
+
</div>
|
|
722
|
+
|
|
723
|
+
<div class="chat-container" id="chatContainer">
|
|
724
|
+
<div class="empty-state" id="emptyState">
|
|
725
|
+
<div class="icon">⚡</div>
|
|
726
|
+
<h3>PromptArchitect</h3>
|
|
727
|
+
<p>Build, refine, and execute complex prompts with ease.</p>
|
|
728
|
+
<div class="quick-actions">
|
|
729
|
+
<button class="quick-action" data-prompt="Refactor this code to follow SOLID principles:">🔧 Refactor</button>
|
|
730
|
+
<button class="quick-action" data-prompt="Write unit tests for this file:">🧪 Test</button>
|
|
731
|
+
<button class="quick-action" data-prompt="Explain how this code works:">💡 Explain</button>
|
|
732
|
+
<button class="quick-action" data-prompt="Find potential bugs in:">🐛 Debug</button>
|
|
733
|
+
</div>
|
|
734
|
+
</div>
|
|
735
|
+
</div>
|
|
736
|
+
|
|
737
|
+
<div class="composer">
|
|
738
|
+
<!-- Polish Toolbar (Collapsed by default) -->
|
|
739
|
+
<div class="polish-toolbar collapsed" id="polishToolbar">
|
|
740
|
+
<div class="polish-header" id="polishToggle">
|
|
741
|
+
<span style="display: flex; align-items: center; gap: 6px;">✨ Polish Prompt <span id="refineStatus" style="font-weight: 400; color: var(--vscode-descriptionForeground); font-size: 10px;">(Optional)</span></span>
|
|
742
|
+
<span class="chevron" id="polishChevron">▼</span>
|
|
743
|
+
</div>
|
|
744
|
+
<div class="polish-content">
|
|
745
|
+
<button class="refine-btn" data-mode="clarity">✨ Clarity</button>
|
|
746
|
+
<button class="refine-btn" data-mode="detailed">📝 Detail</button>
|
|
747
|
+
<button class="refine-btn" data-mode="concise">✂️ Concise</button>
|
|
748
|
+
<button class="refine-btn" data-mode="technical">⚙️ Technical</button>
|
|
749
|
+
</div>
|
|
750
|
+
</div>
|
|
751
|
+
|
|
752
|
+
<!-- Main Input Area -->
|
|
753
|
+
<div class="input-container">
|
|
754
|
+
<textarea class="chat-input" id="chatInput" placeholder="Describe what you want to build..." rows="4"></textarea>
|
|
755
|
+
|
|
756
|
+
<!-- Attachments Bar -->
|
|
757
|
+
<div class="attachments-bar" id="attachmentsBar">
|
|
758
|
+
<button class="attach-btn" id="attachCodeBtn">📎 Code</button>
|
|
759
|
+
<button class="attach-btn" id="attachFileBtn">📄 File</button>
|
|
760
|
+
<!-- Dynamic chips go here -->
|
|
761
|
+
<div id="attachmentChips" style="display:contents"></div>
|
|
762
|
+
</div>
|
|
763
|
+
</div>
|
|
764
|
+
|
|
765
|
+
<!-- Execute Button -->
|
|
766
|
+
<button class="execute-btn" id="sendBtn">
|
|
767
|
+
<span class="btn-icon">⚡</span>
|
|
768
|
+
<span class="btn-text">Execute Prompt</span>
|
|
769
|
+
</button>
|
|
770
|
+
|
|
771
|
+
<div class="keyboard-hints">
|
|
772
|
+
Enter to send · Shift+Enter for new line
|
|
773
|
+
</div>
|
|
774
|
+
</div>
|
|
775
|
+
|
|
776
|
+
<script>
|
|
777
|
+
const vscode = acquireVsCodeApi();
|
|
778
|
+
|
|
779
|
+
// UI Elements
|
|
780
|
+
const chatContainer = document.getElementById('chatContainer');
|
|
781
|
+
const emptyState = document.getElementById('emptyState');
|
|
782
|
+
const input = document.getElementById('chatInput');
|
|
783
|
+
const sendBtn = document.getElementById('sendBtn');
|
|
784
|
+
const polishToolbar = document.getElementById('polishToolbar');
|
|
785
|
+
const polishToggle = document.getElementById('polishToggle');
|
|
786
|
+
const polishChevron = document.getElementById('polishChevron');
|
|
787
|
+
const refineBtns = document.querySelectorAll('.refine-btn');
|
|
788
|
+
const attachmentChips = document.getElementById('attachmentChips');
|
|
789
|
+
const refineStatus = document.getElementById('refineStatus');
|
|
790
|
+
|
|
791
|
+
// State
|
|
792
|
+
let isLoading = false;
|
|
793
|
+
let wasRefined = false;
|
|
794
|
+
let attachedContexts = [];
|
|
795
|
+
let chatHistory = [];
|
|
796
|
+
|
|
797
|
+
// --- Autosize Textarea ---
|
|
798
|
+
function resizeInput() {
|
|
799
|
+
input.style.height = 'auto';
|
|
800
|
+
input.style.height = Math.min(input.scrollHeight, 300) + 'px';
|
|
801
|
+
}
|
|
802
|
+
input.addEventListener('input', resizeInput);
|
|
803
|
+
|
|
804
|
+
// --- Polish Toolbar ---
|
|
805
|
+
polishToggle.addEventListener('click', () => {
|
|
806
|
+
const isCollapsed = polishToolbar.classList.toggle('collapsed');
|
|
807
|
+
polishChevron.textContent = isCollapsed ? '▼' : '▲';
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
refineBtns.forEach(btn => {
|
|
811
|
+
btn.addEventListener('click', () => {
|
|
812
|
+
if (!input.value.trim()) return;
|
|
813
|
+
|
|
814
|
+
// Visual feedback
|
|
815
|
+
refineBtns.forEach(b => b.classList.remove('active'));
|
|
816
|
+
btn.classList.add('active');
|
|
817
|
+
refineStatus.textContent = '(Refining...)';
|
|
818
|
+
|
|
819
|
+
vscode.postMessage({
|
|
820
|
+
command: 'refine',
|
|
821
|
+
text: input.value,
|
|
822
|
+
mode: btn.dataset.mode
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
// --- Attachments ---
|
|
828
|
+
document.getElementById('attachCodeBtn').addEventListener('click', () => {
|
|
829
|
+
vscode.postMessage({ command: 'addContext' });
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
document.getElementById('attachFileBtn').addEventListener('click', () => {
|
|
833
|
+
vscode.postMessage({ command: 'attachFile' });
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
function renderAttachments() {
|
|
837
|
+
attachmentChips.innerHTML = attachedContexts.map((ctx, i) => \`
|
|
838
|
+
<div class="attachment-chip">
|
|
839
|
+
<span>\${ctx.type === 'file' ? '📄' : '✂️'} \${ctx.name}</span>
|
|
840
|
+
<button class="chip-remove" onclick="removeAttachment(\${i})">✕</button>
|
|
841
|
+
</div>
|
|
842
|
+
\`).join('');
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
window.removeAttachment = (index) => {
|
|
846
|
+
attachedContexts.splice(index, 1);
|
|
847
|
+
renderAttachments();
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
// --- Quick Actions ---
|
|
851
|
+
document.querySelectorAll('.quick-action').forEach(btn => {
|
|
852
|
+
btn.addEventListener('click', () => {
|
|
853
|
+
input.value = btn.dataset.prompt + ' ';
|
|
854
|
+
input.focus();
|
|
855
|
+
resizeInput();
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
// --- Messaging ---
|
|
860
|
+
sendBtn.addEventListener('click', sendMessage);
|
|
861
|
+
|
|
862
|
+
input.addEventListener('keydown', (e) => {
|
|
863
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
864
|
+
e.preventDefault();
|
|
865
|
+
sendMessage();
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
function sendMessage() {
|
|
870
|
+
if (isLoading || !input.value.trim()) return;
|
|
871
|
+
|
|
872
|
+
let text = input.value;
|
|
873
|
+
|
|
874
|
+
// Combine context
|
|
875
|
+
if (attachedContexts.length > 0) {
|
|
876
|
+
const contextStr = attachedContexts.map(ctx =>
|
|
877
|
+
\`**\${ctx.type}: \${ctx.name}**\\n\\\`\\\`\\\`\${ctx.language || ''}\\n\${ctx.content}\\n\\\`\\\`\\\`\`
|
|
878
|
+
).join('\\n\\n');
|
|
879
|
+
text = contextStr + '\\n\\n' + text;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
vscode.postMessage({ command: 'sendMessage', text, refined: wasRefined });
|
|
883
|
+
|
|
884
|
+
// Reset state
|
|
885
|
+
input.value = '';
|
|
886
|
+
resizeInput();
|
|
887
|
+
wasRefined = false;
|
|
888
|
+
attachedContexts = [];
|
|
889
|
+
renderAttachments();
|
|
890
|
+
refineBtns.forEach(b => b.classList.remove('active'));
|
|
891
|
+
polishToolbar.classList.add('collapsed');
|
|
892
|
+
polishChevron.textContent = '▼';
|
|
893
|
+
refineStatus.textContent = '(Optional)';
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// --- Message Rendering ---
|
|
897
|
+
function renderMessage(msg) {
|
|
898
|
+
const div = document.createElement('div');
|
|
899
|
+
div.className = \`message \${msg.role}\`;
|
|
900
|
+
|
|
901
|
+
let content = msg.content
|
|
902
|
+
.replace(/\\\`\\\`\\\`(\\w*)\\n([\\s\\S]*?)\\\`\\\`\\\`/g, '<pre><code>$2</code></pre>')
|
|
903
|
+
.replace(/\\\`([^\\\`]+)\\\`/g, '<code>$1</code>')
|
|
904
|
+
.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')
|
|
905
|
+
.replace(/\\n/g, '<br>');
|
|
906
|
+
|
|
907
|
+
const time = new Date(msg.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
908
|
+
|
|
909
|
+
div.innerHTML = \`
|
|
910
|
+
<div class="bubble">
|
|
911
|
+
\${content}
|
|
912
|
+
</div>
|
|
913
|
+
<div class="message-meta">
|
|
914
|
+
<span>\${time}</span>
|
|
915
|
+
\${msg.refined ? '<span>✨ refined</span>' : ''}
|
|
916
|
+
<div class="msg-actions">
|
|
917
|
+
<button class="msg-action" onclick="copyMsg('\${msg.id}')" title="Copy">📋</button>
|
|
918
|
+
\${msg.role === 'assistant' ? \`<button class="msg-action" onclick="insertMsg('\${msg.id}')" title="Insert">📥</button>\` : ''}
|
|
919
|
+
</div>
|
|
920
|
+
</div>
|
|
921
|
+
\`;
|
|
922
|
+
return div;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// --- Extension Communication ---
|
|
926
|
+
window.addEventListener('message', event => {
|
|
927
|
+
const msg = event.data;
|
|
928
|
+
|
|
929
|
+
switch (msg.command) {
|
|
930
|
+
case 'loading':
|
|
931
|
+
isLoading = msg.loading;
|
|
932
|
+
sendBtn.disabled = msg.loading;
|
|
933
|
+
const btnText = sendBtn.querySelector('.btn-text');
|
|
934
|
+
const btnIcon = sendBtn.querySelector('.btn-icon');
|
|
935
|
+
|
|
936
|
+
if (msg.loading) {
|
|
937
|
+
btnIcon.innerHTML = '<div class="spinner"></div>';
|
|
938
|
+
btnText.textContent = 'Thinking...';
|
|
939
|
+
} else {
|
|
940
|
+
btnIcon.textContent = '⚡';
|
|
941
|
+
btnText.textContent = 'Execute Prompt';
|
|
942
|
+
}
|
|
943
|
+
break;
|
|
944
|
+
|
|
945
|
+
case 'refined':
|
|
946
|
+
input.value = msg.text;
|
|
947
|
+
wasRefined = true;
|
|
948
|
+
resizeInput();
|
|
949
|
+
refineStatus.textContent = '✓ Refined';
|
|
950
|
+
break;
|
|
951
|
+
|
|
952
|
+
case 'refining':
|
|
953
|
+
refineBtns.forEach(b => b.disabled = msg.refining);
|
|
954
|
+
break;
|
|
955
|
+
|
|
956
|
+
case 'syncHistory':
|
|
957
|
+
chatHistory = msg.history;
|
|
958
|
+
chatContainer.innerHTML = '';
|
|
959
|
+
if (chatHistory.length === 0) {
|
|
960
|
+
chatContainer.appendChild(emptyState);
|
|
961
|
+
} else {
|
|
962
|
+
chatHistory.forEach(m => chatContainer.appendChild(renderMessage(m)));
|
|
963
|
+
}
|
|
964
|
+
chatContainer.scrollTop = chatContainer.scrollHeight;
|
|
965
|
+
break;
|
|
966
|
+
|
|
967
|
+
case 'addContext':
|
|
968
|
+
case 'addFile':
|
|
969
|
+
attachedContexts.push({
|
|
970
|
+
type: msg.command === 'addFile' ? 'file' : 'code',
|
|
971
|
+
name: msg.command === 'addFile' ? msg.fileName : msg.context.fileName,
|
|
972
|
+
language: msg.command === 'addFile' ? msg.language : msg.context.language,
|
|
973
|
+
content: msg.command === 'addFile' ? msg.content : msg.context.code
|
|
974
|
+
});
|
|
975
|
+
renderAttachments();
|
|
976
|
+
break;
|
|
977
|
+
|
|
978
|
+
case 'prefill':
|
|
979
|
+
input.value = msg.text;
|
|
980
|
+
input.focus();
|
|
981
|
+
resizeInput();
|
|
982
|
+
break;
|
|
983
|
+
|
|
984
|
+
case 'clearChat':
|
|
985
|
+
chatHistory = [];
|
|
986
|
+
chatContainer.innerHTML = '';
|
|
987
|
+
chatContainer.appendChild(emptyState);
|
|
988
|
+
break;
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
// Helper functions exposed to window
|
|
993
|
+
window.copyMsg = (id) => {
|
|
994
|
+
const msg = chatHistory.find(m => m.id === id);
|
|
995
|
+
if (msg) vscode.postMessage({ command: 'copyToClipboard', text: msg.content });
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
window.insertMsg = (id) => {
|
|
999
|
+
const msg = chatHistory.find(m => m.id === id);
|
|
1000
|
+
if (msg) vscode.postMessage({ command: 'insertInEditor', text: msg.content });
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
// Header Actions
|
|
1004
|
+
document.getElementById('newChatBtn').addEventListener('click', () => {
|
|
1005
|
+
vscode.postMessage({ command: 'clearChat' });
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
document.getElementById('exportBtn').addEventListener('click', () => {
|
|
1009
|
+
const exportText = chatHistory.map(m =>
|
|
1010
|
+
\`[\${m.role.toUpperCase()}]\\n\${m.content}\`
|
|
1011
|
+
).join('\\n\\n---\\n\\n');
|
|
1012
|
+
vscode.postMessage({ command: 'copyToClipboard', text: exportText });
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
// Init
|
|
1016
|
+
resizeInput();
|
|
1017
|
+
input.focus();
|
|
1018
|
+
</script>
|
|
1019
|
+
</body>
|
|
1020
|
+
</html>
|
|
1021
|
+
`;
|
|
1022
|
+
}
|
|
1023
|
+
}
|