copilot-liku-cli 0.0.3 → 0.0.8
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/QUICKSTART.md +24 -0
- package/README.md +85 -33
- package/package.json +23 -14
- package/scripts/postinstall.js +63 -0
- package/src/cli/commands/window.js +66 -0
- package/src/main/agents/base-agent.js +15 -7
- package/src/main/agents/builder.js +211 -0
- package/src/main/agents/index.js +7 -4
- package/src/main/agents/orchestrator.js +13 -0
- package/src/main/agents/producer.js +891 -0
- package/src/main/agents/researcher.js +78 -0
- package/src/main/agents/state-manager.js +134 -2
- package/src/main/agents/verifier.js +201 -0
- package/src/main/ai-service.js +349 -35
- package/src/main/index.js +702 -113
- package/src/main/inspect-service.js +24 -1
- package/src/main/python-bridge.js +395 -0
- package/src/main/system-automation.js +876 -131
- package/src/main/ui-automation/core/ui-provider.js +99 -0
- package/src/main/ui-automation/core/uia-host.js +214 -0
- package/src/main/ui-automation/index.js +30 -0
- package/src/main/ui-automation/interactions/element-click.js +6 -6
- package/src/main/ui-automation/interactions/high-level.js +28 -6
- package/src/main/ui-automation/interactions/index.js +21 -0
- package/src/main/ui-automation/interactions/pattern-actions.js +236 -0
- package/src/main/ui-automation/window/index.js +6 -0
- package/src/main/ui-automation/window/manager.js +173 -26
- package/src/main/ui-watcher.js +401 -58
- package/src/main/visual-awareness.js +18 -1
- package/src/native/windows-uia/Program.cs +89 -0
- package/src/native/windows-uia/build.ps1 +24 -0
- package/src/native/windows-uia-dotnet/Program.cs +920 -0
- package/src/native/windows-uia-dotnet/WindowsUIA.csproj +11 -0
- package/src/native/windows-uia-dotnet/build.ps1 +24 -0
- package/src/renderer/chat/chat.js +915 -671
- package/src/renderer/chat/index.html +2 -4
- package/src/renderer/chat/preload.js +8 -1
- package/src/renderer/overlay/overlay.js +157 -8
- package/src/renderer/overlay/preload.js +4 -0
- package/src/shared/inspect-types.js +82 -6
- package/ARCHITECTURE.md +0 -411
- package/CONFIGURATION.md +0 -302
- package/CONTRIBUTING.md +0 -225
- package/ELECTRON_README.md +0 -121
- package/PROJECT_STATUS.md +0 -229
- package/TESTING.md +0 -274
|
@@ -1,671 +1,915 @@
|
|
|
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
|
-
// =====
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
//
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
function
|
|
251
|
-
if (
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
if (
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
.
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
}
|
|
577
|
-
.
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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
|
+
// ===== CHAT HISTORY PERSISTENCE =====
|
|
11
|
+
const HISTORY_KEY = 'liku-chat-history';
|
|
12
|
+
const MAX_PERSISTED_MESSAGES = 100;
|
|
13
|
+
|
|
14
|
+
function saveHistory() {
|
|
15
|
+
try {
|
|
16
|
+
const toSave = messages.slice(-MAX_PERSISTED_MESSAGES).map(m => ({
|
|
17
|
+
text: m.text,
|
|
18
|
+
type: m.type,
|
|
19
|
+
timestamp: m.timestamp
|
|
20
|
+
}));
|
|
21
|
+
localStorage.setItem(HISTORY_KEY, JSON.stringify(toSave));
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.warn('[CHAT] Failed to save history:', e);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadHistory() {
|
|
28
|
+
try {
|
|
29
|
+
const saved = localStorage.getItem(HISTORY_KEY);
|
|
30
|
+
if (saved) {
|
|
31
|
+
const loaded = JSON.parse(saved);
|
|
32
|
+
if (Array.isArray(loaded) && loaded.length > 0) {
|
|
33
|
+
// Remove empty state if present
|
|
34
|
+
const emptyState = chatHistory.querySelector('.empty-state');
|
|
35
|
+
if (emptyState) emptyState.remove();
|
|
36
|
+
|
|
37
|
+
loaded.forEach(msg => {
|
|
38
|
+
// Recreate message elements without re-saving
|
|
39
|
+
const messageEl = document.createElement('div');
|
|
40
|
+
messageEl.className = `message ${msg.type}`;
|
|
41
|
+
|
|
42
|
+
const textEl = document.createElement('div');
|
|
43
|
+
textEl.textContent = msg.text;
|
|
44
|
+
messageEl.appendChild(textEl);
|
|
45
|
+
|
|
46
|
+
const timestampEl = document.createElement('div');
|
|
47
|
+
timestampEl.className = 'timestamp';
|
|
48
|
+
timestampEl.textContent = new Date(msg.timestamp).toLocaleTimeString();
|
|
49
|
+
messageEl.appendChild(timestampEl);
|
|
50
|
+
|
|
51
|
+
chatHistory.appendChild(messageEl);
|
|
52
|
+
messages.push(msg);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
|
56
|
+
console.log(`[CHAT] Loaded ${loaded.length} messages from history`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch (e) {
|
|
60
|
+
console.warn('[CHAT] Failed to load history:', e);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function clearHistory() {
|
|
65
|
+
try {
|
|
66
|
+
localStorage.removeItem(HISTORY_KEY);
|
|
67
|
+
messages = [];
|
|
68
|
+
chatHistory.innerHTML = '';
|
|
69
|
+
console.log('[CHAT] History cleared');
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.warn('[CHAT] Failed to clear history:', e);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ===== ELEMENTS =====
|
|
76
|
+
const chatHistory = document.getElementById('chat-history');
|
|
77
|
+
const messageInput = document.getElementById('message-input');
|
|
78
|
+
const sendButton = document.getElementById('send-button');
|
|
79
|
+
const passiveBtn = document.getElementById('passive-btn');
|
|
80
|
+
const selectionBtn = document.getElementById('selection-btn');
|
|
81
|
+
const minimizeBtn = document.getElementById('minimize-btn');
|
|
82
|
+
const closeBtn = document.getElementById('close-btn');
|
|
83
|
+
const captureBtn = document.getElementById('capture-btn');
|
|
84
|
+
const contextPanel = document.getElementById('context-panel');
|
|
85
|
+
const contextHeader = document.getElementById('context-header');
|
|
86
|
+
const contextContent = document.getElementById('context-content');
|
|
87
|
+
const contextCount = document.getElementById('context-count');
|
|
88
|
+
const providerSelect = document.getElementById('provider-select');
|
|
89
|
+
const modelSelect = document.getElementById('model-select');
|
|
90
|
+
const authStatus = document.getElementById('auth-status');
|
|
91
|
+
const tokenCount = document.getElementById('token-count');
|
|
92
|
+
|
|
93
|
+
function applyElectronAppRegions() {
|
|
94
|
+
const titlebar = document.getElementById('titlebar');
|
|
95
|
+
const titlebarControls = document.getElementById('titlebar-controls');
|
|
96
|
+
|
|
97
|
+
if (titlebar) {
|
|
98
|
+
titlebar.style.setProperty('-webkit-app-region', 'drag');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (titlebarControls) {
|
|
102
|
+
titlebarControls.style.setProperty('-webkit-app-region', 'no-drag');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ===== TOKEN ESTIMATION =====
|
|
107
|
+
// Rough estimate: ~4 chars per token for English text
|
|
108
|
+
function estimateTokens(text) {
|
|
109
|
+
return Math.ceil(text.length / 4);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function updateTokenCount(additionalTokens = 0) {
|
|
113
|
+
totalTokens += additionalTokens;
|
|
114
|
+
if (tokenCount) {
|
|
115
|
+
tokenCount.textContent = `${totalTokens.toLocaleString()} tokens`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function resetTokenCount() {
|
|
120
|
+
totalTokens = 0;
|
|
121
|
+
updateTokenCount();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ===== AUTH STATUS =====
|
|
125
|
+
function updateAuthStatus(status, provider) {
|
|
126
|
+
if (!authStatus) return;
|
|
127
|
+
|
|
128
|
+
authStatus.className = 'status-badge';
|
|
129
|
+
|
|
130
|
+
switch (status) {
|
|
131
|
+
case 'connected':
|
|
132
|
+
authStatus.classList.add('connected');
|
|
133
|
+
authStatus.textContent = `${getProviderName(provider)} Connected`;
|
|
134
|
+
break;
|
|
135
|
+
case 'pending':
|
|
136
|
+
authStatus.classList.add('pending');
|
|
137
|
+
authStatus.textContent = 'Authenticating...';
|
|
138
|
+
break;
|
|
139
|
+
case 'error':
|
|
140
|
+
authStatus.classList.add('disconnected');
|
|
141
|
+
authStatus.textContent = 'Auth Error';
|
|
142
|
+
break;
|
|
143
|
+
default:
|
|
144
|
+
authStatus.classList.add('disconnected');
|
|
145
|
+
authStatus.textContent = 'Not Connected';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getProviderName(provider) {
|
|
150
|
+
const names = {
|
|
151
|
+
copilot: 'Copilot',
|
|
152
|
+
openai: 'OpenAI',
|
|
153
|
+
anthropic: 'Anthropic',
|
|
154
|
+
ollama: 'Ollama'
|
|
155
|
+
};
|
|
156
|
+
return names[provider] || provider;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ===== PROVIDER FUNCTIONS =====
|
|
160
|
+
function setProvider(provider) {
|
|
161
|
+
currentProvider = provider;
|
|
162
|
+
if (window.electronAPI.setProvider) {
|
|
163
|
+
window.electronAPI.setProvider(provider);
|
|
164
|
+
}
|
|
165
|
+
// Also send as a command for backward compatibility
|
|
166
|
+
window.electronAPI.sendMessage(`/provider ${provider}`);
|
|
167
|
+
addMessage(`Switched to ${getProviderName(provider)}`, 'system');
|
|
168
|
+
|
|
169
|
+
// Show/hide model selector based on provider
|
|
170
|
+
updateModelSelector(provider);
|
|
171
|
+
|
|
172
|
+
// Check auth status for new provider
|
|
173
|
+
checkProviderAuth(provider);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ===== MODEL FUNCTIONS =====
|
|
177
|
+
function setModel(model) {
|
|
178
|
+
currentModel = model;
|
|
179
|
+
// Send model change command
|
|
180
|
+
window.electronAPI.sendMessage(`/model ${model}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function updateModelSelector(provider) {
|
|
184
|
+
if (!modelSelect) return;
|
|
185
|
+
|
|
186
|
+
// Only show model selector for Copilot
|
|
187
|
+
modelSelect.style.display = provider === 'copilot' ? 'block' : 'none';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ===== MESSAGE FUNCTIONS =====
|
|
191
|
+
function addMessage(text, type = 'agent', timestamp = Date.now(), extra = {}) {
|
|
192
|
+
const emptyState = chatHistory.querySelector('.empty-state');
|
|
193
|
+
if (emptyState) emptyState.remove();
|
|
194
|
+
|
|
195
|
+
const messageEl = document.createElement('div');
|
|
196
|
+
messageEl.className = `message ${type}`;
|
|
197
|
+
if (extra.subtype) messageEl.classList.add(extra.subtype);
|
|
198
|
+
|
|
199
|
+
const textEl = document.createElement('div');
|
|
200
|
+
textEl.textContent = text;
|
|
201
|
+
messageEl.appendChild(textEl);
|
|
202
|
+
|
|
203
|
+
const timestampEl = document.createElement('div');
|
|
204
|
+
timestampEl.className = 'timestamp';
|
|
205
|
+
timestampEl.textContent = new Date(timestamp).toLocaleTimeString();
|
|
206
|
+
messageEl.appendChild(timestampEl);
|
|
207
|
+
|
|
208
|
+
chatHistory.appendChild(messageEl);
|
|
209
|
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
|
210
|
+
|
|
211
|
+
messages.push({ text, type, timestamp, ...extra });
|
|
212
|
+
|
|
213
|
+
// Track tokens for user and agent messages
|
|
214
|
+
if (type === 'user' || type === 'agent') {
|
|
215
|
+
updateTokenCount(estimateTokens(text));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Auto-save chat history
|
|
219
|
+
saveHistory();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ===== AGENT ROUTING =====
|
|
223
|
+
// Agent triggers - only for EXPLICIT agent invocations
|
|
224
|
+
// These should be intentional, not accidental matches on common words
|
|
225
|
+
const AGENT_TRIGGERS = {
|
|
226
|
+
research: /\b(research\s+agent|spawn.*research|investigate\s+this|gather\s+info(?:rmation)?)\b/i,
|
|
227
|
+
verify: /\b(verify\s+agent|spawn.*verif|validate\s+this|verification\s+agent)\b/i,
|
|
228
|
+
build: /\b(build\s+agent|spawn.*build|builder\s+agent|code\s+agent)\b/i,
|
|
229
|
+
produce: /(^\\s*\\/produce\\b)|\\b(agentic\\s+producer|producer\\s+agent)\\b/i,
|
|
230
|
+
orchestrate: /\b(spawn\s+(?:a\s+)?(?:sub)?agent|orchestrat|multi-?agent|agent\s+system|coordinate\s+agents?)\b/i
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
function detectAgentIntent(text) {
|
|
234
|
+
// Only trigger on explicit agent invocation phrases
|
|
235
|
+
// Avoid false positives from common words like "check", "build", "create"
|
|
236
|
+
if (AGENT_TRIGGERS.orchestrate.test(text)) return 'orchestrate';
|
|
237
|
+
if (AGENT_TRIGGERS.produce.test(text)) return 'produce';
|
|
238
|
+
if (AGENT_TRIGGERS.research.test(text)) return 'research';
|
|
239
|
+
if (AGENT_TRIGGERS.verify.test(text)) return 'verify';
|
|
240
|
+
if (AGENT_TRIGGERS.build.test(text)) return 'build';
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function extractFirstUrl(text) {
|
|
245
|
+
if (!text || typeof text !== 'string') return null;
|
|
246
|
+
const match = text.match(/https?:\/\/[^\s)]+/i);
|
|
247
|
+
return match ? match[0] : null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function parseProduceOptions(rawText) {
|
|
251
|
+
if (!rawText || typeof rawText !== 'string') {
|
|
252
|
+
return { prompt: rawText || '', options: {} };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
let prompt = rawText;
|
|
256
|
+
const options = {};
|
|
257
|
+
|
|
258
|
+
if (/--accept-generation\b|--allow-critic-fail\b/i.test(prompt)) {
|
|
259
|
+
options.allowCriticGateFailure = true;
|
|
260
|
+
prompt = prompt
|
|
261
|
+
.replace(/--accept-generation\b/ig, '')
|
|
262
|
+
.replace(/--allow-critic-fail\b/ig, '')
|
|
263
|
+
.trim();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return { prompt, options };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function routeToAgent(text, agentType) {
|
|
270
|
+
addMessage(`🤖 Routing to ${agentType} agent...`, 'system');
|
|
271
|
+
showTypingIndicator();
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
let result;
|
|
275
|
+
switch (agentType) {
|
|
276
|
+
case 'produce': {
|
|
277
|
+
const cleaned = text.replace(/^\\s*\\/produce\\b\\s*/i, '');
|
|
278
|
+
const parsed = parseProduceOptions(cleaned || text);
|
|
279
|
+
const finalPrompt = parsed.prompt || (cleaned || text);
|
|
280
|
+
const referenceUrl = extractFirstUrl(finalPrompt);
|
|
281
|
+
const options = { ...parsed.options };
|
|
282
|
+
if (referenceUrl) {
|
|
283
|
+
options.referenceUrl = referenceUrl;
|
|
284
|
+
}
|
|
285
|
+
result = await window.electronAPI.agentProduce({
|
|
286
|
+
prompt: finalPrompt,
|
|
287
|
+
options
|
|
288
|
+
});
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
case 'research':
|
|
292
|
+
result = await window.electronAPI.agentResearch({ query: text });
|
|
293
|
+
break;
|
|
294
|
+
case 'verify':
|
|
295
|
+
result = await window.electronAPI.agentVerify({ target: text });
|
|
296
|
+
break;
|
|
297
|
+
case 'build':
|
|
298
|
+
result = await window.electronAPI.agentBuild({ specification: text });
|
|
299
|
+
break;
|
|
300
|
+
case 'orchestrate':
|
|
301
|
+
default:
|
|
302
|
+
result = await window.electronAPI.agentRun({ task: text });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
removeTypingIndicator();
|
|
306
|
+
|
|
307
|
+
if (result.success) {
|
|
308
|
+
const responseText = result.result?.result?.response ||
|
|
309
|
+
result.result?.response ||
|
|
310
|
+
JSON.stringify(result.result, null, 2);
|
|
311
|
+
addMessage(`✅ Agent completed:\n${responseText}`, 'agent');
|
|
312
|
+
} else {
|
|
313
|
+
addMessage(`❌ Agent error: ${result.error}`, 'system');
|
|
314
|
+
// Fallback to regular AI
|
|
315
|
+
addMessage('Falling back to regular AI...', 'system');
|
|
316
|
+
window.electronAPI.sendMessage(text);
|
|
317
|
+
}
|
|
318
|
+
} catch (err) {
|
|
319
|
+
removeTypingIndicator();
|
|
320
|
+
console.error('[CHAT] Agent routing failed:', err);
|
|
321
|
+
addMessage(`⚠️ Agent unavailable: ${err.message}. Using regular AI.`, 'system');
|
|
322
|
+
// Fallback to regular AI
|
|
323
|
+
window.electronAPI.sendMessage(text);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function sendMessage() {
|
|
328
|
+
const text = messageInput.value.trim();
|
|
329
|
+
if (!text) return;
|
|
330
|
+
|
|
331
|
+
addMessage(text, 'user');
|
|
332
|
+
|
|
333
|
+
// Check for agent-level tasks
|
|
334
|
+
const agentType = detectAgentIntent(text);
|
|
335
|
+
|
|
336
|
+
if (agentType) {
|
|
337
|
+
routeToAgent(text, agentType);
|
|
338
|
+
} else {
|
|
339
|
+
// Regular AI message
|
|
340
|
+
window.electronAPI.sendMessage(text);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
messageInput.value = '';
|
|
344
|
+
messageInput.style.height = 'auto';
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ===== MODE FUNCTIONS =====
|
|
348
|
+
function updateModeDisplay() {
|
|
349
|
+
passiveBtn.classList.toggle('active', currentMode === 'passive');
|
|
350
|
+
selectionBtn.classList.toggle('active', currentMode === 'selection');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function setMode(mode) {
|
|
354
|
+
currentMode = mode;
|
|
355
|
+
window.electronAPI.setMode(mode);
|
|
356
|
+
updateModeDisplay();
|
|
357
|
+
|
|
358
|
+
if (mode === 'selection') {
|
|
359
|
+
addMessage('Selection mode active. Click dots on overlay or scroll to zoom.', 'system');
|
|
360
|
+
} else {
|
|
361
|
+
addMessage('Passive mode. Overlay is click-through.', 'system');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ===== CONTEXT PANEL FUNCTIONS =====
|
|
366
|
+
function addContextItem(data) {
|
|
367
|
+
contextItems.push(data);
|
|
368
|
+
updateContextPanel();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function updateContextPanel() {
|
|
372
|
+
contextCount.textContent = contextItems.length;
|
|
373
|
+
contextContent.innerHTML = '';
|
|
374
|
+
|
|
375
|
+
contextItems.forEach((item) => {
|
|
376
|
+
const itemEl = document.createElement('div');
|
|
377
|
+
itemEl.className = 'context-item';
|
|
378
|
+
itemEl.innerHTML = `
|
|
379
|
+
<span class="dot-marker"></span>
|
|
380
|
+
<span>${item.label}</span>
|
|
381
|
+
<span class="coords">(${item.x}, ${item.y})</span>
|
|
382
|
+
`;
|
|
383
|
+
contextContent.appendChild(itemEl);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
if (contextItems.length > 0) {
|
|
387
|
+
contextPanel.classList.add('expanded');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function toggleContextPanel() {
|
|
392
|
+
contextPanel.classList.toggle('expanded');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ===== WINDOW CONTROLS =====
|
|
396
|
+
minimizeBtn.addEventListener('click', () => {
|
|
397
|
+
window.electronAPI.minimizeWindow();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
closeBtn.addEventListener('click', () => {
|
|
401
|
+
window.electronAPI.hideWindow();
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
// ===== CAPTURE FUNCTION =====
|
|
405
|
+
captureBtn.addEventListener('click', () => {
|
|
406
|
+
addMessage('Initiating screen capture...', 'system', Date.now(), { subtype: 'capture' });
|
|
407
|
+
window.electronAPI.captureScreen();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// ===== EVENT LISTENERS =====
|
|
411
|
+
sendButton.addEventListener('click', sendMessage);
|
|
412
|
+
|
|
413
|
+
messageInput.addEventListener('keydown', (e) => {
|
|
414
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
415
|
+
e.preventDefault();
|
|
416
|
+
sendMessage();
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Auto-resize textarea
|
|
421
|
+
messageInput.addEventListener('input', () => {
|
|
422
|
+
messageInput.style.height = 'auto';
|
|
423
|
+
messageInput.style.height = Math.min(messageInput.scrollHeight, 120) + 'px';
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
passiveBtn.addEventListener('click', () => setMode('passive'));
|
|
427
|
+
selectionBtn.addEventListener('click', () => setMode('selection'));
|
|
428
|
+
contextHeader.addEventListener('click', toggleContextPanel);
|
|
429
|
+
|
|
430
|
+
// Provider selection
|
|
431
|
+
if (providerSelect) {
|
|
432
|
+
providerSelect.addEventListener('change', (e) => {
|
|
433
|
+
setProvider(e.target.value);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Model selection
|
|
438
|
+
if (modelSelect) {
|
|
439
|
+
modelSelect.addEventListener('change', (e) => {
|
|
440
|
+
setModel(e.target.value);
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Check provider auth status
|
|
445
|
+
function checkProviderAuth(provider) {
|
|
446
|
+
if (window.electronAPI.checkAuth) {
|
|
447
|
+
window.electronAPI.checkAuth(provider);
|
|
448
|
+
} else {
|
|
449
|
+
// Fallback: use /status command
|
|
450
|
+
window.electronAPI.sendMessage('/status');
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ===== IPC LISTENERS =====
|
|
455
|
+
window.electronAPI.onDotSelected((data) => {
|
|
456
|
+
if (data.cancelled) {
|
|
457
|
+
addMessage('Selection cancelled', 'system');
|
|
458
|
+
setMode('passive');
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
addMessage(`Selected: ${data.label} at (${data.x}, ${data.y})`, 'system');
|
|
463
|
+
addContextItem(data);
|
|
464
|
+
|
|
465
|
+
window.electronAPI.getState().then(state => {
|
|
466
|
+
currentMode = state.overlayMode;
|
|
467
|
+
updateModeDisplay();
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
window.electronAPI.onAgentResponse((data) => {
|
|
472
|
+
removeTypingIndicator();
|
|
473
|
+
const msgType = data.type === 'error' ? 'system' : 'agent';
|
|
474
|
+
|
|
475
|
+
// Check if response contains actions
|
|
476
|
+
if (data.hasActions && data.actionData && data.actionData.actions) {
|
|
477
|
+
console.log('[CHAT] Received agent response with actions:', data.actionData.actions.length);
|
|
478
|
+
|
|
479
|
+
// Show the AI's thought/explanation first (without the JSON)
|
|
480
|
+
const cleanText = data.text.replace(/```json[\s\S]*?```/g, '').trim();
|
|
481
|
+
if (cleanText) {
|
|
482
|
+
addMessage(cleanText, msgType, data.timestamp, {
|
|
483
|
+
provider: data.provider,
|
|
484
|
+
hasVisualContext: data.hasVisualContext
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Show action confirmation UI
|
|
489
|
+
showActionConfirmation(data.actionData);
|
|
490
|
+
} else {
|
|
491
|
+
// Normal response without actions
|
|
492
|
+
addMessage(data.text, msgType, data.timestamp, {
|
|
493
|
+
provider: data.provider,
|
|
494
|
+
hasVisualContext: data.hasVisualContext
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
if (window.electronAPI.onAgentTyping) {
|
|
500
|
+
window.electronAPI.onAgentTyping((data) => {
|
|
501
|
+
if (data.isTyping) {
|
|
502
|
+
showTypingIndicator();
|
|
503
|
+
} else {
|
|
504
|
+
removeTypingIndicator();
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (window.electronAPI.onScreenCaptured) {
|
|
510
|
+
window.electronAPI.onScreenCaptured((data) => {
|
|
511
|
+
if (data.error) {
|
|
512
|
+
addMessage(`Capture failed: ${data.error}`, 'system');
|
|
513
|
+
} else {
|
|
514
|
+
addMessage(`Screen captured: ${data.width}x${data.height}. AI can now see your screen.`, 'system', Date.now(), { subtype: 'capture' });
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (window.electronAPI.onVisualContextUpdate) {
|
|
520
|
+
window.electronAPI.onVisualContextUpdate((data) => {
|
|
521
|
+
updateVisualContextIndicator(data.count);
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Auth status updates
|
|
526
|
+
if (window.electronAPI.onAuthStatus) {
|
|
527
|
+
window.electronAPI.onAuthStatus((data) => {
|
|
528
|
+
updateAuthStatus(data.status, data.provider);
|
|
529
|
+
if (data.provider && providerSelect) {
|
|
530
|
+
providerSelect.value = data.provider;
|
|
531
|
+
currentProvider = data.provider;
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Token usage updates from API responses
|
|
537
|
+
if (window.electronAPI.onTokenUsage) {
|
|
538
|
+
window.electronAPI.onTokenUsage((data) => {
|
|
539
|
+
if (data.inputTokens) {
|
|
540
|
+
totalTokens = data.totalTokens || totalTokens + data.inputTokens + (data.outputTokens || 0);
|
|
541
|
+
updateTokenCount();
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// ===== TYPING INDICATOR =====
|
|
547
|
+
function showTypingIndicator() {
|
|
548
|
+
if (document.getElementById('typing-indicator')) return;
|
|
549
|
+
|
|
550
|
+
const typingEl = document.createElement('div');
|
|
551
|
+
typingEl.id = 'typing-indicator';
|
|
552
|
+
typingEl.className = 'message agent typing';
|
|
553
|
+
typingEl.innerHTML = `
|
|
554
|
+
<div class="typing-dots">
|
|
555
|
+
<span></span><span></span><span></span>
|
|
556
|
+
</div>
|
|
557
|
+
`;
|
|
558
|
+
chatHistory.appendChild(typingEl);
|
|
559
|
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function removeTypingIndicator() {
|
|
563
|
+
const indicator = document.getElementById('typing-indicator');
|
|
564
|
+
if (indicator) indicator.remove();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// ===== VISUAL CONTEXT INDICATOR =====
|
|
568
|
+
function updateVisualContextIndicator(count) {
|
|
569
|
+
let indicator = document.getElementById('visual-context-indicator');
|
|
570
|
+
if (!indicator) {
|
|
571
|
+
indicator = document.createElement('div');
|
|
572
|
+
indicator.id = 'visual-context-indicator';
|
|
573
|
+
indicator.style.cssText = 'position:absolute;top:8px;right:8px;background:var(--accent-green);color:white;padding:2px 8px;border-radius:10px;font-size:10px;';
|
|
574
|
+
document.getElementById('toolbar').appendChild(indicator);
|
|
575
|
+
}
|
|
576
|
+
indicator.textContent = count > 0 ? `📸 ${count}` : '';
|
|
577
|
+
indicator.style.display = count > 0 ? 'block' : 'none';
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// ===== INITIALIZATION =====
|
|
581
|
+
// Load persisted chat history first
|
|
582
|
+
loadHistory();
|
|
583
|
+
applyElectronAppRegions();
|
|
584
|
+
|
|
585
|
+
window.electronAPI.getState().then(state => {
|
|
586
|
+
currentMode = state.overlayMode;
|
|
587
|
+
updateModeDisplay();
|
|
588
|
+
|
|
589
|
+
// Load current provider
|
|
590
|
+
if (state.aiProvider) {
|
|
591
|
+
currentProvider = state.aiProvider;
|
|
592
|
+
if (providerSelect) {
|
|
593
|
+
providerSelect.value = state.aiProvider;
|
|
594
|
+
}
|
|
595
|
+
console.log('Current AI provider:', state.aiProvider);
|
|
596
|
+
updateModelSelector(state.aiProvider);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Load current model
|
|
600
|
+
if (state.model && modelSelect) {
|
|
601
|
+
currentModel = state.model;
|
|
602
|
+
modelSelect.value = state.model;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Check auth status for current provider (async - response comes via onAuthStatus)
|
|
606
|
+
checkProviderAuth(currentProvider);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// Initialize auth status display as pending until check completes
|
|
610
|
+
updateAuthStatus('pending', currentProvider);
|
|
611
|
+
updateModelSelector(currentProvider);
|
|
612
|
+
|
|
613
|
+
// ===== AGENTIC ACTION UI =====
|
|
614
|
+
function showActionConfirmation(actionData) {
|
|
615
|
+
pendingActions = actionData;
|
|
616
|
+
|
|
617
|
+
const emptyState = chatHistory.querySelector('.empty-state');
|
|
618
|
+
if (emptyState) emptyState.remove();
|
|
619
|
+
|
|
620
|
+
const actionEl = document.createElement('div');
|
|
621
|
+
actionEl.id = 'action-confirmation';
|
|
622
|
+
actionEl.className = 'message agent action-card';
|
|
623
|
+
|
|
624
|
+
const actionsHtml = actionData.actions.map((action, idx) => {
|
|
625
|
+
let icon = '🖱️';
|
|
626
|
+
let desc = '';
|
|
627
|
+
|
|
628
|
+
switch (action.type) {
|
|
629
|
+
case 'click':
|
|
630
|
+
icon = '🖱️';
|
|
631
|
+
desc = `Click at (${action.x}, ${action.y})`;
|
|
632
|
+
if (action.coordinate) desc = `Click ${action.coordinate}`;
|
|
633
|
+
break;
|
|
634
|
+
case 'double_click':
|
|
635
|
+
icon = '🖱️🖱️';
|
|
636
|
+
desc = `Double-click at (${action.x}, ${action.y})`;
|
|
637
|
+
break;
|
|
638
|
+
case 'right_click':
|
|
639
|
+
icon = '🖱️';
|
|
640
|
+
desc = `Right-click at (${action.x}, ${action.y})`;
|
|
641
|
+
break;
|
|
642
|
+
case 'type':
|
|
643
|
+
icon = '⌨️';
|
|
644
|
+
desc = `Type: "${action.text.substring(0, 30)}${action.text.length > 30 ? '...' : ''}"`;
|
|
645
|
+
break;
|
|
646
|
+
case 'key':
|
|
647
|
+
icon = '⌨️';
|
|
648
|
+
desc = `Press: ${action.key || action.keys || 'unknown'}`;
|
|
649
|
+
break;
|
|
650
|
+
case 'scroll':
|
|
651
|
+
icon = '📜';
|
|
652
|
+
desc = `Scroll ${action.direction || 'down'} ${action.amount || 3} lines`;
|
|
653
|
+
break;
|
|
654
|
+
case 'wait':
|
|
655
|
+
icon = '⏳';
|
|
656
|
+
desc = `Wait ${action.ms}ms`;
|
|
657
|
+
break;
|
|
658
|
+
case 'move_mouse':
|
|
659
|
+
icon = '➡️';
|
|
660
|
+
desc = `Move to (${action.x}, ${action.y})`;
|
|
661
|
+
break;
|
|
662
|
+
case 'drag':
|
|
663
|
+
icon = '✋';
|
|
664
|
+
desc = `Drag from (${action.fromX}, ${action.fromY}) to (${action.toX}, ${action.toY})`;
|
|
665
|
+
break;
|
|
666
|
+
case 'run_command':
|
|
667
|
+
icon = '💻';
|
|
668
|
+
const cmdDisplay = action.command.length > 40
|
|
669
|
+
? action.command.substring(0, 40) + '...'
|
|
670
|
+
: action.command;
|
|
671
|
+
desc = `Run: <code>${cmdDisplay}</code>`;
|
|
672
|
+
if (action.cwd) desc += ` in ${action.cwd}`;
|
|
673
|
+
break;
|
|
674
|
+
default:
|
|
675
|
+
desc = JSON.stringify(action);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return `<div class="action-item"><span class="action-icon">${icon}</span><span class="action-desc">${idx + 1}. ${desc}</span></div>`;
|
|
679
|
+
}).join('');
|
|
680
|
+
|
|
681
|
+
actionEl.innerHTML = `
|
|
682
|
+
<div class="action-header">
|
|
683
|
+
<span class="action-title">🤖 AI wants to perform ${actionData.actions.length} action${actionData.actions.length > 1 ? 's' : ''}:</span>
|
|
684
|
+
</div>
|
|
685
|
+
${actionData.thought ? `<div class="action-thought">${actionData.thought}</div>` : ''}
|
|
686
|
+
<div class="action-list">${actionsHtml}</div>
|
|
687
|
+
<div class="action-buttons">
|
|
688
|
+
<button id="execute-actions-btn" class="action-btn execute">▶ Execute</button>
|
|
689
|
+
<button id="cancel-actions-btn" class="action-btn cancel">✕ Cancel</button>
|
|
690
|
+
</div>
|
|
691
|
+
`;
|
|
692
|
+
|
|
693
|
+
chatHistory.appendChild(actionEl);
|
|
694
|
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
|
695
|
+
|
|
696
|
+
// Attach event listeners
|
|
697
|
+
document.getElementById('execute-actions-btn').addEventListener('click', executeActions);
|
|
698
|
+
document.getElementById('cancel-actions-btn').addEventListener('click', cancelActions);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function executeActions() {
|
|
702
|
+
if (!pendingActions) return;
|
|
703
|
+
|
|
704
|
+
const confirmEl = document.getElementById('action-confirmation');
|
|
705
|
+
if (confirmEl) {
|
|
706
|
+
const buttons = confirmEl.querySelector('.action-buttons');
|
|
707
|
+
if (buttons) {
|
|
708
|
+
buttons.innerHTML = '<span class="executing">⏳ Executing...</span>';
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
window.electronAPI.executeActions(pendingActions);
|
|
713
|
+
pendingActions = null;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function cancelActions() {
|
|
717
|
+
const confirmEl = document.getElementById('action-confirmation');
|
|
718
|
+
if (confirmEl) {
|
|
719
|
+
confirmEl.remove();
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
window.electronAPI.cancelActions();
|
|
723
|
+
pendingActions = null;
|
|
724
|
+
addMessage('Actions cancelled', 'system');
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function showActionProgress(data) {
|
|
728
|
+
let progressEl = document.getElementById('action-progress');
|
|
729
|
+
if (!progressEl) {
|
|
730
|
+
progressEl = document.createElement('div');
|
|
731
|
+
progressEl.id = 'action-progress';
|
|
732
|
+
progressEl.className = 'message system action-progress';
|
|
733
|
+
chatHistory.appendChild(progressEl);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
progressEl.textContent = `⏳ ${data.message || `Executing action ${data.current} of ${data.total}...`}`;
|
|
737
|
+
chatHistory.scrollTop = chatHistory.scrollHeight;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function showActionComplete(data) {
|
|
741
|
+
const confirmEl = document.getElementById('action-confirmation');
|
|
742
|
+
if (confirmEl) confirmEl.remove();
|
|
743
|
+
|
|
744
|
+
const progressEl = document.getElementById('action-progress');
|
|
745
|
+
if (progressEl) progressEl.remove();
|
|
746
|
+
|
|
747
|
+
if (data.success) {
|
|
748
|
+
addMessage(`✅ ${data.actionsCount} action${data.actionsCount > 1 ? 's' : ''} completed successfully`, 'system');
|
|
749
|
+
} else {
|
|
750
|
+
addMessage(`❌ Action failed: ${data.error}`, 'system');
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Agentic action IPC listeners
|
|
755
|
+
if (window.electronAPI.onActionExecuting) {
|
|
756
|
+
window.electronAPI.onActionExecuting((data) => {
|
|
757
|
+
showActionConfirmation(data);
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if (window.electronAPI.onActionProgress) {
|
|
762
|
+
window.electronAPI.onActionProgress((data) => {
|
|
763
|
+
showActionProgress(data);
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (window.electronAPI.onActionComplete) {
|
|
768
|
+
window.electronAPI.onActionComplete((data) => {
|
|
769
|
+
showActionComplete(data);
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// ===== AGENT EVENT HANDLING =====
|
|
774
|
+
if (window.electronAPI.onAgentEvent) {
|
|
775
|
+
window.electronAPI.onAgentEvent((data) => {
|
|
776
|
+
console.log('[CHAT] Agent event:', data.type, data);
|
|
777
|
+
switch (data.type) {
|
|
778
|
+
case 'session-started':
|
|
779
|
+
addMessage(`🚀 Agent session started: ${data.sessionId}`, 'system');
|
|
780
|
+
break;
|
|
781
|
+
case 'execution-started':
|
|
782
|
+
showTypingIndicator();
|
|
783
|
+
addMessage(`⚙️ Agent working on: ${typeof data.task === 'string' ? data.task : data.task?.description || 'task'}`, 'system');
|
|
784
|
+
break;
|
|
785
|
+
case 'execution-complete':
|
|
786
|
+
removeTypingIndicator();
|
|
787
|
+
if (data.result?.success) {
|
|
788
|
+
const response = data.result.result?.response ||
|
|
789
|
+
data.result.response ||
|
|
790
|
+
'Task completed successfully';
|
|
791
|
+
addMessage(`✅ ${response}`, 'agent');
|
|
792
|
+
} else {
|
|
793
|
+
addMessage(`❌ Agent failed: ${data.result?.error || 'Unknown error'}`, 'system');
|
|
794
|
+
}
|
|
795
|
+
break;
|
|
796
|
+
case 'execution-error':
|
|
797
|
+
removeTypingIndicator();
|
|
798
|
+
addMessage(`❌ Agent error: ${data.error}`, 'system');
|
|
799
|
+
break;
|
|
800
|
+
case 'handoff':
|
|
801
|
+
addMessage(`🔄 Agent handoff: ${data.from} → ${data.to}`, 'system');
|
|
802
|
+
break;
|
|
803
|
+
case 'agent:log':
|
|
804
|
+
console.log('[AGENT LOG]', data);
|
|
805
|
+
break;
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Add typing indicator styles
|
|
811
|
+
const style = document.createElement('style');
|
|
812
|
+
style.textContent = `
|
|
813
|
+
.message.typing {
|
|
814
|
+
padding: 12px 16px;
|
|
815
|
+
}
|
|
816
|
+
.typing-dots {
|
|
817
|
+
display: flex;
|
|
818
|
+
gap: 4px;
|
|
819
|
+
align-items: center;
|
|
820
|
+
}
|
|
821
|
+
.typing-dots span {
|
|
822
|
+
width: 8px;
|
|
823
|
+
height: 8px;
|
|
824
|
+
background: var(--text-secondary);
|
|
825
|
+
border-radius: 50%;
|
|
826
|
+
animation: typing-bounce 1.4s ease-in-out infinite;
|
|
827
|
+
}
|
|
828
|
+
.typing-dots span:nth-child(2) { animation-delay: 0.2s; }
|
|
829
|
+
.typing-dots span:nth-child(3) { animation-delay: 0.4s; }
|
|
830
|
+
@keyframes typing-bounce {
|
|
831
|
+
0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
|
|
832
|
+
30% { transform: translateY(-8px); opacity: 1; }
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/* Action card styles */
|
|
836
|
+
.action-card {
|
|
837
|
+
background: linear-gradient(135deg, #1a1a2e, #16213e);
|
|
838
|
+
border: 1px solid var(--accent-blue);
|
|
839
|
+
border-radius: 12px;
|
|
840
|
+
padding: 16px;
|
|
841
|
+
}
|
|
842
|
+
.action-header {
|
|
843
|
+
margin-bottom: 8px;
|
|
844
|
+
}
|
|
845
|
+
.action-title {
|
|
846
|
+
font-weight: 600;
|
|
847
|
+
color: var(--accent-blue);
|
|
848
|
+
}
|
|
849
|
+
.action-thought {
|
|
850
|
+
font-style: italic;
|
|
851
|
+
color: var(--text-secondary);
|
|
852
|
+
margin-bottom: 12px;
|
|
853
|
+
padding: 8px;
|
|
854
|
+
background: rgba(255,255,255,0.05);
|
|
855
|
+
border-radius: 6px;
|
|
856
|
+
}
|
|
857
|
+
.action-list {
|
|
858
|
+
margin-bottom: 12px;
|
|
859
|
+
}
|
|
860
|
+
.action-item {
|
|
861
|
+
display: flex;
|
|
862
|
+
align-items: center;
|
|
863
|
+
gap: 8px;
|
|
864
|
+
padding: 6px 8px;
|
|
865
|
+
background: rgba(255,255,255,0.05);
|
|
866
|
+
border-radius: 4px;
|
|
867
|
+
margin-bottom: 4px;
|
|
868
|
+
}
|
|
869
|
+
.action-icon {
|
|
870
|
+
font-size: 16px;
|
|
871
|
+
}
|
|
872
|
+
.action-desc {
|
|
873
|
+
font-family: 'Consolas', monospace;
|
|
874
|
+
font-size: 12px;
|
|
875
|
+
}
|
|
876
|
+
.action-buttons {
|
|
877
|
+
display: flex;
|
|
878
|
+
gap: 12px;
|
|
879
|
+
justify-content: flex-end;
|
|
880
|
+
}
|
|
881
|
+
.action-btn {
|
|
882
|
+
padding: 8px 16px;
|
|
883
|
+
border: none;
|
|
884
|
+
border-radius: 6px;
|
|
885
|
+
cursor: pointer;
|
|
886
|
+
font-weight: 600;
|
|
887
|
+
font-size: 13px;
|
|
888
|
+
transition: all 0.2s;
|
|
889
|
+
}
|
|
890
|
+
.action-btn.execute {
|
|
891
|
+
background: var(--accent-green);
|
|
892
|
+
color: white;
|
|
893
|
+
}
|
|
894
|
+
.action-btn.execute:hover {
|
|
895
|
+
background: #00c853;
|
|
896
|
+
transform: scale(1.02);
|
|
897
|
+
}
|
|
898
|
+
.action-btn.cancel {
|
|
899
|
+
background: rgba(255,255,255,0.1);
|
|
900
|
+
color: var(--text-secondary);
|
|
901
|
+
}
|
|
902
|
+
.action-btn.cancel:hover {
|
|
903
|
+
background: rgba(255,100,100,0.2);
|
|
904
|
+
color: #ff6b6b;
|
|
905
|
+
}
|
|
906
|
+
.executing {
|
|
907
|
+
color: var(--accent-blue);
|
|
908
|
+
font-style: italic;
|
|
909
|
+
}
|
|
910
|
+
.action-progress {
|
|
911
|
+
background: rgba(0,150,255,0.1);
|
|
912
|
+
border-left: 3px solid var(--accent-blue);
|
|
913
|
+
}
|
|
914
|
+
`;
|
|
915
|
+
document.head.appendChild(style);
|