Ab-Romia commited on
Commit
bcd2b44
·
verified ·
1 Parent(s): e0d345b

Update static/app.js

Browse files
Files changed (1) hide show
  1. static/app.js +165 -184
static/app.js CHANGED
@@ -2,6 +2,7 @@ class ContextAwareApp {
2
  constructor() {
3
  // Centralized DOM element references
4
  this.elements = {
 
5
  contextInput: document.getElementById('context-input'),
6
  chatInput: document.getElementById('chat-input'),
7
  sendButton: document.getElementById('send-button'),
@@ -12,6 +13,7 @@ class ContextAwareApp {
12
  taskSelect: document.getElementById('task-select'),
13
  charCount: document.getElementById('char-count'),
14
  wordCount: document.getElementById('word-count'),
 
15
  // API Key elements
16
  apiKeyInput: document.getElementById('api-key-input'),
17
  testApiKeyBtn: document.getElementById('test-api-key'),
@@ -22,6 +24,14 @@ class ContextAwareApp {
22
  toggleApiSection: document.getElementById('toggle-api-section'),
23
  apiKeyContent: document.getElementById('api-key-content'),
24
  toggleIcon: document.getElementById('toggle-icon'),
 
 
 
 
 
 
 
 
25
  };
26
 
27
  // Application state
@@ -31,7 +41,10 @@ class ContextAwareApp {
31
  isIndexed: false,
32
  apiKeyValidated: false,
33
  userApiKey: '',
 
34
  apiSectionCollapsed: false,
 
 
35
  };
36
 
37
  this.initialize();
@@ -43,13 +56,14 @@ class ContextAwareApp {
43
  async initialize() {
44
  this.addEventListeners();
45
  this.loadStoredApiKey();
 
46
 
47
  // Show welcome message
48
  this.addMessageToChat(
49
  "👋 **Welcome to ContextIQ!**\n\n" +
50
  "To get started:\n" +
51
- "1. **Enter your OpenRouter API key** in the configuration section above\n" +
52
- "2. **Add your context** in the Knowledge Base on the left\n" +
53
  "3. **Index the context** and start asking questions!\n\n" +
54
  "🆓 You can get a free API key from [openrouter.ai](https://openrouter.ai) - no credit card required!",
55
  'system'
@@ -62,7 +76,6 @@ class ContextAwareApp {
62
  * Binds all necessary event listeners to DOM elements.
63
  */
64
  addEventListeners() {
65
- // Existing listeners
66
  this.elements.indexContextBtn.addEventListener('click', () => this.handleIndexContext());
67
  this.elements.clearContextBtn.addEventListener('click', () => this.handleClearContext());
68
  this.elements.sendButton.addEventListener('click', () => this.handleSubmit());
@@ -86,8 +99,64 @@ class ContextAwareApp {
86
  }
87
  });
88
 
89
- // Toggle API section
90
- this.elements.toggleApiSection.addEventListener('click', () => this.toggleApiKeySection());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  }
92
 
93
  /**
@@ -108,16 +177,15 @@ class ContextAwareApp {
108
  onApiKeyInputChange() {
109
  const apiKey = this.elements.apiKeyInput.value.trim();
110
 
111
- // Reset validation state when user changes the key
112
  this.state.apiKeyValidated = false;
113
  this.state.userApiKey = '';
114
 
115
  if (!apiKey) {
116
  this.updateApiKeyStatus('pending', 'Enter API key and click Test');
117
  } else if (!apiKey.startsWith('sk-or-')) {
118
- this.updateApiKeyStatus('error', 'OpenRouter API keys should start with "sk-or-"');
119
  } else if (apiKey.length < 40) {
120
- this.updateApiKeyStatus('error', 'API key appears to be too short');
121
  } else {
122
  this.updateApiKeyStatus('pending', 'Click "Test Key" to validate');
123
  }
@@ -128,137 +196,72 @@ class ContextAwareApp {
128
  /**
129
  * Test the API key validity
130
  */
131
- async testApiKey(silent = false) {
132
- const apiKey = this.elements.apiKeyInput.value.trim();
133
-
134
- if (!apiKey) {
135
- this.updateApiKeyStatus('error', 'Please enter an API key');
136
- return;
137
- }
138
-
139
- if (!apiKey.startsWith('sk-or-')) {
140
- this.updateApiKeyStatus('error', 'OpenRouter API keys should start with "sk-or-"');
141
- return;
142
- }
143
-
144
- if (apiKey.length < 40) {
145
- this.updateApiKeyStatus('error', 'API key appears to be too short');
146
- return;
147
- }
148
-
149
- if (!silent) {
150
- this.updateApiKeyStatus('testing', 'Testing API key...');
151
- this.elements.testApiKeyBtn.disabled = true;
152
- this.elements.testApiKeyBtn.textContent = 'Testing...';
153
- }
154
-
155
- try {
156
- const response = await fetch('/api/v1/test-api-key', {
157
- method: 'POST',
158
- headers: {
159
- 'Content-Type': 'application/json'
160
- },
161
- body: JSON.stringify({ api_key: apiKey }),
162
- });
163
-
164
- // Parse the response regardless of HTTP status
165
- const result = await response.json();
166
-
167
- if (response.ok && result.valid) {
168
- // API key is valid
169
- this.state.apiKeyValidated = true;
170
- this.state.userApiKey = apiKey;
171
-
172
- let message = result.message;
173
- if (result.model_info && result.model_info.credits === 'insufficient') {
174
- message = 'API key is valid but has no credits. You may need to add credits to use it.';
175
- }
176
-
177
- this.updateApiKeyStatus('success', message);
178
-
179
- if (!silent) {
180
- this.addMessageToChat(
181
- "✅ **API Key Validated!** You can now use the assistant. Start by adding some context to the Knowledge Base.",
182
- 'system'
183
- );
184
- }
185
-
186
- // Auto-collapse the API section after successful validation
187
- if (!this.state.apiSectionCollapsed) {
188
- this.toggleApiKeySection();
189
- }
190
- } else {
191
- // API key is invalid
192
- this.state.apiKeyValidated = false;
193
- this.state.userApiKey = '';
194
-
195
- const errorMessage = result.message || 'Unknown error occurred';
196
- this.updateApiKeyStatus('error', errorMessage);
197
-
198
- if (!silent) {
199
- this.addMessageToChat(
200
- `❌ **API Key Invalid**: ${errorMessage}`,
201
- 'system'
202
- );
203
- }
204
- }
205
- } catch (error) {
206
- console.error('API key test error:', error);
207
- this.state.apiKeyValidated = false;
208
- this.state.userApiKey = '';
209
-
210
- const errorMessage = `Error testing API key: ${error.message}`;
211
- this.updateApiKeyStatus('error', errorMessage);
212
-
213
- if (!silent) {
214
- this.addMessageToChat(
215
- `❌ **Connection Error**: ${errorMessage}`,
216
- 'system'
217
- );
218
- }
219
- } finally {
220
- this.elements.testApiKeyBtn.disabled = false;
221
- this.elements.testApiKeyBtn.textContent = 'Test Key';
222
- this.updateUI();
223
- }
224
- }
225
 
226
  /**
227
  * Save the API key to localStorage
228
  */
229
  saveApiKey() {
230
  const apiKey = this.elements.apiKeyInput.value.trim();
231
-
232
  if (!this.state.apiKeyValidated) {
233
- this.updateApiKeyStatus('error', 'Please test the API key first');
234
- this.addMessageToChat(
235
- "⚠️ **Please test the API key first** before saving it. Click the 'Test Key' button.",
236
- 'system'
237
- );
238
- return;
239
- }
240
-
241
- if (!apiKey) {
242
- this.updateApiKeyStatus('error', 'No API key to save');
243
  return;
244
  }
245
-
246
  try {
247
  localStorage.setItem('openrouter_api_key', apiKey);
248
  this.updateApiKeyStatus('success', 'API key saved locally!');
249
-
250
- this.addMessageToChat(
251
- "💾 **API Key Saved!** Your key is stored locally and will be remembered for future sessions.",
252
- 'system'
253
- );
254
  } catch (error) {
255
- console.error('Save API key error:', error);
256
- this.updateApiKeyStatus('error', 'Failed to save API key locally');
257
-
258
- this.addMessageToChat(
259
- "❌ **Save Failed**: Could not save API key to local storage.",
260
- 'system'
261
- );
262
  }
263
  }
264
 
@@ -266,52 +269,41 @@ class ContextAwareApp {
266
  * Update API key status display
267
  */
268
  updateApiKeyStatus(status, message) {
269
- const statusElement = this.elements.apiKeyStatus;
270
- const iconElement = this.elements.apiStatusIcon;
271
- const textElement = this.elements.apiStatusText;
272
-
273
- statusElement.classList.remove('hidden');
274
- statusElement.className = 'p-3 rounded-lg text-sm';
275
 
276
  switch (status) {
277
  case 'testing':
278
- statusElement.classList.add('bg-blue-500/20', 'text-blue-300', 'border', 'border-blue-500/30');
279
- iconElement.className = 'w-3 h-3 bg-blue-500 rounded-full animate-pulse';
280
- textElement.textContent = 'Testing...';
281
  break;
282
  case 'success':
283
- statusElement.classList.add('bg-green-500/20', 'text-green-300', 'border', 'border-green-500/30');
284
- iconElement.className = 'w-3 h-3 bg-green-500 rounded-full';
285
- textElement.textContent = 'API Key Valid';
286
  break;
287
  case 'error':
288
- statusElement.classList.add('bg-red-500/20', 'text-red-300', 'border', 'border-red-500/30');
289
- iconElement.className = 'w-3 h-3 bg-red-500 rounded-full';
290
- textElement.textContent = 'API Key Required';
291
  break;
292
  case 'pending':
293
- statusElement.classList.add('bg-yellow-500/20', 'text-yellow-300', 'border', 'border-yellow-500/30');
294
- iconElement.className = 'w-3 h-3 bg-yellow-500 rounded-full';
295
- textElement.textContent = 'API Key Pending';
296
  break;
297
  }
298
-
299
- statusElement.textContent = message;
300
- }
301
 
302
- /**
303
- * Toggle API key section visibility
304
- */
305
- toggleApiKeySection() {
306
- this.state.apiSectionCollapsed = !this.state.apiSectionCollapsed;
307
-
308
- if (this.state.apiSectionCollapsed) {
309
- this.elements.apiKeyContent.style.display = 'none';
310
- this.elements.toggleIcon.style.transform = 'rotate(-90deg)';
311
- } else {
312
- this.elements.apiKeyContent.style.display = 'block';
313
- this.elements.toggleIcon.style.transform = 'rotate(0deg)';
314
- }
315
  }
316
 
317
  /**
@@ -319,11 +311,10 @@ class ContextAwareApp {
319
  */
320
  handleSubmit() {
321
  if (!this.state.apiKeyValidated) {
322
- this.updateApiKeyStatus('error', 'Please enter and test a valid API key first');
323
- this.addMessageToChat(
324
- "🔑 **API Key Required**: Please enter your OpenRouter API key in the configuration section above to use the assistant.",
325
- 'system'
326
- );
327
  return;
328
  }
329
 
@@ -340,7 +331,7 @@ class ContextAwareApp {
340
  */
341
  async handleIndexContext() {
342
  if (!this.state.apiKeyValidated) {
343
- this.updateApiKeyStatus('error', 'Please enter and test a valid API key first');
344
  return;
345
  }
346
 
@@ -383,10 +374,10 @@ class ContextAwareApp {
383
  */
384
  async handleSendPrompt() {
385
  const prompt = this.elements.chatInput.value.trim();
386
- if (prompt.length < 2 || this.state.isGenerating || !this.state.isIndexed) {
387
- if (!this.state.isIndexed) {
388
- this.showStatus('Please index your context before asking questions.', 'error');
389
- }
390
  return;
391
  }
392
 
@@ -438,7 +429,7 @@ class ContextAwareApp {
438
  return;
439
  }
440
 
441
- let userMessage = `Task: ${task_type}`;
442
  if (prompt) {
443
  userMessage += `\nPrompt: ${prompt}`;
444
  }
@@ -449,7 +440,7 @@ class ContextAwareApp {
449
 
450
  this.state.isGenerating = true;
451
  this.updateUI();
452
- this.showStatus('AI is performing the task...', 'loading');
453
 
454
  try {
455
  const response = await fetch('/api/v1/task', {
@@ -491,9 +482,7 @@ class ContextAwareApp {
491
  try {
492
  await fetch('/api/v1/clear_index', {
493
  method: 'POST',
494
- headers: {
495
- 'X-API-Key': this.state.userApiKey
496
- }
497
  });
498
  this.showStatus('Knowledge base cleared. Ready for new context.', 'success');
499
  } catch (error) {
@@ -509,7 +498,6 @@ class ContextAwareApp {
509
  const isQandA = this.elements.taskSelect.value === 'q_and_a';
510
  const hasValidApiKey = this.state.apiKeyValidated;
511
 
512
- // Disable buttons if no API key
513
  this.elements.indexContextBtn.disabled = this.state.isIndexing || !hasContext || !hasValidApiKey;
514
 
515
  if (isQandA) {
@@ -518,7 +506,6 @@ class ContextAwareApp {
518
  this.elements.sendButton.disabled = this.state.isGenerating || !hasContext || !hasValidApiKey;
519
  }
520
 
521
- // Update API key input button states
522
  const apiKeyEntered = this.elements.apiKeyInput.value.trim().length > 0;
523
  this.elements.testApiKeyBtn.disabled = !apiKeyEntered;
524
  this.elements.saveApiKeyBtn.disabled = !this.state.apiKeyValidated;
@@ -526,8 +513,6 @@ class ContextAwareApp {
526
 
527
  /**
528
  * Displays a status message to the user.
529
- * @param {string} message - The message to display.
530
- * @param {'loading'|'success'|'error'} type - The type of message.
531
  */
532
  showStatus(message, type) {
533
  const indicator = this.elements.statusIndicator;
@@ -538,7 +523,6 @@ class ContextAwareApp {
538
 
539
  indicator.innerHTML = `<span class="${colorClass}">${message}</span>`;
540
 
541
- // Hide the message after a delay unless it's a loading indicator
542
  if (type !== 'loading') {
543
  setTimeout(() => indicator.classList.add('hidden'), 5000);
544
  }
@@ -546,8 +530,6 @@ class ContextAwareApp {
546
 
547
  /**
548
  * Adds a new message to the chat display.
549
- * @param {string} message - The message content.
550
- * @param {'user'|'ai'|'system'} sender - The sender of the message.
551
  */
552
  addMessageToChat(message, sender) {
553
  const messageDiv = document.createElement('div');
@@ -565,7 +547,6 @@ class ContextAwareApp {
565
  system: 'bg-gradient-to-br from-blue-500 to-cyan-600'
566
  };
567
 
568
- // Use marked to parse markdown, but sanitize first
569
  const formattedMessage = sender === 'user' ?
570
  this.escapeHtml(message).replace(/\n/g, '<br>') :
571
  marked.parse(message);
@@ -607,4 +588,4 @@ class ContextAwareApp {
607
 
608
  document.addEventListener('DOMContentLoaded', () => {
609
  new ContextAwareApp();
610
- });
 
2
  constructor() {
3
  // Centralized DOM element references
4
  this.elements = {
5
+ // Main layout panels
6
  contextInput: document.getElementById('context-input'),
7
  chatInput: document.getElementById('chat-input'),
8
  sendButton: document.getElementById('send-button'),
 
13
  taskSelect: document.getElementById('task-select'),
14
  charCount: document.getElementById('char-count'),
15
  wordCount: document.getElementById('word-count'),
16
+
17
  // API Key elements
18
  apiKeyInput: document.getElementById('api-key-input'),
19
  testApiKeyBtn: document.getElementById('test-api-key'),
 
24
  toggleApiSection: document.getElementById('toggle-api-section'),
25
  apiKeyContent: document.getElementById('api-key-content'),
26
  toggleIcon: document.getElementById('toggle-icon'),
27
+
28
+ // Responsive collapsible section elements
29
+ kbHeader: document.getElementById('kb-header'),
30
+ kbContent: document.getElementById('kb-content'),
31
+ kbToggleIcon: document.getElementById('kb-toggle-icon'),
32
+ assistantHeader: document.getElementById('assistant-header'),
33
+ assistantContent: document.getElementById('assistant-content'),
34
+ assistantToggleIcon: document.getElementById('assistant-toggle-icon'),
35
  };
36
 
37
  // Application state
 
41
  isIndexed: false,
42
  apiKeyValidated: false,
43
  userApiKey: '',
44
+ // Collapse states for mobile view
45
  apiSectionCollapsed: false,
46
+ kbSectionCollapsed: false,
47
+ assistantSectionCollapsed: false,
48
  };
49
 
50
  this.initialize();
 
56
  async initialize() {
57
  this.addEventListeners();
58
  this.loadStoredApiKey();
59
+ this.setupResponsiveUI();
60
 
61
  // Show welcome message
62
  this.addMessageToChat(
63
  "👋 **Welcome to ContextIQ!**\n\n" +
64
  "To get started:\n" +
65
+ "1. **Enter your OpenRouter API key** in the configuration section above.\n" +
66
+ "2. **Add your context** in the Knowledge Base on the left.\n" +
67
  "3. **Index the context** and start asking questions!\n\n" +
68
  "🆓 You can get a free API key from [openrouter.ai](https://openrouter.ai) - no credit card required!",
69
  'system'
 
76
  * Binds all necessary event listeners to DOM elements.
77
  */
78
  addEventListeners() {
 
79
  this.elements.indexContextBtn.addEventListener('click', () => this.handleIndexContext());
80
  this.elements.clearContextBtn.addEventListener('click', () => this.handleClearContext());
81
  this.elements.sendButton.addEventListener('click', () => this.handleSubmit());
 
99
  }
100
  });
101
 
102
+ // Toggle listeners for collapsible sections
103
+ this.elements.toggleApiSection.addEventListener('click', () => this.toggleSection('api'));
104
+ this.elements.kbHeader.addEventListener('click', () => this.toggleSection('kb'));
105
+ this.elements.assistantHeader.addEventListener('click', () => this.toggleSection('assistant'));
106
+
107
+ // Listen for window resize to adjust UI
108
+ window.addEventListener('resize', () => this.setupResponsiveUI());
109
+ }
110
+
111
+ /**
112
+ * Sets up the initial state of collapsible sections based on screen size.
113
+ */
114
+ setupResponsiveUI() {
115
+ const isMobile = window.innerWidth < 1024;
116
+
117
+ // On mobile, collapse the knowledge base by default to show the chat first.
118
+ // On desktop, ensure everything is expanded.
119
+ this.state.kbSectionCollapsed = isMobile;
120
+ this.state.assistantSectionCollapsed = false; // Always show assistant on load
121
+
122
+ // Hide API section by default if a valid key is already loaded
123
+ if (this.state.apiKeyValidated) {
124
+ this.state.apiSectionCollapsed = true;
125
+ }
126
+
127
+ this.updateSectionVisibility('api');
128
+ this.updateSectionVisibility('kb');
129
+ this.updateSectionVisibility('assistant');
130
+ }
131
+
132
+ /**
133
+ * Toggles a specific collapsible section.
134
+ * @param {'api' | 'kb' | 'assistant'} sectionName - The name of the section to toggle.
135
+ */
136
+ toggleSection(sectionName) {
137
+ const stateKey = `${sectionName}SectionCollapsed`;
138
+ this.state[stateKey] = !this.state[stateKey];
139
+ this.updateSectionVisibility(sectionName);
140
+ }
141
+
142
+ /**
143
+ * Updates the visibility of a collapsible section based on its state.
144
+ * @param {'api' | 'kb' | 'assistant'} sectionName - The name of the section to update.
145
+ */
146
+ updateSectionVisibility(sectionName) {
147
+ const contentEl = this.elements[`${sectionName}Content`];
148
+ const toggleIconEl = this.elements[`${sectionName}ToggleIcon`];
149
+ const isCollapsed = this.state[`${sectionName}SectionCollapsed`];
150
+
151
+ if (contentEl) {
152
+ contentEl.style.display = isCollapsed ? 'none' : 'block';
153
+ if(sectionName !== 'api' && contentEl.classList.contains('lg:flex')){
154
+ contentEl.style.display = isCollapsed ? 'none' : 'flex';
155
+ }
156
+ }
157
+ if (toggleIconEl) {
158
+ toggleIconEl.style.transform = isCollapsed ? 'rotate(-90deg)' : 'rotate(0deg)';
159
+ }
160
  }
161
 
162
  /**
 
177
  onApiKeyInputChange() {
178
  const apiKey = this.elements.apiKeyInput.value.trim();
179
 
 
180
  this.state.apiKeyValidated = false;
181
  this.state.userApiKey = '';
182
 
183
  if (!apiKey) {
184
  this.updateApiKeyStatus('pending', 'Enter API key and click Test');
185
  } else if (!apiKey.startsWith('sk-or-')) {
186
+ this.updateApiKeyStatus('error', 'Key should start with "sk-or-"');
187
  } else if (apiKey.length < 40) {
188
+ this.updateApiKeyStatus('error', 'API key appears too short');
189
  } else {
190
  this.updateApiKeyStatus('pending', 'Click "Test Key" to validate');
191
  }
 
196
  /**
197
  * Test the API key validity
198
  */
199
+ async testApiKey(silent = false) {
200
+ const apiKey = this.elements.apiKeyInput.value.trim();
201
+
202
+ if (!apiKey) {
203
+ if (!silent) this.updateApiKeyStatus('error', 'Please enter an API key');
204
+ return;
205
+ }
206
+
207
+ if (!silent) {
208
+ this.updateApiKeyStatus('testing', 'Testing API key...');
209
+ this.elements.testApiKeyBtn.disabled = true;
210
+ this.elements.testApiKeyBtn.textContent = 'Testing...';
211
+ }
212
+
213
+ try {
214
+ const response = await fetch('/api/v1/test-api-key', {
215
+ method: 'POST',
216
+ headers: { 'Content-Type': 'application/json' },
217
+ body: JSON.stringify({ api_key: apiKey }),
218
+ });
219
+ const result = await response.json();
220
+
221
+ if (response.ok && result.valid) {
222
+ this.state.apiKeyValidated = true;
223
+ this.state.userApiKey = apiKey;
224
+ this.updateApiKeyStatus('success', result.message);
225
+ if (!silent) {
226
+ this.addMessageToChat("✅ **API Key Validated!** You can now use the assistant.", 'system');
227
+ this.state.apiSectionCollapsed = true;
228
+ this.updateSectionVisibility('api');
229
+ }
230
+ } else {
231
+ this.state.apiKeyValidated = false;
232
+ this.state.userApiKey = '';
233
+ this.updateApiKeyStatus('error', result.message || 'Unknown error');
234
+ if (!silent) this.addMessageToChat(`❌ **API Key Invalid**: ${result.message}`, 'system');
235
+ }
236
+ } catch (error) {
237
+ console.error('API key test error:', error);
238
+ this.state.apiKeyValidated = false;
239
+ this.state.userApiKey = '';
240
+ const errorMessage = `Error testing API key: ${error.message}`;
241
+ this.updateApiKeyStatus('error', errorMessage);
242
+ if (!silent) this.addMessageToChat(`❌ **Connection Error**: ${errorMessage}`, 'system');
243
+ } finally {
244
+ this.elements.testApiKeyBtn.disabled = false;
245
+ this.elements.testApiKeyBtn.textContent = 'Test Key';
246
+ this.updateUI();
247
+ }
248
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
  /**
251
  * Save the API key to localStorage
252
  */
253
  saveApiKey() {
254
  const apiKey = this.elements.apiKeyInput.value.trim();
 
255
  if (!this.state.apiKeyValidated) {
256
+ this.addMessageToChat("⚠️ **Please test the API key first** before saving.", 'system');
 
 
 
 
 
 
 
 
 
257
  return;
258
  }
 
259
  try {
260
  localStorage.setItem('openrouter_api_key', apiKey);
261
  this.updateApiKeyStatus('success', 'API key saved locally!');
262
+ this.addMessageToChat("💾 **API Key Saved!** It will be remembered for future sessions.", 'system');
 
 
 
 
263
  } catch (error) {
264
+ this.addMessageToChat("❌ **Save Failed**: Could not save API key to local storage.", 'system');
 
 
 
 
 
 
265
  }
266
  }
267
 
 
269
  * Update API key status display
270
  */
271
  updateApiKeyStatus(status, message) {
272
+ const statusEl = this.elements.apiStatusText;
273
+ const iconEl = this.elements.apiStatusIcon;
 
 
 
 
274
 
275
  switch (status) {
276
  case 'testing':
277
+ iconEl.className = 'w-3 h-3 bg-blue-500 rounded-full animate-pulse flex-shrink-0';
278
+ statusEl.textContent = 'Testing...';
 
279
  break;
280
  case 'success':
281
+ iconEl.className = 'w-3 h-3 bg-green-500 rounded-full flex-shrink-0';
282
+ statusEl.textContent = 'API Key Valid';
 
283
  break;
284
  case 'error':
285
+ iconEl.className = 'w-3 h-3 bg-red-500 rounded-full flex-shrink-0';
286
+ statusEl.textContent = 'API Key Invalid';
 
287
  break;
288
  case 'pending':
289
+ iconEl.className = 'w-3 h-3 bg-yellow-500 rounded-full flex-shrink-0';
290
+ statusEl.textContent = 'API Key Pending';
 
291
  break;
292
  }
 
 
 
293
 
294
+ // Also update the detailed status message box
295
+ const detailedStatusEl = this.elements.apiKeyStatus;
296
+ detailedStatusEl.textContent = message;
297
+ detailedStatusEl.classList.remove('hidden');
298
+ detailedStatusEl.className = 'p-3 rounded-lg text-sm ';
299
+
300
+ const colors = {
301
+ testing: 'bg-blue-500/20 text-blue-300 border border-blue-500/30',
302
+ success: 'bg-green-500/20 text-green-300 border border-green-500/30',
303
+ error: 'bg-red-500/20 text-red-300 border border-red-500/30',
304
+ pending: 'bg-yellow-500/20 text-yellow-300 border border-yellow-500/30',
305
+ };
306
+ detailedStatusEl.classList.add(colors[status]);
307
  }
308
 
309
  /**
 
311
  */
312
  handleSubmit() {
313
  if (!this.state.apiKeyValidated) {
314
+ this.addMessageToChat("🔑 **API Key Required**: Please enter and test your API key first.", 'system');
315
+ this.state.apiSectionCollapsed = false;
316
+ this.updateSectionVisibility('api');
317
+ this.elements.apiKeyInput.focus();
 
318
  return;
319
  }
320
 
 
331
  */
332
  async handleIndexContext() {
333
  if (!this.state.apiKeyValidated) {
334
+ this.addMessageToChat("🔑 **API Key Required**: Please validate your API key before indexing.", 'system');
335
  return;
336
  }
337
 
 
374
  */
375
  async handleSendPrompt() {
376
  const prompt = this.elements.chatInput.value.trim();
377
+ if (prompt.length < 2 || this.state.isGenerating) return;
378
+
379
+ if (!this.state.isIndexed) {
380
+ this.showStatus('Please index your context before asking questions.', 'error');
381
  return;
382
  }
383
 
 
429
  return;
430
  }
431
 
432
+ let userMessage = `Task: ${task_type.charAt(0).toUpperCase() + task_type.slice(1)}`;
433
  if (prompt) {
434
  userMessage += `\nPrompt: ${prompt}`;
435
  }
 
440
 
441
  this.state.isGenerating = true;
442
  this.updateUI();
443
+ this.showStatus(`AI is performing task: ${task_type}...`, 'loading');
444
 
445
  try {
446
  const response = await fetch('/api/v1/task', {
 
482
  try {
483
  await fetch('/api/v1/clear_index', {
484
  method: 'POST',
485
+ headers: { 'X-API-Key': this.state.userApiKey }
 
 
486
  });
487
  this.showStatus('Knowledge base cleared. Ready for new context.', 'success');
488
  } catch (error) {
 
498
  const isQandA = this.elements.taskSelect.value === 'q_and_a';
499
  const hasValidApiKey = this.state.apiKeyValidated;
500
 
 
501
  this.elements.indexContextBtn.disabled = this.state.isIndexing || !hasContext || !hasValidApiKey;
502
 
503
  if (isQandA) {
 
506
  this.elements.sendButton.disabled = this.state.isGenerating || !hasContext || !hasValidApiKey;
507
  }
508
 
 
509
  const apiKeyEntered = this.elements.apiKeyInput.value.trim().length > 0;
510
  this.elements.testApiKeyBtn.disabled = !apiKeyEntered;
511
  this.elements.saveApiKeyBtn.disabled = !this.state.apiKeyValidated;
 
513
 
514
  /**
515
  * Displays a status message to the user.
 
 
516
  */
517
  showStatus(message, type) {
518
  const indicator = this.elements.statusIndicator;
 
523
 
524
  indicator.innerHTML = `<span class="${colorClass}">${message}</span>`;
525
 
 
526
  if (type !== 'loading') {
527
  setTimeout(() => indicator.classList.add('hidden'), 5000);
528
  }
 
530
 
531
  /**
532
  * Adds a new message to the chat display.
 
 
533
  */
534
  addMessageToChat(message, sender) {
535
  const messageDiv = document.createElement('div');
 
547
  system: 'bg-gradient-to-br from-blue-500 to-cyan-600'
548
  };
549
 
 
550
  const formattedMessage = sender === 'user' ?
551
  this.escapeHtml(message).replace(/\n/g, '<br>') :
552
  marked.parse(message);
 
588
 
589
  document.addEventListener('DOMContentLoaded', () => {
590
  new ContextAwareApp();
591
+ });