copilot-liku-cli 0.0.1

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.
Files changed (71) hide show
  1. package/ARCHITECTURE.md +411 -0
  2. package/CONFIGURATION.md +302 -0
  3. package/CONTRIBUTING.md +225 -0
  4. package/ELECTRON_README.md +121 -0
  5. package/INSTALLATION.md +350 -0
  6. package/LICENSE.md +1 -0
  7. package/PROJECT_STATUS.md +229 -0
  8. package/QUICKSTART.md +255 -0
  9. package/README.md +167 -0
  10. package/TESTING.md +274 -0
  11. package/package.json +61 -0
  12. package/scripts/start.js +30 -0
  13. package/src/assets/tray-icon.png +0 -0
  14. package/src/cli/commands/agent.js +327 -0
  15. package/src/cli/commands/click.js +108 -0
  16. package/src/cli/commands/drag.js +85 -0
  17. package/src/cli/commands/find.js +109 -0
  18. package/src/cli/commands/keys.js +132 -0
  19. package/src/cli/commands/mouse.js +79 -0
  20. package/src/cli/commands/repl.js +290 -0
  21. package/src/cli/commands/screenshot.js +72 -0
  22. package/src/cli/commands/scroll.js +74 -0
  23. package/src/cli/commands/start.js +67 -0
  24. package/src/cli/commands/type.js +57 -0
  25. package/src/cli/commands/wait.js +84 -0
  26. package/src/cli/commands/window.js +104 -0
  27. package/src/cli/liku.js +249 -0
  28. package/src/cli/util/output.js +174 -0
  29. package/src/main/agents/base-agent.js +410 -0
  30. package/src/main/agents/builder.js +484 -0
  31. package/src/main/agents/index.js +62 -0
  32. package/src/main/agents/orchestrator.js +362 -0
  33. package/src/main/agents/researcher.js +511 -0
  34. package/src/main/agents/state-manager.js +344 -0
  35. package/src/main/agents/supervisor.js +365 -0
  36. package/src/main/agents/verifier.js +452 -0
  37. package/src/main/ai-service.js +1633 -0
  38. package/src/main/index.js +2208 -0
  39. package/src/main/inspect-service.js +467 -0
  40. package/src/main/system-automation.js +1186 -0
  41. package/src/main/ui-automation/config.js +76 -0
  42. package/src/main/ui-automation/core/helpers.js +41 -0
  43. package/src/main/ui-automation/core/index.js +15 -0
  44. package/src/main/ui-automation/core/powershell.js +82 -0
  45. package/src/main/ui-automation/elements/finder.js +274 -0
  46. package/src/main/ui-automation/elements/index.js +14 -0
  47. package/src/main/ui-automation/elements/wait.js +66 -0
  48. package/src/main/ui-automation/index.js +164 -0
  49. package/src/main/ui-automation/interactions/element-click.js +211 -0
  50. package/src/main/ui-automation/interactions/high-level.js +230 -0
  51. package/src/main/ui-automation/interactions/index.js +47 -0
  52. package/src/main/ui-automation/keyboard/index.js +15 -0
  53. package/src/main/ui-automation/keyboard/input.js +179 -0
  54. package/src/main/ui-automation/mouse/click.js +186 -0
  55. package/src/main/ui-automation/mouse/drag.js +88 -0
  56. package/src/main/ui-automation/mouse/index.js +30 -0
  57. package/src/main/ui-automation/mouse/movement.js +51 -0
  58. package/src/main/ui-automation/mouse/scroll.js +116 -0
  59. package/src/main/ui-automation/screenshot.js +183 -0
  60. package/src/main/ui-automation/window/index.js +23 -0
  61. package/src/main/ui-automation/window/manager.js +305 -0
  62. package/src/main/utils/time.js +62 -0
  63. package/src/main/visual-awareness.js +597 -0
  64. package/src/renderer/chat/chat.js +671 -0
  65. package/src/renderer/chat/index.html +725 -0
  66. package/src/renderer/chat/preload.js +112 -0
  67. package/src/renderer/overlay/index.html +648 -0
  68. package/src/renderer/overlay/overlay.js +782 -0
  69. package/src/renderer/overlay/preload.js +90 -0
  70. package/src/shared/grid-math.js +82 -0
  71. package/src/shared/inspect-types.js +230 -0
@@ -0,0 +1,671 @@
1
+ // ===== STATE =====
2
+ let currentMode = 'passive';
3
+ let currentProvider = 'copilot';
4
+ let currentModel = 'gpt-4o';
5
+ let totalTokens = 0;
6
+ let messages = [];
7
+ let contextItems = [];
8
+ let pendingActions = null;
9
+
10
+ // ===== ELEMENTS =====
11
+ const chatHistory = document.getElementById('chat-history');
12
+ const messageInput = document.getElementById('message-input');
13
+ const sendButton = document.getElementById('send-button');
14
+ const passiveBtn = document.getElementById('passive-btn');
15
+ const selectionBtn = document.getElementById('selection-btn');
16
+ const minimizeBtn = document.getElementById('minimize-btn');
17
+ const closeBtn = document.getElementById('close-btn');
18
+ const captureBtn = document.getElementById('capture-btn');
19
+ const contextPanel = document.getElementById('context-panel');
20
+ const contextHeader = document.getElementById('context-header');
21
+ const contextContent = document.getElementById('context-content');
22
+ const contextCount = document.getElementById('context-count');
23
+ const providerSelect = document.getElementById('provider-select');
24
+ const modelSelect = document.getElementById('model-select');
25
+ const authStatus = document.getElementById('auth-status');
26
+ const tokenCount = document.getElementById('token-count');
27
+
28
+ // ===== TOKEN ESTIMATION =====
29
+ // Rough estimate: ~4 chars per token for English text
30
+ function estimateTokens(text) {
31
+ return Math.ceil(text.length / 4);
32
+ }
33
+
34
+ function updateTokenCount(additionalTokens = 0) {
35
+ totalTokens += additionalTokens;
36
+ if (tokenCount) {
37
+ tokenCount.textContent = `${totalTokens.toLocaleString()} tokens`;
38
+ }
39
+ }
40
+
41
+ function resetTokenCount() {
42
+ totalTokens = 0;
43
+ updateTokenCount();
44
+ }
45
+
46
+ // ===== AUTH STATUS =====
47
+ function updateAuthStatus(status, provider) {
48
+ if (!authStatus) return;
49
+
50
+ authStatus.className = 'status-badge';
51
+
52
+ switch (status) {
53
+ case 'connected':
54
+ authStatus.classList.add('connected');
55
+ authStatus.textContent = `${getProviderName(provider)} Connected`;
56
+ break;
57
+ case 'pending':
58
+ authStatus.classList.add('pending');
59
+ authStatus.textContent = 'Authenticating...';
60
+ break;
61
+ case 'error':
62
+ authStatus.classList.add('disconnected');
63
+ authStatus.textContent = 'Auth Error';
64
+ break;
65
+ default:
66
+ authStatus.classList.add('disconnected');
67
+ authStatus.textContent = 'Not Connected';
68
+ }
69
+ }
70
+
71
+ function getProviderName(provider) {
72
+ const names = {
73
+ copilot: 'Copilot',
74
+ openai: 'OpenAI',
75
+ anthropic: 'Anthropic',
76
+ ollama: 'Ollama'
77
+ };
78
+ return names[provider] || provider;
79
+ }
80
+
81
+ // ===== PROVIDER FUNCTIONS =====
82
+ function setProvider(provider) {
83
+ currentProvider = provider;
84
+ if (window.electronAPI.setProvider) {
85
+ window.electronAPI.setProvider(provider);
86
+ }
87
+ // Also send as a command for backward compatibility
88
+ window.electronAPI.sendMessage(`/provider ${provider}`);
89
+ addMessage(`Switched to ${getProviderName(provider)}`, 'system');
90
+
91
+ // Show/hide model selector based on provider
92
+ updateModelSelector(provider);
93
+
94
+ // Check auth status for new provider
95
+ checkProviderAuth(provider);
96
+ }
97
+
98
+ // ===== MODEL FUNCTIONS =====
99
+ function setModel(model) {
100
+ currentModel = model;
101
+ // Send model change command
102
+ window.electronAPI.sendMessage(`/model ${model}`);
103
+ }
104
+
105
+ function updateModelSelector(provider) {
106
+ if (!modelSelect) return;
107
+
108
+ // Only show model selector for Copilot
109
+ modelSelect.style.display = provider === 'copilot' ? 'block' : 'none';
110
+ }
111
+
112
+ // ===== MESSAGE FUNCTIONS =====
113
+ function addMessage(text, type = 'agent', timestamp = Date.now(), extra = {}) {
114
+ const emptyState = chatHistory.querySelector('.empty-state');
115
+ if (emptyState) emptyState.remove();
116
+
117
+ const messageEl = document.createElement('div');
118
+ messageEl.className = `message ${type}`;
119
+ if (extra.subtype) messageEl.classList.add(extra.subtype);
120
+
121
+ const textEl = document.createElement('div');
122
+ textEl.textContent = text;
123
+ messageEl.appendChild(textEl);
124
+
125
+ const timestampEl = document.createElement('div');
126
+ timestampEl.className = 'timestamp';
127
+ timestampEl.textContent = new Date(timestamp).toLocaleTimeString();
128
+ messageEl.appendChild(timestampEl);
129
+
130
+ chatHistory.appendChild(messageEl);
131
+ chatHistory.scrollTop = chatHistory.scrollHeight;
132
+
133
+ messages.push({ text, type, timestamp, ...extra });
134
+
135
+ // Track tokens for user and agent messages
136
+ if (type === 'user' || type === 'agent') {
137
+ updateTokenCount(estimateTokens(text));
138
+ }
139
+ }
140
+
141
+ function sendMessage() {
142
+ const text = messageInput.value.trim();
143
+ if (!text) return;
144
+
145
+ addMessage(text, 'user');
146
+ window.electronAPI.sendMessage(text);
147
+
148
+ messageInput.value = '';
149
+ messageInput.style.height = 'auto';
150
+ }
151
+
152
+ // ===== MODE FUNCTIONS =====
153
+ function updateModeDisplay() {
154
+ passiveBtn.classList.toggle('active', currentMode === 'passive');
155
+ selectionBtn.classList.toggle('active', currentMode === 'selection');
156
+ }
157
+
158
+ function setMode(mode) {
159
+ currentMode = mode;
160
+ window.electronAPI.setMode(mode);
161
+ updateModeDisplay();
162
+
163
+ if (mode === 'selection') {
164
+ addMessage('Selection mode active. Click dots on overlay or scroll to zoom.', 'system');
165
+ } else {
166
+ addMessage('Passive mode. Overlay is click-through.', 'system');
167
+ }
168
+ }
169
+
170
+ // ===== CONTEXT PANEL FUNCTIONS =====
171
+ function addContextItem(data) {
172
+ contextItems.push(data);
173
+ updateContextPanel();
174
+ }
175
+
176
+ function updateContextPanel() {
177
+ contextCount.textContent = contextItems.length;
178
+ contextContent.innerHTML = '';
179
+
180
+ contextItems.forEach((item) => {
181
+ const itemEl = document.createElement('div');
182
+ itemEl.className = 'context-item';
183
+ itemEl.innerHTML = `
184
+ <span class="dot-marker"></span>
185
+ <span>${item.label}</span>
186
+ <span class="coords">(${item.x}, ${item.y})</span>
187
+ `;
188
+ contextContent.appendChild(itemEl);
189
+ });
190
+
191
+ if (contextItems.length > 0) {
192
+ contextPanel.classList.add('expanded');
193
+ }
194
+ }
195
+
196
+ function toggleContextPanel() {
197
+ contextPanel.classList.toggle('expanded');
198
+ }
199
+
200
+ // ===== WINDOW CONTROLS =====
201
+ minimizeBtn.addEventListener('click', () => {
202
+ window.electronAPI.minimizeWindow();
203
+ });
204
+
205
+ closeBtn.addEventListener('click', () => {
206
+ window.electronAPI.hideWindow();
207
+ });
208
+
209
+ // ===== CAPTURE FUNCTION =====
210
+ captureBtn.addEventListener('click', () => {
211
+ addMessage('Initiating screen capture...', 'system', Date.now(), { subtype: 'capture' });
212
+ window.electronAPI.captureScreen();
213
+ });
214
+
215
+ // ===== EVENT LISTENERS =====
216
+ sendButton.addEventListener('click', sendMessage);
217
+
218
+ messageInput.addEventListener('keydown', (e) => {
219
+ if (e.key === 'Enter' && !e.shiftKey) {
220
+ e.preventDefault();
221
+ sendMessage();
222
+ }
223
+ });
224
+
225
+ // Auto-resize textarea
226
+ messageInput.addEventListener('input', () => {
227
+ messageInput.style.height = 'auto';
228
+ messageInput.style.height = Math.min(messageInput.scrollHeight, 120) + 'px';
229
+ });
230
+
231
+ passiveBtn.addEventListener('click', () => setMode('passive'));
232
+ selectionBtn.addEventListener('click', () => setMode('selection'));
233
+ contextHeader.addEventListener('click', toggleContextPanel);
234
+
235
+ // Provider selection
236
+ if (providerSelect) {
237
+ providerSelect.addEventListener('change', (e) => {
238
+ setProvider(e.target.value);
239
+ });
240
+ }
241
+
242
+ // Model selection
243
+ if (modelSelect) {
244
+ modelSelect.addEventListener('change', (e) => {
245
+ setModel(e.target.value);
246
+ });
247
+ }
248
+
249
+ // Check provider auth status
250
+ function checkProviderAuth(provider) {
251
+ if (window.electronAPI.checkAuth) {
252
+ window.electronAPI.checkAuth(provider);
253
+ } else {
254
+ // Fallback: use /status command
255
+ window.electronAPI.sendMessage('/status');
256
+ }
257
+ }
258
+
259
+ // ===== IPC LISTENERS =====
260
+ window.electronAPI.onDotSelected((data) => {
261
+ if (data.cancelled) {
262
+ addMessage('Selection cancelled', 'system');
263
+ setMode('passive');
264
+ return;
265
+ }
266
+
267
+ addMessage(`Selected: ${data.label} at (${data.x}, ${data.y})`, 'system');
268
+ addContextItem(data);
269
+
270
+ window.electronAPI.getState().then(state => {
271
+ currentMode = state.overlayMode;
272
+ updateModeDisplay();
273
+ });
274
+ });
275
+
276
+ window.electronAPI.onAgentResponse((data) => {
277
+ removeTypingIndicator();
278
+ const msgType = data.type === 'error' ? 'system' : 'agent';
279
+
280
+ // Check if response contains actions
281
+ if (data.hasActions && data.actionData && data.actionData.actions) {
282
+ console.log('[CHAT] Received agent response with actions:', data.actionData.actions.length);
283
+
284
+ // Show the AI's thought/explanation first (without the JSON)
285
+ const cleanText = data.text.replace(/```json[\s\S]*?```/g, '').trim();
286
+ if (cleanText) {
287
+ addMessage(cleanText, msgType, data.timestamp, {
288
+ provider: data.provider,
289
+ hasVisualContext: data.hasVisualContext
290
+ });
291
+ }
292
+
293
+ // Show action confirmation UI
294
+ showActionConfirmation(data.actionData);
295
+ } else {
296
+ // Normal response without actions
297
+ addMessage(data.text, msgType, data.timestamp, {
298
+ provider: data.provider,
299
+ hasVisualContext: data.hasVisualContext
300
+ });
301
+ }
302
+ });
303
+
304
+ if (window.electronAPI.onAgentTyping) {
305
+ window.electronAPI.onAgentTyping((data) => {
306
+ if (data.isTyping) {
307
+ showTypingIndicator();
308
+ } else {
309
+ removeTypingIndicator();
310
+ }
311
+ });
312
+ }
313
+
314
+ if (window.electronAPI.onScreenCaptured) {
315
+ window.electronAPI.onScreenCaptured((data) => {
316
+ if (data.error) {
317
+ addMessage(`Capture failed: ${data.error}`, 'system');
318
+ } else {
319
+ addMessage(`Screen captured: ${data.width}x${data.height}. AI can now see your screen.`, 'system', Date.now(), { subtype: 'capture' });
320
+ }
321
+ });
322
+ }
323
+
324
+ if (window.electronAPI.onVisualContextUpdate) {
325
+ window.electronAPI.onVisualContextUpdate((data) => {
326
+ updateVisualContextIndicator(data.count);
327
+ });
328
+ }
329
+
330
+ // Auth status updates
331
+ if (window.electronAPI.onAuthStatus) {
332
+ window.electronAPI.onAuthStatus((data) => {
333
+ updateAuthStatus(data.status, data.provider);
334
+ if (data.provider && providerSelect) {
335
+ providerSelect.value = data.provider;
336
+ currentProvider = data.provider;
337
+ }
338
+ });
339
+ }
340
+
341
+ // Token usage updates from API responses
342
+ if (window.electronAPI.onTokenUsage) {
343
+ window.electronAPI.onTokenUsage((data) => {
344
+ if (data.inputTokens) {
345
+ totalTokens = data.totalTokens || totalTokens + data.inputTokens + (data.outputTokens || 0);
346
+ updateTokenCount();
347
+ }
348
+ });
349
+ }
350
+
351
+ // ===== TYPING INDICATOR =====
352
+ function showTypingIndicator() {
353
+ if (document.getElementById('typing-indicator')) return;
354
+
355
+ const typingEl = document.createElement('div');
356
+ typingEl.id = 'typing-indicator';
357
+ typingEl.className = 'message agent typing';
358
+ typingEl.innerHTML = `
359
+ <div class="typing-dots">
360
+ <span></span><span></span><span></span>
361
+ </div>
362
+ `;
363
+ chatHistory.appendChild(typingEl);
364
+ chatHistory.scrollTop = chatHistory.scrollHeight;
365
+ }
366
+
367
+ function removeTypingIndicator() {
368
+ const indicator = document.getElementById('typing-indicator');
369
+ if (indicator) indicator.remove();
370
+ }
371
+
372
+ // ===== VISUAL CONTEXT INDICATOR =====
373
+ function updateVisualContextIndicator(count) {
374
+ let indicator = document.getElementById('visual-context-indicator');
375
+ if (!indicator) {
376
+ indicator = document.createElement('div');
377
+ indicator.id = 'visual-context-indicator';
378
+ indicator.style.cssText = 'position:absolute;top:8px;right:8px;background:var(--accent-green);color:white;padding:2px 8px;border-radius:10px;font-size:10px;';
379
+ document.getElementById('toolbar').appendChild(indicator);
380
+ }
381
+ indicator.textContent = count > 0 ? `📸 ${count}` : '';
382
+ indicator.style.display = count > 0 ? 'block' : 'none';
383
+ }
384
+
385
+ // ===== INITIALIZATION =====
386
+ window.electronAPI.getState().then(state => {
387
+ currentMode = state.overlayMode;
388
+ updateModeDisplay();
389
+
390
+ // Load current provider
391
+ if (state.aiProvider) {
392
+ currentProvider = state.aiProvider;
393
+ if (providerSelect) {
394
+ providerSelect.value = state.aiProvider;
395
+ }
396
+ console.log('Current AI provider:', state.aiProvider);
397
+ updateModelSelector(state.aiProvider);
398
+ }
399
+
400
+ // Load current model
401
+ if (state.model && modelSelect) {
402
+ currentModel = state.model;
403
+ modelSelect.value = state.model;
404
+ }
405
+
406
+ // Check auth status for current provider (async - response comes via onAuthStatus)
407
+ checkProviderAuth(currentProvider);
408
+ });
409
+
410
+ // Initialize auth status display as pending until check completes
411
+ updateAuthStatus('pending', currentProvider);
412
+ updateModelSelector(currentProvider);
413
+
414
+ // ===== AGENTIC ACTION UI =====
415
+ function showActionConfirmation(actionData) {
416
+ pendingActions = actionData;
417
+
418
+ const emptyState = chatHistory.querySelector('.empty-state');
419
+ if (emptyState) emptyState.remove();
420
+
421
+ const actionEl = document.createElement('div');
422
+ actionEl.id = 'action-confirmation';
423
+ actionEl.className = 'message agent action-card';
424
+
425
+ const actionsHtml = actionData.actions.map((action, idx) => {
426
+ let icon = '🖱️';
427
+ let desc = '';
428
+
429
+ switch (action.type) {
430
+ case 'click':
431
+ icon = '🖱️';
432
+ desc = `Click at (${action.x}, ${action.y})`;
433
+ if (action.coordinate) desc = `Click ${action.coordinate}`;
434
+ break;
435
+ case 'double_click':
436
+ icon = '🖱️🖱️';
437
+ desc = `Double-click at (${action.x}, ${action.y})`;
438
+ break;
439
+ case 'right_click':
440
+ icon = '🖱️';
441
+ desc = `Right-click at (${action.x}, ${action.y})`;
442
+ break;
443
+ case 'type':
444
+ icon = '⌨️';
445
+ desc = `Type: "${action.text.substring(0, 30)}${action.text.length > 30 ? '...' : ''}"`;
446
+ break;
447
+ case 'key':
448
+ icon = '⌨️';
449
+ desc = `Press: ${action.keys}`;
450
+ break;
451
+ case 'scroll':
452
+ icon = '📜';
453
+ desc = `Scroll ${action.direction || 'down'} ${action.amount || 3} lines`;
454
+ break;
455
+ case 'wait':
456
+ icon = '⏳';
457
+ desc = `Wait ${action.ms}ms`;
458
+ break;
459
+ case 'move_mouse':
460
+ icon = '➡️';
461
+ desc = `Move to (${action.x}, ${action.y})`;
462
+ break;
463
+ case 'drag':
464
+ icon = '✋';
465
+ desc = `Drag from (${action.fromX}, ${action.fromY}) to (${action.toX}, ${action.toY})`;
466
+ break;
467
+ default:
468
+ desc = JSON.stringify(action);
469
+ }
470
+
471
+ return `<div class="action-item"><span class="action-icon">${icon}</span><span class="action-desc">${idx + 1}. ${desc}</span></div>`;
472
+ }).join('');
473
+
474
+ actionEl.innerHTML = `
475
+ <div class="action-header">
476
+ <span class="action-title">🤖 AI wants to perform ${actionData.actions.length} action${actionData.actions.length > 1 ? 's' : ''}:</span>
477
+ </div>
478
+ ${actionData.thought ? `<div class="action-thought">${actionData.thought}</div>` : ''}
479
+ <div class="action-list">${actionsHtml}</div>
480
+ <div class="action-buttons">
481
+ <button id="execute-actions-btn" class="action-btn execute">▶ Execute</button>
482
+ <button id="cancel-actions-btn" class="action-btn cancel">✕ Cancel</button>
483
+ </div>
484
+ `;
485
+
486
+ chatHistory.appendChild(actionEl);
487
+ chatHistory.scrollTop = chatHistory.scrollHeight;
488
+
489
+ // Attach event listeners
490
+ document.getElementById('execute-actions-btn').addEventListener('click', executeActions);
491
+ document.getElementById('cancel-actions-btn').addEventListener('click', cancelActions);
492
+ }
493
+
494
+ function executeActions() {
495
+ if (!pendingActions) return;
496
+
497
+ const confirmEl = document.getElementById('action-confirmation');
498
+ if (confirmEl) {
499
+ const buttons = confirmEl.querySelector('.action-buttons');
500
+ if (buttons) {
501
+ buttons.innerHTML = '<span class="executing">⏳ Executing...</span>';
502
+ }
503
+ }
504
+
505
+ window.electronAPI.executeActions(pendingActions);
506
+ pendingActions = null;
507
+ }
508
+
509
+ function cancelActions() {
510
+ const confirmEl = document.getElementById('action-confirmation');
511
+ if (confirmEl) {
512
+ confirmEl.remove();
513
+ }
514
+
515
+ window.electronAPI.cancelActions();
516
+ pendingActions = null;
517
+ addMessage('Actions cancelled', 'system');
518
+ }
519
+
520
+ function showActionProgress(data) {
521
+ let progressEl = document.getElementById('action-progress');
522
+ if (!progressEl) {
523
+ progressEl = document.createElement('div');
524
+ progressEl.id = 'action-progress';
525
+ progressEl.className = 'message system action-progress';
526
+ chatHistory.appendChild(progressEl);
527
+ }
528
+
529
+ progressEl.textContent = `⏳ ${data.message || `Executing action ${data.current} of ${data.total}...`}`;
530
+ chatHistory.scrollTop = chatHistory.scrollHeight;
531
+ }
532
+
533
+ function showActionComplete(data) {
534
+ const confirmEl = document.getElementById('action-confirmation');
535
+ if (confirmEl) confirmEl.remove();
536
+
537
+ const progressEl = document.getElementById('action-progress');
538
+ if (progressEl) progressEl.remove();
539
+
540
+ if (data.success) {
541
+ addMessage(`✅ ${data.actionsCount} action${data.actionsCount > 1 ? 's' : ''} completed successfully`, 'system');
542
+ } else {
543
+ addMessage(`❌ Action failed: ${data.error}`, 'system');
544
+ }
545
+ }
546
+
547
+ // Agentic action IPC listeners
548
+ if (window.electronAPI.onActionExecuting) {
549
+ window.electronAPI.onActionExecuting((data) => {
550
+ showActionConfirmation(data);
551
+ });
552
+ }
553
+
554
+ if (window.electronAPI.onActionProgress) {
555
+ window.electronAPI.onActionProgress((data) => {
556
+ showActionProgress(data);
557
+ });
558
+ }
559
+
560
+ if (window.electronAPI.onActionComplete) {
561
+ window.electronAPI.onActionComplete((data) => {
562
+ showActionComplete(data);
563
+ });
564
+ }
565
+
566
+ // Add typing indicator styles
567
+ const style = document.createElement('style');
568
+ style.textContent = `
569
+ .message.typing {
570
+ padding: 12px 16px;
571
+ }
572
+ .typing-dots {
573
+ display: flex;
574
+ gap: 4px;
575
+ align-items: center;
576
+ }
577
+ .typing-dots span {
578
+ width: 8px;
579
+ height: 8px;
580
+ background: var(--text-secondary);
581
+ border-radius: 50%;
582
+ animation: typing-bounce 1.4s ease-in-out infinite;
583
+ }
584
+ .typing-dots span:nth-child(2) { animation-delay: 0.2s; }
585
+ .typing-dots span:nth-child(3) { animation-delay: 0.4s; }
586
+ @keyframes typing-bounce {
587
+ 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
588
+ 30% { transform: translateY(-8px); opacity: 1; }
589
+ }
590
+
591
+ /* Action card styles */
592
+ .action-card {
593
+ background: linear-gradient(135deg, #1a1a2e, #16213e);
594
+ border: 1px solid var(--accent-blue);
595
+ border-radius: 12px;
596
+ padding: 16px;
597
+ }
598
+ .action-header {
599
+ margin-bottom: 8px;
600
+ }
601
+ .action-title {
602
+ font-weight: 600;
603
+ color: var(--accent-blue);
604
+ }
605
+ .action-thought {
606
+ font-style: italic;
607
+ color: var(--text-secondary);
608
+ margin-bottom: 12px;
609
+ padding: 8px;
610
+ background: rgba(255,255,255,0.05);
611
+ border-radius: 6px;
612
+ }
613
+ .action-list {
614
+ margin-bottom: 12px;
615
+ }
616
+ .action-item {
617
+ display: flex;
618
+ align-items: center;
619
+ gap: 8px;
620
+ padding: 6px 8px;
621
+ background: rgba(255,255,255,0.05);
622
+ border-radius: 4px;
623
+ margin-bottom: 4px;
624
+ }
625
+ .action-icon {
626
+ font-size: 16px;
627
+ }
628
+ .action-desc {
629
+ font-family: 'Consolas', monospace;
630
+ font-size: 12px;
631
+ }
632
+ .action-buttons {
633
+ display: flex;
634
+ gap: 12px;
635
+ justify-content: flex-end;
636
+ }
637
+ .action-btn {
638
+ padding: 8px 16px;
639
+ border: none;
640
+ border-radius: 6px;
641
+ cursor: pointer;
642
+ font-weight: 600;
643
+ font-size: 13px;
644
+ transition: all 0.2s;
645
+ }
646
+ .action-btn.execute {
647
+ background: var(--accent-green);
648
+ color: white;
649
+ }
650
+ .action-btn.execute:hover {
651
+ background: #00c853;
652
+ transform: scale(1.02);
653
+ }
654
+ .action-btn.cancel {
655
+ background: rgba(255,255,255,0.1);
656
+ color: var(--text-secondary);
657
+ }
658
+ .action-btn.cancel:hover {
659
+ background: rgba(255,100,100,0.2);
660
+ color: #ff6b6b;
661
+ }
662
+ .executing {
663
+ color: var(--accent-blue);
664
+ font-style: italic;
665
+ }
666
+ .action-progress {
667
+ background: rgba(0,150,255,0.1);
668
+ border-left: 3px solid var(--accent-blue);
669
+ }
670
+ `;
671
+ document.head.appendChild(style);