claude-code-templates 1.24.16 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-claude-config.js +1 -0
- package/package.json +2 -1
- package/src/analytics-web/chats_mobile.html +2147 -138
- package/src/chats-mobile.js +296 -1
- package/src/index.js +44 -1
- package/src/session-sharing.js +396 -0
|
@@ -183,6 +183,605 @@
|
|
|
183
183
|
color: var(--text-secondary);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/* Advanced Search Panel - Telegram Style */
|
|
187
|
+
.search-panel-overlay {
|
|
188
|
+
position: fixed;
|
|
189
|
+
top: 0;
|
|
190
|
+
left: 0;
|
|
191
|
+
right: 0;
|
|
192
|
+
bottom: 0;
|
|
193
|
+
background: rgba(0, 0, 0, 0.5);
|
|
194
|
+
z-index: 1000;
|
|
195
|
+
display: none;
|
|
196
|
+
animation: fadeIn 0.2s ease;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.search-panel-overlay.active {
|
|
200
|
+
display: flex;
|
|
201
|
+
align-items: flex-start;
|
|
202
|
+
justify-content: center;
|
|
203
|
+
padding-top: 10vh;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@keyframes fadeIn {
|
|
207
|
+
from { opacity: 0; }
|
|
208
|
+
to { opacity: 1; }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.search-panel {
|
|
212
|
+
background: var(--bg-secondary);
|
|
213
|
+
border-radius: 12px;
|
|
214
|
+
width: 90%;
|
|
215
|
+
max-width: 600px;
|
|
216
|
+
max-height: 80vh;
|
|
217
|
+
overflow-y: auto;
|
|
218
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|
219
|
+
animation: slideDown 0.3s ease;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@keyframes slideDown {
|
|
223
|
+
from {
|
|
224
|
+
opacity: 0;
|
|
225
|
+
transform: translateY(-20px);
|
|
226
|
+
}
|
|
227
|
+
to {
|
|
228
|
+
opacity: 1;
|
|
229
|
+
transform: translateY(0);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.search-panel-header {
|
|
234
|
+
padding: 20px;
|
|
235
|
+
border-bottom: 1px solid var(--border-primary);
|
|
236
|
+
display: flex;
|
|
237
|
+
align-items: center;
|
|
238
|
+
justify-content: space-between;
|
|
239
|
+
position: sticky;
|
|
240
|
+
top: 0;
|
|
241
|
+
background: var(--bg-secondary);
|
|
242
|
+
z-index: 10;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.search-panel-title {
|
|
246
|
+
font-size: 1.25rem;
|
|
247
|
+
font-weight: 600;
|
|
248
|
+
color: var(--text-primary);
|
|
249
|
+
display: flex;
|
|
250
|
+
align-items: center;
|
|
251
|
+
gap: 10px;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.search-panel-close {
|
|
255
|
+
background: none;
|
|
256
|
+
border: none;
|
|
257
|
+
color: var(--text-secondary);
|
|
258
|
+
font-size: 1.5rem;
|
|
259
|
+
cursor: pointer;
|
|
260
|
+
padding: 4px 8px;
|
|
261
|
+
border-radius: 4px;
|
|
262
|
+
transition: all 0.2s ease;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.search-panel-close:hover {
|
|
266
|
+
background: var(--bg-tertiary);
|
|
267
|
+
color: var(--text-primary);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.search-panel-body {
|
|
271
|
+
padding: 20px;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.search-filter-group {
|
|
275
|
+
margin-bottom: 24px;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.search-filter-label {
|
|
279
|
+
display: block;
|
|
280
|
+
color: var(--text-secondary);
|
|
281
|
+
font-size: 0.875rem;
|
|
282
|
+
font-weight: 500;
|
|
283
|
+
margin-bottom: 8px;
|
|
284
|
+
text-transform: uppercase;
|
|
285
|
+
letter-spacing: 0.5px;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.search-filter-input {
|
|
289
|
+
width: 100%;
|
|
290
|
+
padding: 12px 16px;
|
|
291
|
+
background: var(--bg-tertiary);
|
|
292
|
+
border: 1px solid var(--border-primary);
|
|
293
|
+
border-radius: 8px;
|
|
294
|
+
color: var(--text-primary);
|
|
295
|
+
font-size: 1rem;
|
|
296
|
+
outline: none;
|
|
297
|
+
transition: all 0.2s ease;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.search-filter-input:focus {
|
|
301
|
+
border-color: var(--terminal-orange);
|
|
302
|
+
background: var(--bg-primary);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.search-filter-input::placeholder {
|
|
306
|
+
color: var(--text-secondary);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.search-filter-date-range {
|
|
310
|
+
display: grid;
|
|
311
|
+
grid-template-columns: 1fr 1fr;
|
|
312
|
+
gap: 12px;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.search-filter-date-wrapper {
|
|
316
|
+
display: flex;
|
|
317
|
+
flex-direction: column;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.search-filter-date-label {
|
|
321
|
+
font-size: 0.75rem;
|
|
322
|
+
color: var(--text-secondary);
|
|
323
|
+
margin-bottom: 4px;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.search-panel-actions {
|
|
327
|
+
padding: 16px 20px;
|
|
328
|
+
border-top: 1px solid var(--border-primary);
|
|
329
|
+
display: flex;
|
|
330
|
+
gap: 12px;
|
|
331
|
+
position: sticky;
|
|
332
|
+
bottom: 0;
|
|
333
|
+
background: var(--bg-secondary);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.search-btn {
|
|
337
|
+
flex: 1;
|
|
338
|
+
padding: 14px 24px;
|
|
339
|
+
border: none;
|
|
340
|
+
border-radius: 8px;
|
|
341
|
+
font-size: 1rem;
|
|
342
|
+
font-weight: 600;
|
|
343
|
+
cursor: pointer;
|
|
344
|
+
transition: all 0.2s ease;
|
|
345
|
+
text-transform: uppercase;
|
|
346
|
+
letter-spacing: 0.5px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.search-btn-primary {
|
|
350
|
+
background: var(--terminal-orange);
|
|
351
|
+
color: white;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.search-btn-primary:hover {
|
|
355
|
+
background: var(--terminal-orange-hover);
|
|
356
|
+
transform: translateY(-1px);
|
|
357
|
+
box-shadow: 0 4px 12px rgba(255, 107, 53, 0.3);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.search-btn-secondary {
|
|
361
|
+
background: var(--bg-tertiary);
|
|
362
|
+
color: var(--text-primary);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.search-btn-secondary:hover {
|
|
366
|
+
background: var(--border-secondary);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.search-btn:active {
|
|
370
|
+
transform: translateY(0);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.search-filter-icon {
|
|
374
|
+
display: inline-block;
|
|
375
|
+
margin-right: 6px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.advanced-search-toggle {
|
|
379
|
+
background: var(--bg-tertiary);
|
|
380
|
+
border: 1px solid var(--border-primary);
|
|
381
|
+
color: var(--terminal-orange);
|
|
382
|
+
padding: 8px 12px;
|
|
383
|
+
border-radius: 20px;
|
|
384
|
+
cursor: pointer;
|
|
385
|
+
font-size: 0.875rem;
|
|
386
|
+
margin-left: 8px;
|
|
387
|
+
transition: all 0.2s ease;
|
|
388
|
+
display: inline-flex;
|
|
389
|
+
align-items: center;
|
|
390
|
+
gap: 6px;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.advanced-search-toggle:hover {
|
|
394
|
+
background: var(--terminal-orange);
|
|
395
|
+
color: white;
|
|
396
|
+
border-color: var(--terminal-orange);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.search-results-info {
|
|
400
|
+
background: var(--bg-tertiary);
|
|
401
|
+
border-radius: 8px;
|
|
402
|
+
padding: 12px 16px;
|
|
403
|
+
margin-bottom: 16px;
|
|
404
|
+
color: var(--text-secondary);
|
|
405
|
+
font-size: 0.875rem;
|
|
406
|
+
display: none;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.search-results-info.active {
|
|
410
|
+
display: block;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.search-results-count {
|
|
414
|
+
color: var(--terminal-orange);
|
|
415
|
+
font-weight: 600;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.clear-filters-btn {
|
|
419
|
+
background: none;
|
|
420
|
+
border: none;
|
|
421
|
+
color: var(--terminal-orange);
|
|
422
|
+
cursor: pointer;
|
|
423
|
+
font-size: 0.875rem;
|
|
424
|
+
text-decoration: underline;
|
|
425
|
+
padding: 0;
|
|
426
|
+
margin-left: 8px;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.clear-filters-btn:hover {
|
|
430
|
+
color: var(--terminal-orange-hover);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.filter-tag {
|
|
434
|
+
display: inline-flex;
|
|
435
|
+
align-items: center;
|
|
436
|
+
gap: 6px;
|
|
437
|
+
background: var(--bg-primary);
|
|
438
|
+
border: 1px solid var(--border-secondary);
|
|
439
|
+
border-radius: 12px;
|
|
440
|
+
padding: 4px 10px;
|
|
441
|
+
font-size: 0.75rem;
|
|
442
|
+
color: var(--text-primary);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.filter-tag-icon {
|
|
446
|
+
font-size: 0.7rem;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.filter-tag-label {
|
|
450
|
+
color: var(--text-secondary);
|
|
451
|
+
margin-right: 2px;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.filter-tag-value {
|
|
455
|
+
color: var(--terminal-orange);
|
|
456
|
+
font-weight: 500;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/* Folder Browser */
|
|
460
|
+
.folder-browser-wrapper {
|
|
461
|
+
position: relative;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.folder-browser-toggle {
|
|
465
|
+
position: absolute;
|
|
466
|
+
right: 12px;
|
|
467
|
+
top: 50%;
|
|
468
|
+
transform: translateY(-50%);
|
|
469
|
+
background: var(--terminal-orange);
|
|
470
|
+
border: none;
|
|
471
|
+
color: white;
|
|
472
|
+
padding: 6px 12px;
|
|
473
|
+
border-radius: 6px;
|
|
474
|
+
cursor: pointer;
|
|
475
|
+
font-size: 0.875rem;
|
|
476
|
+
transition: all 0.2s ease;
|
|
477
|
+
z-index: 1;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.folder-browser-toggle:hover {
|
|
481
|
+
background: var(--terminal-orange-hover);
|
|
482
|
+
transform: translateY(-50%) scale(1.05);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.folder-browser-dropdown {
|
|
486
|
+
position: absolute;
|
|
487
|
+
top: calc(100% + 8px);
|
|
488
|
+
left: 0;
|
|
489
|
+
right: 0;
|
|
490
|
+
background: var(--bg-primary);
|
|
491
|
+
border: 1px solid var(--border-secondary);
|
|
492
|
+
border-radius: 8px;
|
|
493
|
+
max-height: 300px;
|
|
494
|
+
overflow-y: auto;
|
|
495
|
+
z-index: 100;
|
|
496
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
|
497
|
+
display: none;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.folder-browser-dropdown.active {
|
|
501
|
+
display: block;
|
|
502
|
+
animation: slideDown 0.2s ease;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.folder-browser-header {
|
|
506
|
+
padding: 12px 16px;
|
|
507
|
+
background: var(--bg-secondary);
|
|
508
|
+
border-bottom: 1px solid var(--border-primary);
|
|
509
|
+
position: sticky;
|
|
510
|
+
top: 0;
|
|
511
|
+
z-index: 10;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.folder-browser-search {
|
|
515
|
+
width: 100%;
|
|
516
|
+
padding: 8px 12px;
|
|
517
|
+
background: var(--bg-tertiary);
|
|
518
|
+
border: 1px solid var(--border-primary);
|
|
519
|
+
border-radius: 6px;
|
|
520
|
+
color: var(--text-primary);
|
|
521
|
+
font-size: 0.875rem;
|
|
522
|
+
outline: none;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.folder-browser-search:focus {
|
|
526
|
+
border-color: var(--terminal-orange);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.folder-browser-list {
|
|
530
|
+
padding: 4px 0;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.folder-item {
|
|
534
|
+
padding: 12px 16px;
|
|
535
|
+
cursor: pointer;
|
|
536
|
+
transition: background 0.2s ease;
|
|
537
|
+
display: flex;
|
|
538
|
+
align-items: center;
|
|
539
|
+
gap: 10px;
|
|
540
|
+
border-bottom: 1px solid var(--border-primary);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.folder-item:last-child {
|
|
544
|
+
border-bottom: none;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.folder-item:hover {
|
|
548
|
+
background: var(--bg-tertiary);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.folder-item.selected {
|
|
552
|
+
background: var(--terminal-orange);
|
|
553
|
+
color: white;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.folder-icon {
|
|
557
|
+
font-size: 1.2rem;
|
|
558
|
+
flex-shrink: 0;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.folder-path {
|
|
562
|
+
flex: 1;
|
|
563
|
+
font-size: 0.875rem;
|
|
564
|
+
overflow: hidden;
|
|
565
|
+
text-overflow: ellipsis;
|
|
566
|
+
white-space: nowrap;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.folder-item.selected .folder-path {
|
|
570
|
+
color: white;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.folder-count {
|
|
574
|
+
font-size: 0.75rem;
|
|
575
|
+
color: var(--text-secondary);
|
|
576
|
+
background: var(--bg-tertiary);
|
|
577
|
+
padding: 2px 8px;
|
|
578
|
+
border-radius: 10px;
|
|
579
|
+
flex-shrink: 0;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.folder-item.selected .folder-count {
|
|
583
|
+
background: rgba(255, 255, 255, 0.2);
|
|
584
|
+
color: white;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.folder-browser-empty {
|
|
588
|
+
padding: 24px 16px;
|
|
589
|
+
text-align: center;
|
|
590
|
+
color: var(--text-secondary);
|
|
591
|
+
font-size: 0.875rem;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/* In-Conversation Search */
|
|
595
|
+
.chat-search-bar {
|
|
596
|
+
display: none;
|
|
597
|
+
background: var(--bg-secondary);
|
|
598
|
+
border-bottom: 1px solid var(--border-primary);
|
|
599
|
+
padding: 12px 16px;
|
|
600
|
+
align-items: center;
|
|
601
|
+
gap: 12px;
|
|
602
|
+
animation: slideDown 0.2s ease;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.chat-search-bar.active {
|
|
606
|
+
display: flex;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.chat-search-input-wrapper {
|
|
610
|
+
flex: 1;
|
|
611
|
+
position: relative;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
.chat-search-input {
|
|
615
|
+
width: 100%;
|
|
616
|
+
padding: 8px 12px;
|
|
617
|
+
background: var(--bg-tertiary);
|
|
618
|
+
border: 1px solid var(--border-primary);
|
|
619
|
+
border-radius: 6px;
|
|
620
|
+
color: var(--text-primary);
|
|
621
|
+
font-size: 0.875rem;
|
|
622
|
+
outline: none;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.chat-search-input:focus {
|
|
626
|
+
border-color: var(--terminal-orange);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.chat-search-controls {
|
|
630
|
+
display: flex;
|
|
631
|
+
align-items: center;
|
|
632
|
+
gap: 8px;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.chat-search-counter {
|
|
636
|
+
font-size: 0.875rem;
|
|
637
|
+
color: var(--text-secondary);
|
|
638
|
+
white-space: nowrap;
|
|
639
|
+
min-width: 60px;
|
|
640
|
+
text-align: center;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.chat-search-counter.has-results {
|
|
644
|
+
color: var(--terminal-orange);
|
|
645
|
+
font-weight: 500;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.chat-search-nav-btn {
|
|
649
|
+
background: var(--bg-tertiary);
|
|
650
|
+
border: 1px solid var(--border-primary);
|
|
651
|
+
color: var(--text-primary);
|
|
652
|
+
padding: 6px 10px;
|
|
653
|
+
border-radius: 4px;
|
|
654
|
+
cursor: pointer;
|
|
655
|
+
font-size: 1rem;
|
|
656
|
+
transition: all 0.2s ease;
|
|
657
|
+
display: flex;
|
|
658
|
+
align-items: center;
|
|
659
|
+
justify-content: center;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.chat-search-nav-btn:hover:not(:disabled) {
|
|
663
|
+
background: var(--terminal-orange);
|
|
664
|
+
border-color: var(--terminal-orange);
|
|
665
|
+
color: white;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.chat-search-nav-btn:disabled {
|
|
669
|
+
opacity: 0.3;
|
|
670
|
+
cursor: not-allowed;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.chat-search-close-btn {
|
|
674
|
+
background: none;
|
|
675
|
+
border: none;
|
|
676
|
+
color: var(--text-secondary);
|
|
677
|
+
padding: 4px 8px;
|
|
678
|
+
border-radius: 4px;
|
|
679
|
+
cursor: pointer;
|
|
680
|
+
font-size: 1.2rem;
|
|
681
|
+
transition: all 0.2s ease;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.chat-search-close-btn:hover {
|
|
685
|
+
background: var(--bg-tertiary);
|
|
686
|
+
color: var(--text-primary);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/* Message highlight */
|
|
690
|
+
.message-highlight {
|
|
691
|
+
background: var(--terminal-orange);
|
|
692
|
+
color: white;
|
|
693
|
+
padding: 2px 4px;
|
|
694
|
+
border-radius: 3px;
|
|
695
|
+
font-weight: 500;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.message-current-highlight {
|
|
699
|
+
background: #ffaa00;
|
|
700
|
+
color: white;
|
|
701
|
+
padding: 2px 4px;
|
|
702
|
+
border-radius: 3px;
|
|
703
|
+
font-weight: 600;
|
|
704
|
+
box-shadow: 0 0 8px rgba(255, 170, 0, 0.6);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/* Action buttons group - unified design with orange accent */
|
|
708
|
+
.action-buttons-group {
|
|
709
|
+
display: inline-flex;
|
|
710
|
+
border-radius: 8px;
|
|
711
|
+
overflow: hidden;
|
|
712
|
+
border: 2px solid rgba(255, 170, 0, 0.4);
|
|
713
|
+
background: rgba(255, 170, 0, 0.08);
|
|
714
|
+
box-shadow: 0 2px 8px rgba(255, 170, 0, 0.15);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
.action-btn {
|
|
718
|
+
background: transparent;
|
|
719
|
+
border: none;
|
|
720
|
+
border-right: 1px solid rgba(255, 170, 0, 0.2);
|
|
721
|
+
color: var(--text-primary);
|
|
722
|
+
padding: 10px 18px;
|
|
723
|
+
cursor: pointer;
|
|
724
|
+
font-size: 0.875rem;
|
|
725
|
+
font-weight: 600;
|
|
726
|
+
transition: all 0.2s ease;
|
|
727
|
+
display: flex;
|
|
728
|
+
align-items: center;
|
|
729
|
+
gap: 8px;
|
|
730
|
+
white-space: nowrap;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.action-btn:last-child {
|
|
734
|
+
border-right: none;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
.action-btn svg {
|
|
738
|
+
width: 15px;
|
|
739
|
+
height: 15px;
|
|
740
|
+
transition: all 0.2s ease;
|
|
741
|
+
stroke: var(--text-primary);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
.action-btn:hover {
|
|
745
|
+
background: rgba(255, 170, 0, 0.12);
|
|
746
|
+
transform: translateY(-1px);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.action-btn.resume-btn:hover {
|
|
750
|
+
background: rgba(63, 185, 80, 0.15);
|
|
751
|
+
color: rgba(63, 185, 80, 1);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.action-btn.resume-btn:hover svg {
|
|
755
|
+
stroke: rgba(63, 185, 80, 1);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.action-btn.share-btn:hover {
|
|
759
|
+
background: rgba(59, 130, 246, 0.15);
|
|
760
|
+
color: rgba(59, 130, 246, 1);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.action-btn.share-btn:hover svg {
|
|
764
|
+
stroke: rgba(59, 130, 246, 1);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
.action-btn.search-btn:hover {
|
|
768
|
+
background: rgba(251, 146, 60, 0.15);
|
|
769
|
+
color: rgba(251, 146, 60, 1);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
.action-btn.search-btn:hover svg {
|
|
773
|
+
stroke: rgba(251, 146, 60, 1);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.action-btn.search-btn.active {
|
|
777
|
+
background: rgba(251, 146, 60, 0.2);
|
|
778
|
+
color: rgba(251, 146, 60, 1);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.action-btn.search-btn.active svg {
|
|
782
|
+
stroke: rgba(251, 146, 60, 1);
|
|
783
|
+
}
|
|
784
|
+
|
|
186
785
|
/* Conversations list */
|
|
187
786
|
.conversations-list {
|
|
188
787
|
flex: 1;
|
|
@@ -268,6 +867,88 @@
|
|
|
268
867
|
margin-top: 4px;
|
|
269
868
|
}
|
|
270
869
|
|
|
870
|
+
/* Project grouping styles */
|
|
871
|
+
.project-group {
|
|
872
|
+
border-bottom: 1px solid var(--border-secondary);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.project-header {
|
|
876
|
+
display: flex;
|
|
877
|
+
align-items: center;
|
|
878
|
+
padding: 16px 20px;
|
|
879
|
+
background: var(--bg-secondary);
|
|
880
|
+
border-bottom: 1px solid var(--border-primary);
|
|
881
|
+
cursor: pointer;
|
|
882
|
+
transition: background-color 0.2s ease;
|
|
883
|
+
position: sticky;
|
|
884
|
+
top: 0;
|
|
885
|
+
z-index: 10;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
.project-header:hover {
|
|
889
|
+
background: var(--bg-tertiary);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
.project-avatar {
|
|
893
|
+
width: 44px;
|
|
894
|
+
height: 44px;
|
|
895
|
+
border-radius: 8px;
|
|
896
|
+
background: linear-gradient(135deg, var(--terminal-orange) 0%, #e67e22 100%);
|
|
897
|
+
display: flex;
|
|
898
|
+
align-items: center;
|
|
899
|
+
justify-content: center;
|
|
900
|
+
font-size: 1.2rem;
|
|
901
|
+
margin-right: 12px;
|
|
902
|
+
flex-shrink: 0;
|
|
903
|
+
color: var(--bg-primary);
|
|
904
|
+
font-weight: bold;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
.project-info {
|
|
908
|
+
flex: 1;
|
|
909
|
+
min-width: 0;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
.project-name {
|
|
913
|
+
font-weight: 600;
|
|
914
|
+
color: var(--text-primary);
|
|
915
|
+
font-size: 1rem;
|
|
916
|
+
margin-bottom: 2px;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.project-count {
|
|
920
|
+
color: var(--text-secondary);
|
|
921
|
+
font-size: 0.85rem;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.project-toggle {
|
|
925
|
+
margin-left: 8px;
|
|
926
|
+
color: var(--text-secondary);
|
|
927
|
+
transition: transform 0.2s ease;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
.toggle-icon {
|
|
931
|
+
transition: transform 0.2s ease;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.toggle-icon.expanded {
|
|
935
|
+
transform: rotate(180deg);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/* Smaller avatar for conversations within groups */
|
|
939
|
+
.conversation-avatar-small {
|
|
940
|
+
width: 36px;
|
|
941
|
+
height: 36px;
|
|
942
|
+
font-size: 0.85rem;
|
|
943
|
+
background: var(--bg-tertiary);
|
|
944
|
+
color: var(--text-primary);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/* Indent conversations within project groups */
|
|
948
|
+
.project-group .conversation-item {
|
|
949
|
+
padding-left: 36px;
|
|
950
|
+
}
|
|
951
|
+
|
|
271
952
|
.conversation-state {
|
|
272
953
|
font-size: 0.7rem;
|
|
273
954
|
padding: 3px 8px;
|
|
@@ -805,13 +1486,16 @@
|
|
|
805
1486
|
align-items: center;
|
|
806
1487
|
justify-content: center;
|
|
807
1488
|
flex: 0 0 auto;
|
|
1489
|
+
gap: 8px;
|
|
808
1490
|
}
|
|
809
1491
|
|
|
1492
|
+
|
|
810
1493
|
.header-right {
|
|
811
1494
|
display: flex;
|
|
812
1495
|
align-items: center;
|
|
813
1496
|
justify-content: flex-end;
|
|
814
1497
|
flex: 1;
|
|
1498
|
+
gap: 12px;
|
|
815
1499
|
}
|
|
816
1500
|
|
|
817
1501
|
.chat-view-back {
|
|
@@ -1195,12 +1879,30 @@
|
|
|
1195
1879
|
|
|
1196
1880
|
<!-- Search -->
|
|
1197
1881
|
<div class="chat-search">
|
|
1198
|
-
<
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1882
|
+
<div style="display: flex; align-items: center;">
|
|
1883
|
+
<input
|
|
1884
|
+
type="text"
|
|
1885
|
+
class="search-input"
|
|
1886
|
+
placeholder="Search conversations..."
|
|
1887
|
+
id="searchInput"
|
|
1888
|
+
style="flex: 1;"
|
|
1889
|
+
/>
|
|
1890
|
+
<button class="advanced-search-toggle" id="advancedSearchToggle">
|
|
1891
|
+
<span>🔍</span>
|
|
1892
|
+
<span>Advanced</span>
|
|
1893
|
+
</button>
|
|
1894
|
+
</div>
|
|
1895
|
+
|
|
1896
|
+
<!-- Search Results Info -->
|
|
1897
|
+
<div class="search-results-info" id="searchResultsInfo">
|
|
1898
|
+
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
1899
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
1900
|
+
<span>Found <span class="search-results-count" id="searchResultsCount">0</span> conversations</span>
|
|
1901
|
+
<button class="clear-filters-btn" id="clearFiltersBtn">Clear filters</button>
|
|
1902
|
+
</div>
|
|
1903
|
+
<div id="appliedFilters" style="display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px;"></div>
|
|
1904
|
+
</div>
|
|
1905
|
+
</div>
|
|
1204
1906
|
</div>
|
|
1205
1907
|
|
|
1206
1908
|
<!-- Conversations List -->
|
|
@@ -1209,6 +1911,127 @@
|
|
|
1209
1911
|
</div>
|
|
1210
1912
|
</div>
|
|
1211
1913
|
|
|
1914
|
+
<!-- Advanced Search Panel Overlay -->
|
|
1915
|
+
<div class="search-panel-overlay" id="searchPanelOverlay">
|
|
1916
|
+
<div class="search-panel">
|
|
1917
|
+
<!-- Panel Header -->
|
|
1918
|
+
<div class="search-panel-header">
|
|
1919
|
+
<h3 class="search-panel-title">
|
|
1920
|
+
<span class="search-filter-icon">🔍</span>
|
|
1921
|
+
Advanced Search
|
|
1922
|
+
</h3>
|
|
1923
|
+
<button class="search-panel-close" id="searchPanelClose">✕</button>
|
|
1924
|
+
</div>
|
|
1925
|
+
|
|
1926
|
+
<!-- Panel Body -->
|
|
1927
|
+
<div class="search-panel-body">
|
|
1928
|
+
<!-- Quick Search -->
|
|
1929
|
+
<div class="search-filter-group">
|
|
1930
|
+
<label class="search-filter-label">
|
|
1931
|
+
<span class="search-filter-icon">💬</span>
|
|
1932
|
+
Quick Search
|
|
1933
|
+
</label>
|
|
1934
|
+
<input
|
|
1935
|
+
type="text"
|
|
1936
|
+
class="search-filter-input"
|
|
1937
|
+
placeholder="Search by conversation ID or filename..."
|
|
1938
|
+
id="filterQuery"
|
|
1939
|
+
/>
|
|
1940
|
+
</div>
|
|
1941
|
+
|
|
1942
|
+
<!-- Working Directory Filter -->
|
|
1943
|
+
<div class="search-filter-group">
|
|
1944
|
+
<label class="search-filter-label">
|
|
1945
|
+
<span class="search-filter-icon">📁</span>
|
|
1946
|
+
Working Directory
|
|
1947
|
+
</label>
|
|
1948
|
+
<div class="folder-browser-wrapper">
|
|
1949
|
+
<input
|
|
1950
|
+
type="text"
|
|
1951
|
+
class="search-filter-input"
|
|
1952
|
+
placeholder="e.g., /Users/name/Projects/MyProject"
|
|
1953
|
+
id="filterWorkingDirectory"
|
|
1954
|
+
style="padding-right: 100px;"
|
|
1955
|
+
/>
|
|
1956
|
+
<button class="folder-browser-toggle" id="folderBrowserToggle">
|
|
1957
|
+
📂 Browse
|
|
1958
|
+
</button>
|
|
1959
|
+
|
|
1960
|
+
<!-- Folder Browser Dropdown -->
|
|
1961
|
+
<div class="folder-browser-dropdown" id="folderBrowserDropdown">
|
|
1962
|
+
<div class="folder-browser-header">
|
|
1963
|
+
<input
|
|
1964
|
+
type="text"
|
|
1965
|
+
class="folder-browser-search"
|
|
1966
|
+
placeholder="Filter directories..."
|
|
1967
|
+
id="folderBrowserSearch"
|
|
1968
|
+
/>
|
|
1969
|
+
</div>
|
|
1970
|
+
<div class="folder-browser-list" id="folderBrowserList">
|
|
1971
|
+
<div class="folder-browser-empty">
|
|
1972
|
+
Loading directories...
|
|
1973
|
+
</div>
|
|
1974
|
+
</div>
|
|
1975
|
+
</div>
|
|
1976
|
+
</div>
|
|
1977
|
+
</div>
|
|
1978
|
+
|
|
1979
|
+
<!-- Date Range Filter -->
|
|
1980
|
+
<div class="search-filter-group">
|
|
1981
|
+
<label class="search-filter-label">
|
|
1982
|
+
<span class="search-filter-icon">📅</span>
|
|
1983
|
+
Date Range
|
|
1984
|
+
</label>
|
|
1985
|
+
<div class="search-filter-date-range">
|
|
1986
|
+
<div class="search-filter-date-wrapper">
|
|
1987
|
+
<span class="search-filter-date-label">From</span>
|
|
1988
|
+
<input
|
|
1989
|
+
type="date"
|
|
1990
|
+
class="search-filter-input"
|
|
1991
|
+
id="filterDateFrom"
|
|
1992
|
+
/>
|
|
1993
|
+
</div>
|
|
1994
|
+
<div class="search-filter-date-wrapper">
|
|
1995
|
+
<span class="search-filter-date-label">To</span>
|
|
1996
|
+
<input
|
|
1997
|
+
type="date"
|
|
1998
|
+
class="search-filter-input"
|
|
1999
|
+
id="filterDateTo"
|
|
2000
|
+
/>
|
|
2001
|
+
</div>
|
|
2002
|
+
</div>
|
|
2003
|
+
</div>
|
|
2004
|
+
|
|
2005
|
+
<!-- Content Search -->
|
|
2006
|
+
<div class="search-filter-group">
|
|
2007
|
+
<label class="search-filter-label">
|
|
2008
|
+
<span class="search-filter-icon">🔎</span>
|
|
2009
|
+
Search in Messages
|
|
2010
|
+
</label>
|
|
2011
|
+
<input
|
|
2012
|
+
type="text"
|
|
2013
|
+
class="search-filter-input"
|
|
2014
|
+
placeholder="Search within conversation content..."
|
|
2015
|
+
id="filterContentSearch"
|
|
2016
|
+
/>
|
|
2017
|
+
<small style="color: var(--text-secondary); font-size: 0.75rem; margin-top: 4px; display: block;">
|
|
2018
|
+
⚠️ This may take longer for large conversations
|
|
2019
|
+
</small>
|
|
2020
|
+
</div>
|
|
2021
|
+
</div>
|
|
2022
|
+
|
|
2023
|
+
<!-- Panel Actions -->
|
|
2024
|
+
<div class="search-panel-actions">
|
|
2025
|
+
<button class="search-btn search-btn-secondary" id="searchPanelReset">
|
|
2026
|
+
Reset
|
|
2027
|
+
</button>
|
|
2028
|
+
<button class="search-btn search-btn-primary" id="searchPanelApply">
|
|
2029
|
+
Search
|
|
2030
|
+
</button>
|
|
2031
|
+
</div>
|
|
2032
|
+
</div>
|
|
2033
|
+
</div>
|
|
2034
|
+
|
|
1212
2035
|
<!-- Chat View -->
|
|
1213
2036
|
<div class="chat-view" id="chatView">
|
|
1214
2037
|
<div class="chat-view-header">
|
|
@@ -1217,14 +2040,34 @@
|
|
|
1217
2040
|
←
|
|
1218
2041
|
</button>
|
|
1219
2042
|
<div class="chat-view-info">
|
|
1220
|
-
<h2 class="chat-view-title" id="chatViewTitle">Select a
|
|
2043
|
+
<h2 class="chat-view-title" id="chatViewTitle">Select a session</h2>
|
|
1221
2044
|
<p class="chat-view-subtitle" id="chatViewSubtitle"></p>
|
|
1222
2045
|
</div>
|
|
1223
2046
|
</div>
|
|
1224
2047
|
<div class="header-center">
|
|
1225
|
-
<
|
|
1226
|
-
|
|
1227
|
-
|
|
2048
|
+
<div class="action-buttons-group" style="display: none;" id="actionButtonsGroup">
|
|
2049
|
+
<button class="action-btn resume-btn" id="resumeConversation" onclick="resumeConversationWithClaude()">
|
|
2050
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2051
|
+
<polygon points="5 3 19 12 5 21 5 3"></polygon>
|
|
2052
|
+
</svg>
|
|
2053
|
+
<span>Resume</span>
|
|
2054
|
+
</button>
|
|
2055
|
+
<button class="action-btn share-btn" id="shareConversation" onclick="shareConversation()">
|
|
2056
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2057
|
+
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
|
|
2058
|
+
<polyline points="16 6 12 2 8 6"></polyline>
|
|
2059
|
+
<line x1="12" y1="2" x2="12" y2="15"></line>
|
|
2060
|
+
</svg>
|
|
2061
|
+
<span>Share</span>
|
|
2062
|
+
</button>
|
|
2063
|
+
<button class="action-btn search-btn" id="chatSearchToggle">
|
|
2064
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2065
|
+
<circle cx="11" cy="11" r="8"></circle>
|
|
2066
|
+
<path d="m21 21-4.35-4.35"></path>
|
|
2067
|
+
</svg>
|
|
2068
|
+
<span>Search</span>
|
|
2069
|
+
</button>
|
|
2070
|
+
</div>
|
|
1228
2071
|
</div>
|
|
1229
2072
|
<div class="header-right">
|
|
1230
2073
|
<div class="tools-toggle" id="toolsToggle">
|
|
@@ -1236,6 +2079,31 @@
|
|
|
1236
2079
|
</div>
|
|
1237
2080
|
</div>
|
|
1238
2081
|
</div>
|
|
2082
|
+
|
|
2083
|
+
<!-- In-Conversation Search Bar -->
|
|
2084
|
+
<div class="chat-search-bar" id="chatSearchBar">
|
|
2085
|
+
<div class="chat-search-input-wrapper">
|
|
2086
|
+
<input
|
|
2087
|
+
type="text"
|
|
2088
|
+
class="chat-search-input"
|
|
2089
|
+
placeholder="Search in this conversation..."
|
|
2090
|
+
id="chatSearchInput"
|
|
2091
|
+
/>
|
|
2092
|
+
</div>
|
|
2093
|
+
<div class="chat-search-controls">
|
|
2094
|
+
<span class="chat-search-counter" id="chatSearchCounter">0/0</span>
|
|
2095
|
+
<button class="chat-search-nav-btn" id="chatSearchPrev" title="Previous match (↑)">
|
|
2096
|
+
↑
|
|
2097
|
+
</button>
|
|
2098
|
+
<button class="chat-search-nav-btn" id="chatSearchNext" title="Next match (↓)">
|
|
2099
|
+
↓
|
|
2100
|
+
</button>
|
|
2101
|
+
<button class="chat-search-close-btn" id="chatSearchClose">
|
|
2102
|
+
✕
|
|
2103
|
+
</button>
|
|
2104
|
+
</div>
|
|
2105
|
+
</div>
|
|
2106
|
+
|
|
1239
2107
|
<div class="chat-messages" id="chatMessages">
|
|
1240
2108
|
<div class="no-conversations">
|
|
1241
2109
|
<div class="no-conversations-icon">💬</div>
|
|
@@ -1275,6 +2143,106 @@
|
|
|
1275
2143
|
</div>
|
|
1276
2144
|
</div>
|
|
1277
2145
|
|
|
2146
|
+
<!-- Confirm Share Modal (Security Warning) -->
|
|
2147
|
+
<div class="modal-overlay" id="confirmShareModal">
|
|
2148
|
+
<div class="modal">
|
|
2149
|
+
<div class="modal-header" style="background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);">
|
|
2150
|
+
<span class="modal-icon">📤</span>
|
|
2151
|
+
<h3 class="modal-title">Share Your Session</h3>
|
|
2152
|
+
</div>
|
|
2153
|
+
<div style="padding: 20px;">
|
|
2154
|
+
<p class="modal-description" style="margin-bottom: 16px; line-height: 1.5;">
|
|
2155
|
+
Your conversation will be uploaded to <strong>x0.at</strong>, a temporary file hosting service.
|
|
2156
|
+
The generated link can be shared with others to clone your session.
|
|
2157
|
+
</p>
|
|
2158
|
+
|
|
2159
|
+
<div style="background: rgba(59, 130, 246, 0.08); border-left: 3px solid #3b82f6; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
|
|
2160
|
+
<h4 style="color: #3b82f6; margin: 0 0 8px 0; font-size: 14px;">📋 What you're sharing</h4>
|
|
2161
|
+
<p style="margin: 0; font-size: 13px; color: var(--text-secondary); line-height: 1.4;">
|
|
2162
|
+
Last <strong>100 messages</strong> from this conversation (or all messages if less than 100)
|
|
2163
|
+
</p>
|
|
2164
|
+
</div>
|
|
2165
|
+
|
|
2166
|
+
<div style="background: rgba(234, 179, 8, 0.08); border-left: 3px solid #eab308; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
|
|
2167
|
+
<h4 style="color: #eab308; margin: 0 0 8px 0; font-size: 14px;">ℹ️ About x0.at</h4>
|
|
2168
|
+
<ul style="margin: 0; padding-left: 20px; font-size: 13px; color: var(--text-secondary); line-height: 1.5;">
|
|
2169
|
+
<li style="margin-bottom: 4px;">Simple temporary file hosting (open source)</li>
|
|
2170
|
+
<li style="margin-bottom: 4px;">Files available for 3-100 days depending on size</li>
|
|
2171
|
+
<li style="margin-bottom: 4px;">Anyone with the link can download your session</li>
|
|
2172
|
+
<li style="margin-bottom: 4px;">No encryption - avoid sharing sensitive data</li>
|
|
2173
|
+
</ul>
|
|
2174
|
+
</div>
|
|
2175
|
+
|
|
2176
|
+
<div style="background: rgba(100, 116, 139, 0.08); border-left: 3px solid #64748b; padding: 12px; border-radius: 6px;">
|
|
2177
|
+
<p style="margin: 0; font-size: 12px; color: var(--text-secondary); line-height: 1.4;">
|
|
2178
|
+
💡 <strong>Tip:</strong> Only share links with people you trust. The session includes your conversation history and may contain project-specific information.
|
|
2179
|
+
</p>
|
|
2180
|
+
</div>
|
|
2181
|
+
</div>
|
|
2182
|
+
|
|
2183
|
+
<div class="modal-actions">
|
|
2184
|
+
<button class="modal-btn secondary" onclick="closeConfirmShareModal()">Cancel</button>
|
|
2185
|
+
<button class="modal-btn primary" onclick="proceedWithShare()" style="background: #3b82f6;">
|
|
2186
|
+
Continue & Upload
|
|
2187
|
+
</button>
|
|
2188
|
+
</div>
|
|
2189
|
+
</div>
|
|
2190
|
+
</div>
|
|
2191
|
+
|
|
2192
|
+
<!-- Share Modal -->
|
|
2193
|
+
<div class="modal-overlay" id="shareModal">
|
|
2194
|
+
<div class="modal">
|
|
2195
|
+
<div class="modal-header">
|
|
2196
|
+
<span class="modal-icon">📤</span>
|
|
2197
|
+
<h3 class="modal-title">Share Conversation</h3>
|
|
2198
|
+
</div>
|
|
2199
|
+
<div id="shareModalLoading" style="text-align: center; padding: 40px; display: none;">
|
|
2200
|
+
<div class="loading-spinner"></div>
|
|
2201
|
+
<p style="margin-top: 16px; color: var(--text-secondary);">Uploading session...</p>
|
|
2202
|
+
</div>
|
|
2203
|
+
<div id="shareModalContent" style="display: none;">
|
|
2204
|
+
<p class="modal-description">
|
|
2205
|
+
Your conversation has been uploaded to x0.at. Share the command or QR code below with others.
|
|
2206
|
+
</p>
|
|
2207
|
+
<div id="shareMessageInfo" style="display: none; padding: 12px; background: var(--bg-tertiary); border-radius: 6px; margin-bottom: 16px;">
|
|
2208
|
+
<p style="font-size: 13px; color: var(--text-secondary); margin: 0;">
|
|
2209
|
+
<span id="shareMessageCount"></span>
|
|
2210
|
+
</p>
|
|
2211
|
+
</div>
|
|
2212
|
+
<div style="text-align: center; margin: 24px 0;">
|
|
2213
|
+
<div style="margin-bottom: 16px; display: flex; flex-direction: column; align-items: center;">
|
|
2214
|
+
<h4 style="margin-bottom: 12px; color: var(--text-primary);">📱 Scan QR Code</h4>
|
|
2215
|
+
<img id="shareQRCode" src="" alt="QR Code" style="max-width: 300px; border-radius: 8px; border: 2px solid var(--border-color); display: block; margin: 0 auto;">
|
|
2216
|
+
<p style="font-size: 12px; color: var(--text-secondary); margin-top: 12px; margin-bottom: 0;">
|
|
2217
|
+
Scan this QR code to get the share command
|
|
2218
|
+
</p>
|
|
2219
|
+
</div>
|
|
2220
|
+
</div>
|
|
2221
|
+
<div>
|
|
2222
|
+
<h4 style="margin-bottom: 8px; color: var(--text-primary);">📋 Share Command</h4>
|
|
2223
|
+
<div class="modal-command" id="shareModalCommand">
|
|
2224
|
+
<!-- Command will be inserted here -->
|
|
2225
|
+
</div>
|
|
2226
|
+
</div>
|
|
2227
|
+
<div style="margin-top: 16px; padding: 12px; background: var(--bg-tertiary); border-radius: 6px;">
|
|
2228
|
+
<p style="font-size: 12px; color: var(--text-secondary); margin: 0;">
|
|
2229
|
+
🔗 Direct URL: <a id="shareDirectUrl" href="#" target="_blank" style="color: var(--text-accent); word-break: break-all;"></a>
|
|
2230
|
+
</p>
|
|
2231
|
+
<p style="font-size: 12px; color: var(--text-warning); margin: 8px 0 0 0;">
|
|
2232
|
+
⚠️ Files kept for 3-100 days (based on size)
|
|
2233
|
+
</p>
|
|
2234
|
+
<p style="font-size: 12px; color: var(--text-secondary); margin: 4px 0 0 0;">
|
|
2235
|
+
🔓 Files are not encrypted by default
|
|
2236
|
+
</p>
|
|
2237
|
+
</div>
|
|
2238
|
+
</div>
|
|
2239
|
+
<div class="modal-actions">
|
|
2240
|
+
<button class="modal-btn secondary" onclick="closeShareModal()">Close</button>
|
|
2241
|
+
<button class="modal-btn primary" id="copyShareCommandBtn" onclick="copyShareCommand()" style="display: none;">Copy Command</button>
|
|
2242
|
+
</div>
|
|
2243
|
+
</div>
|
|
2244
|
+
</div>
|
|
2245
|
+
|
|
1278
2246
|
<!-- Import WebSocket and Data Services -->
|
|
1279
2247
|
<script src="services/WebSocketService.js"></script>
|
|
1280
2248
|
<script src="services/DataService.js"></script>
|
|
@@ -1340,6 +2308,109 @@
|
|
|
1340
2308
|
this.filterConversations(e.target.value);
|
|
1341
2309
|
});
|
|
1342
2310
|
|
|
2311
|
+
// Advanced search toggle
|
|
2312
|
+
document.getElementById('advancedSearchToggle').addEventListener('click', () => {
|
|
2313
|
+
this.openAdvancedSearch();
|
|
2314
|
+
});
|
|
2315
|
+
|
|
2316
|
+
// Advanced search panel close
|
|
2317
|
+
document.getElementById('searchPanelClose').addEventListener('click', () => {
|
|
2318
|
+
this.closeAdvancedSearch();
|
|
2319
|
+
});
|
|
2320
|
+
|
|
2321
|
+
// Close panel on overlay click
|
|
2322
|
+
document.getElementById('searchPanelOverlay').addEventListener('click', (e) => {
|
|
2323
|
+
if (e.target.id === 'searchPanelOverlay') {
|
|
2324
|
+
this.closeAdvancedSearch();
|
|
2325
|
+
}
|
|
2326
|
+
});
|
|
2327
|
+
|
|
2328
|
+
// Advanced search apply
|
|
2329
|
+
document.getElementById('searchPanelApply').addEventListener('click', () => {
|
|
2330
|
+
this.applyAdvancedSearch();
|
|
2331
|
+
});
|
|
2332
|
+
|
|
2333
|
+
// Advanced search reset
|
|
2334
|
+
document.getElementById('searchPanelReset').addEventListener('click', () => {
|
|
2335
|
+
this.resetAdvancedSearch();
|
|
2336
|
+
});
|
|
2337
|
+
|
|
2338
|
+
// Clear filters button
|
|
2339
|
+
document.getElementById('clearFiltersBtn').addEventListener('click', () => {
|
|
2340
|
+
this.clearAllFilters();
|
|
2341
|
+
});
|
|
2342
|
+
|
|
2343
|
+
// Folder browser toggle
|
|
2344
|
+
document.getElementById('folderBrowserToggle').addEventListener('click', (e) => {
|
|
2345
|
+
e.preventDefault();
|
|
2346
|
+
e.stopPropagation();
|
|
2347
|
+
this.toggleFolderBrowser();
|
|
2348
|
+
});
|
|
2349
|
+
|
|
2350
|
+
// Folder browser search
|
|
2351
|
+
document.getElementById('folderBrowserSearch').addEventListener('input', (e) => {
|
|
2352
|
+
this.filterFolderBrowserList(e.target.value);
|
|
2353
|
+
});
|
|
2354
|
+
|
|
2355
|
+
// Close folder browser when clicking outside
|
|
2356
|
+
document.addEventListener('click', (e) => {
|
|
2357
|
+
const dropdown = document.getElementById('folderBrowserDropdown');
|
|
2358
|
+
const toggle = document.getElementById('folderBrowserToggle');
|
|
2359
|
+
const wrapper = document.querySelector('.folder-browser-wrapper');
|
|
2360
|
+
|
|
2361
|
+
if (dropdown.classList.contains('active') &&
|
|
2362
|
+
!wrapper.contains(e.target)) {
|
|
2363
|
+
dropdown.classList.remove('active');
|
|
2364
|
+
}
|
|
2365
|
+
});
|
|
2366
|
+
|
|
2367
|
+
// In-conversation search toggle
|
|
2368
|
+
document.getElementById('chatSearchToggle').addEventListener('click', () => {
|
|
2369
|
+
this.toggleChatSearch();
|
|
2370
|
+
});
|
|
2371
|
+
|
|
2372
|
+
// In-conversation search input (only search with 3+ characters)
|
|
2373
|
+
document.getElementById('chatSearchInput').addEventListener('input', (e) => {
|
|
2374
|
+
const query = e.target.value.trim();
|
|
2375
|
+
if (query.length >= 3) {
|
|
2376
|
+
this.performChatSearch(query);
|
|
2377
|
+
} else if (query.length === 0) {
|
|
2378
|
+
// Clear search when input is empty
|
|
2379
|
+
this.clearChatSearch();
|
|
2380
|
+
} else {
|
|
2381
|
+
// Show message that minimum 3 characters are required
|
|
2382
|
+
this.updateSearchCounter(0, 0, true);
|
|
2383
|
+
}
|
|
2384
|
+
});
|
|
2385
|
+
|
|
2386
|
+
// In-conversation search navigation
|
|
2387
|
+
document.getElementById('chatSearchPrev').addEventListener('click', () => {
|
|
2388
|
+
this.navigateSearchResults('prev');
|
|
2389
|
+
});
|
|
2390
|
+
|
|
2391
|
+
document.getElementById('chatSearchNext').addEventListener('click', () => {
|
|
2392
|
+
this.navigateSearchResults('next');
|
|
2393
|
+
});
|
|
2394
|
+
|
|
2395
|
+
// In-conversation search close
|
|
2396
|
+
document.getElementById('chatSearchClose').addEventListener('click', () => {
|
|
2397
|
+
this.closeChatSearch();
|
|
2398
|
+
});
|
|
2399
|
+
|
|
2400
|
+
// Keyboard shortcuts for search
|
|
2401
|
+
document.getElementById('chatSearchInput').addEventListener('keydown', (e) => {
|
|
2402
|
+
if (e.key === 'Enter') {
|
|
2403
|
+
e.preventDefault();
|
|
2404
|
+
if (e.shiftKey) {
|
|
2405
|
+
this.navigateSearchResults('prev');
|
|
2406
|
+
} else {
|
|
2407
|
+
this.navigateSearchResults('next');
|
|
2408
|
+
}
|
|
2409
|
+
} else if (e.key === 'Escape') {
|
|
2410
|
+
this.closeChatSearch();
|
|
2411
|
+
}
|
|
2412
|
+
});
|
|
2413
|
+
|
|
1343
2414
|
// Show Tools toggle functionality
|
|
1344
2415
|
const showToolsSwitch = document.getElementById('showToolsSwitch');
|
|
1345
2416
|
showToolsSwitch.addEventListener('change', (e) => {
|
|
@@ -1420,56 +2491,101 @@
|
|
|
1420
2491
|
|
|
1421
2492
|
renderConversations(conversations, states = {}) {
|
|
1422
2493
|
const conversationsList = document.getElementById('conversationsList');
|
|
1423
|
-
|
|
2494
|
+
|
|
1424
2495
|
if (conversations.length === 0) {
|
|
1425
2496
|
conversationsList.innerHTML = `
|
|
1426
2497
|
<div class="no-conversations">
|
|
1427
2498
|
<div class="no-conversations-icon">💬</div>
|
|
1428
|
-
<h3>No
|
|
1429
|
-
<p>Start a
|
|
2499
|
+
<h3>No sessions found</h3>
|
|
2500
|
+
<p>Start a session with Claude Code to see it here</p>
|
|
1430
2501
|
</div>
|
|
1431
2502
|
`;
|
|
1432
2503
|
return;
|
|
1433
2504
|
}
|
|
1434
2505
|
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
const stateClass = this.getStateClass(state);
|
|
1438
|
-
const stateLabel = this.getStateLabel(state);
|
|
1439
|
-
|
|
1440
|
-
// Debug logging for first few conversations
|
|
1441
|
-
console.log(`🔍 Conversation ${conv.id.slice(-8)}: State="${state}" -> Label="${stateLabel}" Class="${stateClass}"`);
|
|
1442
|
-
|
|
1443
|
-
const lastActivity = this.formatRelativeTime(new Date(conv.lastModified));
|
|
1444
|
-
const messageCount = conv.messageCount || 0;
|
|
2506
|
+
// Group conversations by project
|
|
2507
|
+
const groupedByProject = conversations.reduce((groups, conv) => {
|
|
1445
2508
|
const projectName = conv.project || 'Unknown Project';
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
2509
|
+
if (!groups[projectName]) {
|
|
2510
|
+
groups[projectName] = [];
|
|
2511
|
+
}
|
|
2512
|
+
groups[projectName].push(conv);
|
|
2513
|
+
return groups;
|
|
2514
|
+
}, {});
|
|
2515
|
+
|
|
2516
|
+
// Sort projects alphabetically
|
|
2517
|
+
const sortedProjects = Object.keys(groupedByProject).sort();
|
|
2518
|
+
|
|
2519
|
+
// Initialize expanded state if not exists
|
|
2520
|
+
if (!this.expandedProjects) {
|
|
2521
|
+
this.expandedProjects = new Set(); // All collapsed by default
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
// Render grouped conversations
|
|
2525
|
+
conversationsList.innerHTML = sortedProjects.map(projectName => {
|
|
2526
|
+
const projectConversations = groupedByProject[projectName];
|
|
2527
|
+
const isExpanded = this.expandedProjects.has(projectName);
|
|
1449
2528
|
const firstLetter = projectName.charAt(0).toUpperCase();
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
2529
|
+
const conversationCount = projectConversations.length;
|
|
2530
|
+
|
|
2531
|
+
// Render conversations for this project
|
|
2532
|
+
const conversationsHTML = projectConversations.map(conv => {
|
|
2533
|
+
const state = states[conv.id] || 'inactive';
|
|
2534
|
+
const stateClass = this.getStateClass(state);
|
|
2535
|
+
const stateLabel = this.getStateLabel(state);
|
|
2536
|
+
|
|
2537
|
+
const lastActivity = this.formatRelativeTime(new Date(conv.lastModified));
|
|
2538
|
+
const messageCount = conv.messageCount || 0;
|
|
2539
|
+
const conversationId = conv.id.slice(-8);
|
|
2540
|
+
|
|
2541
|
+
return `
|
|
2542
|
+
<div class="conversation-item" data-conversation-id="${conv.id}" style="display: ${isExpanded ? 'flex' : 'none'}">
|
|
2543
|
+
<div class="conversation-avatar conversation-avatar-small">
|
|
2544
|
+
${conversationId.substring(0, 2).toUpperCase()}
|
|
2545
|
+
</div>
|
|
2546
|
+
<div class="conversation-content">
|
|
2547
|
+
<div class="conversation-header">
|
|
2548
|
+
<div class="conversation-name">Session ${conversationId}</div>
|
|
2549
|
+
<div class="conversation-time">${lastActivity}</div>
|
|
2550
|
+
</div>
|
|
2551
|
+
<div class="conversation-meta">
|
|
2552
|
+
<span class="conversation-state ${stateClass}">${stateLabel}</span>
|
|
2553
|
+
${messageCount > 0 ? `<span class="message-count">${messageCount}</span>` : ''}
|
|
2554
|
+
</div>
|
|
2555
|
+
</div>
|
|
1455
2556
|
</div>
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
2557
|
+
`;
|
|
2558
|
+
}).join('');
|
|
2559
|
+
|
|
2560
|
+
return `
|
|
2561
|
+
<div class="project-group" data-project="${projectName}">
|
|
2562
|
+
<div class="project-header" data-project="${projectName}">
|
|
2563
|
+
<div class="project-avatar">
|
|
2564
|
+
${firstLetter}
|
|
1460
2565
|
</div>
|
|
1461
|
-
<div class="
|
|
1462
|
-
|
|
2566
|
+
<div class="project-info">
|
|
2567
|
+
<div class="project-name">${projectName}</div>
|
|
2568
|
+
<div class="project-count">${conversationCount} session${conversationCount !== 1 ? 's' : ''}</div>
|
|
1463
2569
|
</div>
|
|
1464
|
-
<div class="
|
|
1465
|
-
<
|
|
1466
|
-
|
|
2570
|
+
<div class="project-toggle">
|
|
2571
|
+
<svg class="toggle-icon ${isExpanded ? 'expanded' : ''}" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
|
|
2572
|
+
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
|
|
2573
|
+
</svg>
|
|
1467
2574
|
</div>
|
|
1468
2575
|
</div>
|
|
2576
|
+
${conversationsHTML}
|
|
1469
2577
|
</div>
|
|
1470
2578
|
`;
|
|
1471
2579
|
}).join('');
|
|
1472
2580
|
|
|
2581
|
+
// Bind project toggle events
|
|
2582
|
+
conversationsList.querySelectorAll('.project-header').forEach(header => {
|
|
2583
|
+
header.addEventListener('click', (e) => {
|
|
2584
|
+
const projectName = header.dataset.project;
|
|
2585
|
+
this.toggleProject(projectName);
|
|
2586
|
+
});
|
|
2587
|
+
});
|
|
2588
|
+
|
|
1473
2589
|
// Bind conversation click events
|
|
1474
2590
|
conversationsList.querySelectorAll('.conversation-item').forEach(item => {
|
|
1475
2591
|
item.addEventListener('click', () => {
|
|
@@ -1479,6 +2595,17 @@
|
|
|
1479
2595
|
});
|
|
1480
2596
|
}
|
|
1481
2597
|
|
|
2598
|
+
toggleProject(projectName) {
|
|
2599
|
+
if (this.expandedProjects.has(projectName)) {
|
|
2600
|
+
this.expandedProjects.delete(projectName);
|
|
2601
|
+
} else {
|
|
2602
|
+
this.expandedProjects.add(projectName);
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
// Re-render conversations with current states
|
|
2606
|
+
this.renderConversations(this.conversations, this.conversationStates);
|
|
2607
|
+
}
|
|
2608
|
+
|
|
1482
2609
|
selectConversation(conversationId) {
|
|
1483
2610
|
this.selectedConversationId = conversationId;
|
|
1484
2611
|
|
|
@@ -1503,7 +2630,7 @@
|
|
|
1503
2630
|
const projectName = conversation.project || 'Unknown Project';
|
|
1504
2631
|
const convId = conversation.id.slice(-8);
|
|
1505
2632
|
chatViewTitle.textContent = projectName;
|
|
1506
|
-
chatViewSubtitle.textContent = `
|
|
2633
|
+
chatViewSubtitle.textContent = `Session ${convId}`;
|
|
1507
2634
|
|
|
1508
2635
|
// Show chat view with animation
|
|
1509
2636
|
chatView.classList.add('active');
|
|
@@ -1515,10 +2642,14 @@
|
|
|
1515
2642
|
chatView.classList.remove('show-tools');
|
|
1516
2643
|
}
|
|
1517
2644
|
|
|
1518
|
-
// Show
|
|
2645
|
+
// Show action buttons group
|
|
2646
|
+
const actionButtonsGroup = document.getElementById('actionButtonsGroup');
|
|
2647
|
+
actionButtonsGroup.style.display = 'inline-flex';
|
|
2648
|
+
|
|
1519
2649
|
const resumeBtn = document.getElementById('resumeConversation');
|
|
1520
|
-
|
|
2650
|
+
const shareBtn = document.getElementById('shareConversation');
|
|
1521
2651
|
resumeBtn.setAttribute('data-conversation-id', conversationId);
|
|
2652
|
+
shareBtn.setAttribute('data-conversation-id', conversationId);
|
|
1522
2653
|
|
|
1523
2654
|
// Load messages (placeholder for now)
|
|
1524
2655
|
this.loadChatMessages(conversationId);
|
|
@@ -1533,9 +2664,9 @@
|
|
|
1533
2664
|
// Clean up scroll tracking when leaving conversation
|
|
1534
2665
|
this.removeScrollTracking();
|
|
1535
2666
|
|
|
1536
|
-
// Hide
|
|
1537
|
-
const
|
|
1538
|
-
|
|
2667
|
+
// Hide action buttons group
|
|
2668
|
+
const actionButtonsGroup = document.getElementById('actionButtonsGroup');
|
|
2669
|
+
actionButtonsGroup.style.display = 'none';
|
|
1539
2670
|
|
|
1540
2671
|
// Remove active state from conversations
|
|
1541
2672
|
document.querySelectorAll('.conversation-item').forEach(item => {
|
|
@@ -1717,22 +2848,26 @@
|
|
|
1717
2848
|
const scrollHeight = chatMessages.scrollHeight;
|
|
1718
2849
|
|
|
1719
2850
|
// Prepend new messages
|
|
1720
|
-
|
|
2851
|
+
// Calculate message indices (considering we're prepending older messages)
|
|
2852
|
+
const startIndex = this.messagesPagination.currentPage * this.messagesPagination.limit;
|
|
2853
|
+
const newMessagesHTML = messages.map((msg, idx) =>
|
|
2854
|
+
this.renderMessage(msg, startIndex + idx)
|
|
2855
|
+
).join('');
|
|
1721
2856
|
existingMessagesDiv.innerHTML = newMessagesHTML + existingMessagesDiv.innerHTML;
|
|
1722
|
-
|
|
2857
|
+
|
|
1723
2858
|
// Restore scroll position (account for new content)
|
|
1724
2859
|
const newScrollHeight = chatMessages.scrollHeight;
|
|
1725
2860
|
const scrollDiff = newScrollHeight - scrollHeight;
|
|
1726
2861
|
chatMessages.scrollTop = scrollTop + scrollDiff;
|
|
1727
|
-
|
|
2862
|
+
|
|
1728
2863
|
} else {
|
|
1729
2864
|
// Normal render (initial load or replace all)
|
|
1730
2865
|
const messageHTML = `
|
|
1731
2866
|
<div class="messages-list">
|
|
1732
|
-
${messages.map(msg => this.renderMessage(msg)).join('')}
|
|
2867
|
+
${messages.map((msg, idx) => this.renderMessage(msg, idx)).join('')}
|
|
1733
2868
|
</div>
|
|
1734
2869
|
`;
|
|
1735
|
-
|
|
2870
|
+
|
|
1736
2871
|
chatMessages.innerHTML = messageHTML;
|
|
1737
2872
|
}
|
|
1738
2873
|
}
|
|
@@ -1800,17 +2935,17 @@
|
|
|
1800
2935
|
chatMessages.addEventListener('scroll', this.messagesScrollListener);
|
|
1801
2936
|
}
|
|
1802
2937
|
|
|
1803
|
-
renderMessage(message) {
|
|
2938
|
+
renderMessage(message, messageIndex) {
|
|
1804
2939
|
const timestamp = this.formatRelativeTime(new Date(message.timestamp));
|
|
1805
2940
|
const fullTimestamp = new Date(message.timestamp).toLocaleString();
|
|
1806
2941
|
const isUser = message.role === 'user' && !message.isCompactSummary;
|
|
1807
|
-
|
|
2942
|
+
|
|
1808
2943
|
// Detect if message contains tools (either in content or as correlated toolResults)
|
|
1809
|
-
const hasToolsInContent = Array.isArray(message.content) &&
|
|
2944
|
+
const hasToolsInContent = Array.isArray(message.content) &&
|
|
1810
2945
|
message.content.some(block => block.type === 'tool_use');
|
|
1811
2946
|
const hasCorrelatedTools = message.toolResults && message.toolResults.length > 0;
|
|
1812
2947
|
const hasTools = hasToolsInContent || hasCorrelatedTools;
|
|
1813
|
-
|
|
2948
|
+
|
|
1814
2949
|
// Debug logging for tool detection
|
|
1815
2950
|
if (hasTools) {
|
|
1816
2951
|
console.log('🔧 Rendering message with tools', {
|
|
@@ -1823,16 +2958,19 @@
|
|
|
1823
2958
|
willHaveHasToolsClass: !isUser && hasTools
|
|
1824
2959
|
});
|
|
1825
2960
|
}
|
|
1826
|
-
|
|
1827
|
-
const toolCount = hasToolsInContent ?
|
|
2961
|
+
|
|
2962
|
+
const toolCount = hasToolsInContent ?
|
|
1828
2963
|
message.content.filter(block => block.type === 'tool_use').length :
|
|
1829
2964
|
(hasCorrelatedTools ? message.toolResults.length : 0);
|
|
1830
|
-
|
|
2965
|
+
|
|
1831
2966
|
// Add has-tools class to assistant messages that contain tools
|
|
1832
2967
|
const hasToolsClass = (!isUser && hasTools) ? ' has-tools' : '';
|
|
1833
|
-
|
|
2968
|
+
|
|
2969
|
+
// Add message index for search functionality
|
|
2970
|
+
const messageIndexAttr = messageIndex !== undefined ? ` data-message-index="${messageIndex}"` : '';
|
|
2971
|
+
|
|
1834
2972
|
return `
|
|
1835
|
-
<div class="message message-${isUser ? 'user' : 'assistant'}${hasToolsClass}" data-message-id="${message.id || ''}">
|
|
2973
|
+
<div class="message message-${isUser ? 'user' : 'assistant'}${hasToolsClass}" data-message-id="${message.id || ''}"${messageIndexAttr}>
|
|
1836
2974
|
<div class="message-bubble">
|
|
1837
2975
|
<div class="message-content">
|
|
1838
2976
|
${this.formatMessageContent(message.content, message)}
|
|
@@ -2106,107 +3244,785 @@
|
|
|
2106
3244
|
return scrollHeight - scrollTop - clientHeight <= this.scrollThreshold;
|
|
2107
3245
|
}
|
|
2108
3246
|
|
|
2109
|
-
/**
|
|
2110
|
-
* Smart scroll to bottom with chat logic
|
|
2111
|
-
* Only scrolls if user is near bottom or auto-scroll is enabled
|
|
2112
|
-
*/
|
|
2113
|
-
scrollToBottom() {
|
|
2114
|
-
const chatMessages = document.getElementById('chatMessages');
|
|
2115
|
-
if (!chatMessages) return;
|
|
2116
|
-
|
|
2117
|
-
// Always scroll on initial load or if user is near bottom
|
|
2118
|
-
if (this.autoScrollEnabled || this.isNearBottom()) {
|
|
2119
|
-
console.log('📱 Auto-scrolling to bottom', {
|
|
2120
|
-
autoScrollEnabled: this.autoScrollEnabled,
|
|
2121
|
-
isNearBottom: this.isNearBottom(),
|
|
2122
|
-
userScrolling: this.isUserScrolling
|
|
2123
|
-
});
|
|
2124
|
-
|
|
2125
|
-
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
3247
|
+
/**
|
|
3248
|
+
* Smart scroll to bottom with chat logic
|
|
3249
|
+
* Only scrolls if user is near bottom or auto-scroll is enabled
|
|
3250
|
+
*/
|
|
3251
|
+
scrollToBottom() {
|
|
3252
|
+
const chatMessages = document.getElementById('chatMessages');
|
|
3253
|
+
if (!chatMessages) return;
|
|
3254
|
+
|
|
3255
|
+
// Always scroll on initial load or if user is near bottom
|
|
3256
|
+
if (this.autoScrollEnabled || this.isNearBottom()) {
|
|
3257
|
+
console.log('📱 Auto-scrolling to bottom', {
|
|
3258
|
+
autoScrollEnabled: this.autoScrollEnabled,
|
|
3259
|
+
isNearBottom: this.isNearBottom(),
|
|
3260
|
+
userScrolling: this.isUserScrolling
|
|
3261
|
+
});
|
|
3262
|
+
|
|
3263
|
+
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
3264
|
+
} else {
|
|
3265
|
+
console.log('📱 Skipping auto-scroll (user viewing older messages)', {
|
|
3266
|
+
scrollTop: chatMessages.scrollTop,
|
|
3267
|
+
scrollHeight: chatMessages.scrollHeight,
|
|
3268
|
+
isNearBottom: this.isNearBottom()
|
|
3269
|
+
});
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
|
|
3273
|
+
/**
|
|
3274
|
+
* Setup scroll tracking for intelligent auto-scroll
|
|
3275
|
+
*/
|
|
3276
|
+
setupScrollTracking() {
|
|
3277
|
+
const chatMessages = document.getElementById('chatMessages');
|
|
3278
|
+
if (!chatMessages || this.scrollListener) return;
|
|
3279
|
+
|
|
3280
|
+
this.scrollListener = () => {
|
|
3281
|
+
// Clear previous timeout
|
|
3282
|
+
if (this.userScrollTimeout) {
|
|
3283
|
+
clearTimeout(this.userScrollTimeout);
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
// Mark as user scrolling
|
|
3287
|
+
this.isUserScrolling = true;
|
|
3288
|
+
|
|
3289
|
+
// Check if user scrolled back to bottom
|
|
3290
|
+
if (this.isNearBottom()) {
|
|
3291
|
+
this.autoScrollEnabled = true;
|
|
3292
|
+
console.log('📱 User scrolled to bottom, re-enabling auto-scroll');
|
|
3293
|
+
} else {
|
|
3294
|
+
this.autoScrollEnabled = false;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
// Reset user scrolling flag after a delay
|
|
3298
|
+
this.userScrollTimeout = setTimeout(() => {
|
|
3299
|
+
this.isUserScrolling = false;
|
|
3300
|
+
}, 1000);
|
|
3301
|
+
};
|
|
3302
|
+
|
|
3303
|
+
chatMessages.addEventListener('scroll', this.scrollListener, { passive: true });
|
|
3304
|
+
console.log('📱 Scroll tracking enabled for intelligent auto-scroll');
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
/**
|
|
3308
|
+
* Remove scroll tracking
|
|
3309
|
+
*/
|
|
3310
|
+
removeScrollTracking() {
|
|
3311
|
+
const chatMessages = document.getElementById('chatMessages');
|
|
3312
|
+
if (chatMessages && this.scrollListener) {
|
|
3313
|
+
chatMessages.removeEventListener('scroll', this.scrollListener);
|
|
3314
|
+
this.scrollListener = null;
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
if (this.userScrollTimeout) {
|
|
3318
|
+
clearTimeout(this.userScrollTimeout);
|
|
3319
|
+
this.userScrollTimeout = null;
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
/**
|
|
3324
|
+
* Escape HTML characters to prevent double-encoding issues
|
|
3325
|
+
* @param {string} text - Text to escape
|
|
3326
|
+
* @returns {string} Escaped text
|
|
3327
|
+
*/
|
|
3328
|
+
escapeHtml(text) {
|
|
3329
|
+
if (typeof text !== 'string') return text;
|
|
3330
|
+
|
|
3331
|
+
const div = document.createElement('div');
|
|
3332
|
+
div.textContent = text;
|
|
3333
|
+
return div.innerHTML;
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
|
|
3337
|
+
|
|
3338
|
+
filterConversations(searchTerm) {
|
|
3339
|
+
const term = searchTerm.toLowerCase().trim();
|
|
3340
|
+
const projectGroups = document.querySelectorAll('.project-group');
|
|
3341
|
+
|
|
3342
|
+
if (!term) {
|
|
3343
|
+
// If search is empty, collapse all projects and reset
|
|
3344
|
+
this.expandedProjects.clear(); // Close all projects
|
|
3345
|
+
|
|
3346
|
+
projectGroups.forEach(group => {
|
|
3347
|
+
group.style.display = 'block';
|
|
3348
|
+
|
|
3349
|
+
// Hide all conversations
|
|
3350
|
+
const conversations = group.querySelectorAll('.conversation-item');
|
|
3351
|
+
conversations.forEach(conv => {
|
|
3352
|
+
conv.style.display = 'none';
|
|
3353
|
+
});
|
|
3354
|
+
|
|
3355
|
+
// Update toggle icon to collapsed state
|
|
3356
|
+
const toggleIcon = group.querySelector('.toggle-icon');
|
|
3357
|
+
if (toggleIcon) {
|
|
3358
|
+
toggleIcon.classList.remove('expanded');
|
|
3359
|
+
}
|
|
3360
|
+
});
|
|
3361
|
+
return;
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
// Filter projects and conversations
|
|
3365
|
+
projectGroups.forEach(group => {
|
|
3366
|
+
const projectName = group.dataset.project;
|
|
3367
|
+
const projectNameLower = projectName.toLowerCase();
|
|
3368
|
+
const conversations = group.querySelectorAll('.conversation-item');
|
|
3369
|
+
|
|
3370
|
+
let hasMatchingConversation = false;
|
|
3371
|
+
|
|
3372
|
+
// Check each conversation in this project
|
|
3373
|
+
conversations.forEach(item => {
|
|
3374
|
+
const nameElement = item.querySelector('.conversation-name');
|
|
3375
|
+
const name = nameElement ? nameElement.textContent.toLowerCase() : '';
|
|
3376
|
+
const matches = name.includes(term) || projectNameLower.includes(term);
|
|
3377
|
+
|
|
3378
|
+
if (matches) {
|
|
3379
|
+
item.style.display = 'flex';
|
|
3380
|
+
hasMatchingConversation = true;
|
|
3381
|
+
} else {
|
|
3382
|
+
item.style.display = 'none';
|
|
3383
|
+
}
|
|
3384
|
+
});
|
|
3385
|
+
|
|
3386
|
+
// Show project group if it has matching conversations or if project name matches
|
|
3387
|
+
if (hasMatchingConversation || projectNameLower.includes(term)) {
|
|
3388
|
+
group.style.display = 'block';
|
|
3389
|
+
// Auto-expand project when searching
|
|
3390
|
+
if (!this.expandedProjects.has(projectName)) {
|
|
3391
|
+
this.expandedProjects.add(projectName);
|
|
3392
|
+
// Update toggle icon
|
|
3393
|
+
const toggleIcon = group.querySelector('.toggle-icon');
|
|
3394
|
+
if (toggleIcon) {
|
|
3395
|
+
toggleIcon.classList.add('expanded');
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
} else {
|
|
3399
|
+
group.style.display = 'none';
|
|
3400
|
+
}
|
|
3401
|
+
});
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
// Advanced Search Methods
|
|
3405
|
+
openAdvancedSearch() {
|
|
3406
|
+
const overlay = document.getElementById('searchPanelOverlay');
|
|
3407
|
+
overlay.classList.add('active');
|
|
3408
|
+
document.body.style.overflow = 'hidden'; // Prevent background scrolling
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3411
|
+
closeAdvancedSearch() {
|
|
3412
|
+
const overlay = document.getElementById('searchPanelOverlay');
|
|
3413
|
+
overlay.classList.remove('active');
|
|
3414
|
+
document.body.style.overflow = ''; // Restore scrolling
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
async applyAdvancedSearch() {
|
|
3418
|
+
const conversationsList = document.getElementById('conversationsList');
|
|
3419
|
+
const searchResultsInfo = document.getElementById('searchResultsInfo');
|
|
3420
|
+
const searchResultsCount = document.getElementById('searchResultsCount');
|
|
3421
|
+
const appliedFiltersContainer = document.getElementById('appliedFilters');
|
|
3422
|
+
|
|
3423
|
+
try {
|
|
3424
|
+
// Show loading state
|
|
3425
|
+
conversationsList.innerHTML = '<div class="loading-spinner" style="margin: 40px auto;"></div>';
|
|
3426
|
+
|
|
3427
|
+
// Get filter values
|
|
3428
|
+
const filters = {
|
|
3429
|
+
query: document.getElementById('filterQuery').value.trim(),
|
|
3430
|
+
workingDirectory: document.getElementById('filterWorkingDirectory').value.trim(),
|
|
3431
|
+
dateFrom: document.getElementById('filterDateFrom').value,
|
|
3432
|
+
dateTo: document.getElementById('filterDateTo').value,
|
|
3433
|
+
contentSearch: document.getElementById('filterContentSearch').value.trim()
|
|
3434
|
+
};
|
|
3435
|
+
|
|
3436
|
+
// Call the search API
|
|
3437
|
+
const response = await fetch('/api/search', {
|
|
3438
|
+
method: 'POST',
|
|
3439
|
+
headers: {
|
|
3440
|
+
'Content-Type': 'application/json'
|
|
3441
|
+
},
|
|
3442
|
+
body: JSON.stringify(filters)
|
|
3443
|
+
});
|
|
3444
|
+
|
|
3445
|
+
if (!response.ok) {
|
|
3446
|
+
throw new Error(`Search failed: ${response.status}`);
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
const data = await response.json();
|
|
3450
|
+
|
|
3451
|
+
// Update conversations with search results
|
|
3452
|
+
this.conversations = data.results || [];
|
|
3453
|
+
|
|
3454
|
+
// Get conversation states for the results
|
|
3455
|
+
const statesResponse = await fetch('/api/conversation-state');
|
|
3456
|
+
let states = {};
|
|
3457
|
+
if (statesResponse.ok) {
|
|
3458
|
+
const statesData = await statesResponse.json();
|
|
3459
|
+
states = statesData.activeStates || {};
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
// Render search results
|
|
3463
|
+
this.renderConversations(this.conversations, states);
|
|
3464
|
+
|
|
3465
|
+
// Show search results info with applied filters
|
|
3466
|
+
searchResultsCount.textContent = data.count;
|
|
3467
|
+
this.renderAppliedFilters(filters, appliedFiltersContainer);
|
|
3468
|
+
searchResultsInfo.classList.add('active');
|
|
3469
|
+
|
|
3470
|
+
// Close the search panel
|
|
3471
|
+
this.closeAdvancedSearch();
|
|
3472
|
+
|
|
3473
|
+
console.log('🔍 Search completed:', data.count, 'results found');
|
|
3474
|
+
} catch (error) {
|
|
3475
|
+
console.error('Error performing advanced search:', error);
|
|
3476
|
+
conversationsList.innerHTML = `
|
|
3477
|
+
<div class="no-conversations">
|
|
3478
|
+
<div class="no-conversations-icon">⚠️</div>
|
|
3479
|
+
<h3>Search Error</h3>
|
|
3480
|
+
<p>${error.message}</p>
|
|
3481
|
+
<button onclick="location.reload()" style="margin-top: 12px; padding: 8px 16px; background: var(--text-accent); color: white; border: none; border-radius: 4px; cursor: pointer;">Retry</button>
|
|
3482
|
+
</div>
|
|
3483
|
+
`;
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
renderAppliedFilters(filters, container) {
|
|
3488
|
+
container.innerHTML = '';
|
|
3489
|
+
|
|
3490
|
+
const filterTags = [];
|
|
3491
|
+
|
|
3492
|
+
// Quick search filter
|
|
3493
|
+
if (filters.query) {
|
|
3494
|
+
filterTags.push({
|
|
3495
|
+
icon: '💬',
|
|
3496
|
+
label: 'Search',
|
|
3497
|
+
value: filters.query
|
|
3498
|
+
});
|
|
3499
|
+
}
|
|
3500
|
+
|
|
3501
|
+
// Working directory filter
|
|
3502
|
+
if (filters.workingDirectory) {
|
|
3503
|
+
// Shorten the path for display
|
|
3504
|
+
const shortPath = filters.workingDirectory.length > 30
|
|
3505
|
+
? '...' + filters.workingDirectory.slice(-30)
|
|
3506
|
+
: filters.workingDirectory;
|
|
3507
|
+
filterTags.push({
|
|
3508
|
+
icon: '📁',
|
|
3509
|
+
label: 'Directory',
|
|
3510
|
+
value: shortPath
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
|
|
3514
|
+
// Date range filter
|
|
3515
|
+
if (filters.dateFrom || filters.dateTo) {
|
|
3516
|
+
let dateValue = '';
|
|
3517
|
+
if (filters.dateFrom && filters.dateTo) {
|
|
3518
|
+
dateValue = `${this.formatDate(filters.dateFrom)} → ${this.formatDate(filters.dateTo)}`;
|
|
3519
|
+
} else if (filters.dateFrom) {
|
|
3520
|
+
dateValue = `From ${this.formatDate(filters.dateFrom)}`;
|
|
3521
|
+
} else if (filters.dateTo) {
|
|
3522
|
+
dateValue = `Until ${this.formatDate(filters.dateTo)}`;
|
|
3523
|
+
}
|
|
3524
|
+
filterTags.push({
|
|
3525
|
+
icon: '📅',
|
|
3526
|
+
label: 'Date',
|
|
3527
|
+
value: dateValue
|
|
3528
|
+
});
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
// Content search filter
|
|
3532
|
+
if (filters.contentSearch) {
|
|
3533
|
+
filterTags.push({
|
|
3534
|
+
icon: '🔎',
|
|
3535
|
+
label: 'Content',
|
|
3536
|
+
value: filters.contentSearch
|
|
3537
|
+
});
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
// Render filter tags
|
|
3541
|
+
filterTags.forEach(tag => {
|
|
3542
|
+
const tagElement = document.createElement('div');
|
|
3543
|
+
tagElement.className = 'filter-tag';
|
|
3544
|
+
tagElement.innerHTML = `
|
|
3545
|
+
<span class="filter-tag-icon">${tag.icon}</span>
|
|
3546
|
+
<span class="filter-tag-label">${tag.label}:</span>
|
|
3547
|
+
<span class="filter-tag-value">${tag.value}</span>
|
|
3548
|
+
`;
|
|
3549
|
+
container.appendChild(tagElement);
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
formatDate(dateString) {
|
|
3554
|
+
const date = new Date(dateString);
|
|
3555
|
+
const options = { month: 'short', day: 'numeric', year: 'numeric' };
|
|
3556
|
+
return date.toLocaleDateString('en-US', options);
|
|
3557
|
+
}
|
|
3558
|
+
|
|
3559
|
+
// Folder Browser Methods
|
|
3560
|
+
async toggleFolderBrowser() {
|
|
3561
|
+
const dropdown = document.getElementById('folderBrowserDropdown');
|
|
3562
|
+
const isActive = dropdown.classList.contains('active');
|
|
3563
|
+
|
|
3564
|
+
if (isActive) {
|
|
3565
|
+
dropdown.classList.remove('active');
|
|
3566
|
+
} else {
|
|
3567
|
+
// Load directories if not loaded yet
|
|
3568
|
+
if (!this.directories) {
|
|
3569
|
+
await this.loadDirectories();
|
|
3570
|
+
}
|
|
3571
|
+
dropdown.classList.add('active');
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3575
|
+
async loadDirectories() {
|
|
3576
|
+
const listContainer = document.getElementById('folderBrowserList');
|
|
3577
|
+
|
|
3578
|
+
try {
|
|
3579
|
+
listContainer.innerHTML = '<div class="folder-browser-empty">Loading directories...</div>';
|
|
3580
|
+
|
|
3581
|
+
const response = await fetch('/api/directories');
|
|
3582
|
+
if (!response.ok) {
|
|
3583
|
+
throw new Error('Failed to load directories');
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
const data = await response.json();
|
|
3587
|
+
this.directories = data.directories || [];
|
|
3588
|
+
|
|
3589
|
+
// Count conversations per directory
|
|
3590
|
+
this.directoryCounts = {};
|
|
3591
|
+
this.conversations.forEach(conv => {
|
|
3592
|
+
if (conv.project) {
|
|
3593
|
+
this.directoryCounts[conv.project] = (this.directoryCounts[conv.project] || 0) + 1;
|
|
3594
|
+
}
|
|
3595
|
+
});
|
|
3596
|
+
|
|
3597
|
+
this.renderFolderBrowserList(this.directories);
|
|
3598
|
+
} catch (error) {
|
|
3599
|
+
console.error('Error loading directories:', error);
|
|
3600
|
+
listContainer.innerHTML = '<div class="folder-browser-empty">Error loading directories</div>';
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
renderFolderBrowserList(directories) {
|
|
3605
|
+
const listContainer = document.getElementById('folderBrowserList');
|
|
3606
|
+
const currentValue = document.getElementById('filterWorkingDirectory').value;
|
|
3607
|
+
|
|
3608
|
+
if (directories.length === 0) {
|
|
3609
|
+
listContainer.innerHTML = '<div class="folder-browser-empty">No directories found</div>';
|
|
3610
|
+
return;
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
listContainer.innerHTML = directories.map(dir => {
|
|
3614
|
+
const count = this.directoryCounts[dir] || 0;
|
|
3615
|
+
const isSelected = dir === currentValue;
|
|
3616
|
+
|
|
3617
|
+
return `
|
|
3618
|
+
<div class="folder-item ${isSelected ? 'selected' : ''}" data-path="${this.escapeHtml(dir)}">
|
|
3619
|
+
<span class="folder-icon">📁</span>
|
|
3620
|
+
<span class="folder-path" title="${this.escapeHtml(dir)}">${this.escapeHtml(dir)}</span>
|
|
3621
|
+
<span class="folder-count">${count}</span>
|
|
3622
|
+
</div>
|
|
3623
|
+
`;
|
|
3624
|
+
}).join('');
|
|
3625
|
+
|
|
3626
|
+
// Add click handlers to folder items
|
|
3627
|
+
listContainer.querySelectorAll('.folder-item').forEach(item => {
|
|
3628
|
+
item.addEventListener('click', () => {
|
|
3629
|
+
const path = item.getAttribute('data-path');
|
|
3630
|
+
this.selectDirectory(path);
|
|
3631
|
+
});
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
selectDirectory(path) {
|
|
3636
|
+
const input = document.getElementById('filterWorkingDirectory');
|
|
3637
|
+
const dropdown = document.getElementById('folderBrowserDropdown');
|
|
3638
|
+
|
|
3639
|
+
input.value = path;
|
|
3640
|
+
dropdown.classList.remove('active');
|
|
3641
|
+
|
|
3642
|
+
// Update UI to show selected folder
|
|
3643
|
+
this.renderFolderBrowserList(this.directories);
|
|
3644
|
+
}
|
|
3645
|
+
|
|
3646
|
+
filterFolderBrowserList(searchTerm) {
|
|
3647
|
+
if (!this.directories) return;
|
|
3648
|
+
|
|
3649
|
+
const term = searchTerm.toLowerCase();
|
|
3650
|
+
const filtered = this.directories.filter(dir =>
|
|
3651
|
+
dir.toLowerCase().includes(term)
|
|
3652
|
+
);
|
|
3653
|
+
|
|
3654
|
+
this.renderFolderBrowserList(filtered);
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3657
|
+
escapeHtml(text) {
|
|
3658
|
+
const div = document.createElement('div');
|
|
3659
|
+
div.textContent = text;
|
|
3660
|
+
return div.innerHTML;
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
// In-Conversation Search Methods
|
|
3664
|
+
toggleChatSearch() {
|
|
3665
|
+
const searchBar = document.getElementById('chatSearchBar');
|
|
3666
|
+
const toggle = document.getElementById('chatSearchToggle');
|
|
3667
|
+
const isActive = searchBar.classList.contains('active');
|
|
3668
|
+
|
|
3669
|
+
if (isActive) {
|
|
3670
|
+
this.closeChatSearch();
|
|
3671
|
+
} else {
|
|
3672
|
+
searchBar.classList.add('active');
|
|
3673
|
+
toggle.classList.add('active');
|
|
3674
|
+
document.getElementById('chatSearchInput').focus();
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
closeChatSearch() {
|
|
3679
|
+
const searchBar = document.getElementById('chatSearchBar');
|
|
3680
|
+
const toggle = document.getElementById('chatSearchToggle');
|
|
3681
|
+
const input = document.getElementById('chatSearchInput');
|
|
3682
|
+
|
|
3683
|
+
searchBar.classList.remove('active');
|
|
3684
|
+
toggle.classList.remove('active');
|
|
3685
|
+
input.value = '';
|
|
3686
|
+
|
|
3687
|
+
// Clear search state
|
|
3688
|
+
this.chatSearchMatches = [];
|
|
3689
|
+
this.chatSearchCurrentIndex = -1;
|
|
3690
|
+
this.updateSearchCounter();
|
|
3691
|
+
this.clearSearchHighlights();
|
|
3692
|
+
}
|
|
3693
|
+
|
|
3694
|
+
clearChatSearch() {
|
|
3695
|
+
// Clear search results but keep search bar open
|
|
3696
|
+
this.chatSearchMatches = [];
|
|
3697
|
+
this.chatSearchCurrentIndex = -1;
|
|
3698
|
+
this.updateSearchCounter();
|
|
3699
|
+
this.clearSearchHighlights();
|
|
3700
|
+
}
|
|
3701
|
+
|
|
3702
|
+
async performChatSearch(query) {
|
|
3703
|
+
if (!this.selectedConversationId || !query || query.trim().length === 0) {
|
|
3704
|
+
this.chatSearchMatches = [];
|
|
3705
|
+
this.chatSearchCurrentIndex = -1;
|
|
3706
|
+
this.updateSearchCounter();
|
|
3707
|
+
this.clearSearchHighlights();
|
|
3708
|
+
return;
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
try {
|
|
3712
|
+
const response = await fetch(`/api/conversations/${this.selectedConversationId}/search`, {
|
|
3713
|
+
method: 'POST',
|
|
3714
|
+
headers: {
|
|
3715
|
+
'Content-Type': 'application/json'
|
|
3716
|
+
},
|
|
3717
|
+
body: JSON.stringify({ query: query.trim() })
|
|
3718
|
+
});
|
|
3719
|
+
|
|
3720
|
+
if (!response.ok) {
|
|
3721
|
+
throw new Error('Search failed');
|
|
3722
|
+
}
|
|
3723
|
+
|
|
3724
|
+
const data = await response.json();
|
|
3725
|
+
this.chatSearchMatches = data.matches || [];
|
|
3726
|
+
this.chatSearchCurrentIndex = this.chatSearchMatches.length > 0 ? 0 : -1;
|
|
3727
|
+
|
|
3728
|
+
console.log('🔍 Search results:', {
|
|
3729
|
+
query: query,
|
|
3730
|
+
totalMatches: this.chatSearchMatches.length,
|
|
3731
|
+
matches: this.chatSearchMatches
|
|
3732
|
+
});
|
|
3733
|
+
|
|
3734
|
+
this.updateSearchCounter();
|
|
3735
|
+
this.highlightSearchResults(query);
|
|
3736
|
+
|
|
3737
|
+
// Navigate to first match
|
|
3738
|
+
if (this.chatSearchMatches.length > 0) {
|
|
3739
|
+
await this.scrollToSearchMatch(0);
|
|
3740
|
+
}
|
|
3741
|
+
} catch (error) {
|
|
3742
|
+
console.error('Error searching in conversation:', error);
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
|
|
3746
|
+
async navigateSearchResults(direction) {
|
|
3747
|
+
if (this.chatSearchMatches.length === 0) return;
|
|
3748
|
+
|
|
3749
|
+
if (direction === 'next') {
|
|
3750
|
+
this.chatSearchCurrentIndex = (this.chatSearchCurrentIndex + 1) % this.chatSearchMatches.length;
|
|
2126
3751
|
} else {
|
|
2127
|
-
|
|
2128
|
-
scrollTop: chatMessages.scrollTop,
|
|
2129
|
-
scrollHeight: chatMessages.scrollHeight,
|
|
2130
|
-
isNearBottom: this.isNearBottom()
|
|
2131
|
-
});
|
|
3752
|
+
this.chatSearchCurrentIndex = (this.chatSearchCurrentIndex - 1 + this.chatSearchMatches.length) % this.chatSearchMatches.length;
|
|
2132
3753
|
}
|
|
3754
|
+
|
|
3755
|
+
this.updateSearchCounter();
|
|
3756
|
+
await this.scrollToSearchMatch(this.chatSearchCurrentIndex);
|
|
2133
3757
|
}
|
|
2134
3758
|
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
*/
|
|
2138
|
-
setupScrollTracking() {
|
|
2139
|
-
const chatMessages = document.getElementById('chatMessages');
|
|
2140
|
-
if (!chatMessages || this.scrollListener) return;
|
|
3759
|
+
async scrollToSearchMatch(index) {
|
|
3760
|
+
if (index < 0 || index >= this.chatSearchMatches.length) return;
|
|
2141
3761
|
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
3762
|
+
const match = this.chatSearchMatches[index];
|
|
3763
|
+
const messageId = match.messageId;
|
|
3764
|
+
const messageIndex = match.messageIndex;
|
|
3765
|
+
|
|
3766
|
+
// Try to find the message by ID first (more reliable)
|
|
3767
|
+
let messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
|
|
3768
|
+
|
|
3769
|
+
// Fallback to index if ID doesn't work
|
|
3770
|
+
if (!messageElement) {
|
|
3771
|
+
messageElement = document.querySelector(`[data-message-index="${messageIndex}"]`);
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
if (messageElement) {
|
|
3775
|
+
// Message is already in DOM, just scroll to it
|
|
3776
|
+
this.scrollToMessage(messageElement);
|
|
3777
|
+
this.highlightCurrentMatchById(messageId);
|
|
3778
|
+
} else {
|
|
3779
|
+
// Message not loaded yet - need to load more messages
|
|
3780
|
+
// This handles the infinite scroll case
|
|
3781
|
+
await this.loadMessageById(messageId, messageIndex);
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
|
|
3785
|
+
async loadMessageById(messageId, messageIndex) {
|
|
3786
|
+
// Keep loading more messages until we find the target message by ID
|
|
3787
|
+
let attempts = 0;
|
|
3788
|
+
const maxAttempts = 10; // Prevent infinite loops
|
|
3789
|
+
|
|
3790
|
+
while (attempts < maxAttempts) {
|
|
3791
|
+
// Try to find by ID first
|
|
3792
|
+
let messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
|
|
3793
|
+
|
|
3794
|
+
if (messageElement) {
|
|
3795
|
+
// Found the message!
|
|
3796
|
+
this.scrollToMessage(messageElement);
|
|
3797
|
+
this.highlightCurrentMatchById(messageId);
|
|
3798
|
+
return;
|
|
2146
3799
|
}
|
|
2147
3800
|
|
|
2148
|
-
//
|
|
2149
|
-
this.
|
|
3801
|
+
// Try to load more messages
|
|
3802
|
+
if (this.messagesPagination.hasMore && !this.messagesPagination.isLoading) {
|
|
3803
|
+
console.log(`🔄 Loading more messages to find message ${messageId}...`);
|
|
3804
|
+
await this.loadMoreMessages(this.selectedConversationId, false);
|
|
3805
|
+
attempts++;
|
|
2150
3806
|
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
this.autoScrollEnabled = true;
|
|
2154
|
-
console.log('📱 User scrolled to bottom, re-enabling auto-scroll');
|
|
3807
|
+
// Wait a bit for messages to render
|
|
3808
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
2155
3809
|
} else {
|
|
2156
|
-
|
|
3810
|
+
// No more messages to load or already loading
|
|
3811
|
+
console.warn('Could not find message with ID', messageId, 'after', attempts, 'attempts');
|
|
3812
|
+
break;
|
|
2157
3813
|
}
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
2158
3816
|
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
}
|
|
3817
|
+
scrollToMessage(messageElement) {
|
|
3818
|
+
if (!messageElement) {
|
|
3819
|
+
console.warn('No message element to scroll to');
|
|
3820
|
+
return;
|
|
3821
|
+
}
|
|
2164
3822
|
|
|
2165
|
-
|
|
2166
|
-
|
|
3823
|
+
const messagesContainer = document.getElementById('chatMessages');
|
|
3824
|
+
if (!messagesContainer) return;
|
|
3825
|
+
|
|
3826
|
+
// Get the absolute position of the message within the scrollable container
|
|
3827
|
+
const containerTop = messagesContainer.getBoundingClientRect().top;
|
|
3828
|
+
const messageTop = messageElement.getBoundingClientRect().top;
|
|
3829
|
+
const containerHeight = messagesContainer.clientHeight;
|
|
3830
|
+
const messageHeight = messageElement.offsetHeight;
|
|
3831
|
+
|
|
3832
|
+
// Calculate the current scroll position
|
|
3833
|
+
const currentScroll = messagesContainer.scrollTop;
|
|
3834
|
+
|
|
3835
|
+
// Calculate the offset from the top of the container
|
|
3836
|
+
const messageOffsetFromTop = messageTop - containerTop;
|
|
3837
|
+
|
|
3838
|
+
// Calculate target scroll position to center the message
|
|
3839
|
+
// We want the message to be in the middle of the visible area
|
|
3840
|
+
const targetScroll = currentScroll + messageOffsetFromTop - (containerHeight / 2) + (messageHeight / 2);
|
|
3841
|
+
|
|
3842
|
+
// Scroll to the target position
|
|
3843
|
+
messagesContainer.scrollTo({
|
|
3844
|
+
top: Math.max(0, targetScroll), // Don't scroll to negative values
|
|
3845
|
+
behavior: 'smooth'
|
|
3846
|
+
});
|
|
3847
|
+
|
|
3848
|
+
// Add a brief highlight animation to make it more visible
|
|
3849
|
+
messageElement.style.transition = 'background-color 0.3s ease';
|
|
3850
|
+
const originalBg = messageElement.style.backgroundColor;
|
|
3851
|
+
messageElement.style.backgroundColor = 'rgba(255, 107, 53, 0.1)';
|
|
3852
|
+
|
|
3853
|
+
setTimeout(() => {
|
|
3854
|
+
messageElement.style.backgroundColor = originalBg;
|
|
3855
|
+
setTimeout(() => {
|
|
3856
|
+
messageElement.style.transition = '';
|
|
3857
|
+
}, 300);
|
|
3858
|
+
}, 600);
|
|
2167
3859
|
}
|
|
2168
3860
|
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
3861
|
+
highlightSearchResults(query) {
|
|
3862
|
+
this.clearSearchHighlights();
|
|
3863
|
+
const searchTerm = query.toLowerCase();
|
|
3864
|
+
const messageElements = document.querySelectorAll('.message-content');
|
|
3865
|
+
|
|
3866
|
+
messageElements.forEach(element => {
|
|
3867
|
+
const text = element.textContent;
|
|
3868
|
+
if (text.toLowerCase().includes(searchTerm)) {
|
|
3869
|
+
const highlightedHTML = this.highlightText(element.innerHTML, query);
|
|
3870
|
+
element.innerHTML = highlightedHTML;
|
|
3871
|
+
}
|
|
3872
|
+
});
|
|
3873
|
+
}
|
|
3874
|
+
|
|
3875
|
+
highlightText(html, searchTerm) {
|
|
3876
|
+
const text = document.createElement('div');
|
|
3877
|
+
text.innerHTML = html;
|
|
3878
|
+
const textContent = text.textContent;
|
|
3879
|
+
|
|
3880
|
+
if (!textContent.toLowerCase().includes(searchTerm.toLowerCase())) {
|
|
3881
|
+
return html;
|
|
2177
3882
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
3883
|
+
|
|
3884
|
+
// Use regex to find all occurrences (case-insensitive)
|
|
3885
|
+
const regex = new RegExp(`(${this.escapeRegex(searchTerm)})`, 'gi');
|
|
3886
|
+
const newHTML = textContent.replace(regex, '<span class="message-highlight">$1</span>');
|
|
3887
|
+
|
|
3888
|
+
return newHTML;
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
highlightCurrentMatch(messageIndex) {
|
|
3892
|
+
// Remove previous current highlight
|
|
3893
|
+
document.querySelectorAll('.message-current-highlight').forEach(el => {
|
|
3894
|
+
el.classList.remove('message-current-highlight');
|
|
3895
|
+
el.classList.add('message-highlight');
|
|
3896
|
+
});
|
|
3897
|
+
|
|
3898
|
+
// Add current highlight to the current match
|
|
3899
|
+
const messageElement = document.querySelector(`[data-message-index="${messageIndex}"]`);
|
|
3900
|
+
if (messageElement) {
|
|
3901
|
+
const firstHighlight = messageElement.querySelector('.message-highlight');
|
|
3902
|
+
if (firstHighlight) {
|
|
3903
|
+
firstHighlight.classList.remove('message-highlight');
|
|
3904
|
+
firstHighlight.classList.add('message-current-highlight');
|
|
3905
|
+
|
|
3906
|
+
// Scroll the highlight into view within the message
|
|
3907
|
+
// This ensures the actual highlighted text is visible
|
|
3908
|
+
setTimeout(() => {
|
|
3909
|
+
firstHighlight.scrollIntoView({
|
|
3910
|
+
behavior: 'smooth',
|
|
3911
|
+
block: 'center',
|
|
3912
|
+
inline: 'nearest'
|
|
3913
|
+
});
|
|
3914
|
+
}, 100);
|
|
3915
|
+
}
|
|
2182
3916
|
}
|
|
2183
3917
|
}
|
|
2184
3918
|
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
const
|
|
2194
|
-
|
|
2195
|
-
|
|
3919
|
+
highlightCurrentMatchById(messageId) {
|
|
3920
|
+
// Remove previous current highlight
|
|
3921
|
+
document.querySelectorAll('.message-current-highlight').forEach(el => {
|
|
3922
|
+
el.classList.remove('message-current-highlight');
|
|
3923
|
+
el.classList.add('message-highlight');
|
|
3924
|
+
});
|
|
3925
|
+
|
|
3926
|
+
// Add current highlight to the current match (find by message ID)
|
|
3927
|
+
const messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
|
|
3928
|
+
if (messageElement) {
|
|
3929
|
+
const firstHighlight = messageElement.querySelector('.message-highlight');
|
|
3930
|
+
if (firstHighlight) {
|
|
3931
|
+
firstHighlight.classList.remove('message-highlight');
|
|
3932
|
+
firstHighlight.classList.add('message-current-highlight');
|
|
3933
|
+
|
|
3934
|
+
// Scroll the highlight into view within the message
|
|
3935
|
+
// This ensures the actual highlighted text is visible
|
|
3936
|
+
setTimeout(() => {
|
|
3937
|
+
firstHighlight.scrollIntoView({
|
|
3938
|
+
behavior: 'smooth',
|
|
3939
|
+
block: 'center',
|
|
3940
|
+
inline: 'nearest'
|
|
3941
|
+
});
|
|
3942
|
+
}, 100);
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
|
|
3947
|
+
clearSearchHighlights() {
|
|
3948
|
+
document.querySelectorAll('.message-highlight, .message-current-highlight').forEach(el => {
|
|
3949
|
+
const parent = el.parentNode;
|
|
3950
|
+
parent.replaceChild(document.createTextNode(el.textContent), el);
|
|
3951
|
+
parent.normalize(); // Merge adjacent text nodes
|
|
3952
|
+
});
|
|
2196
3953
|
}
|
|
2197
3954
|
|
|
3955
|
+
updateSearchCounter(currentIndex = null, totalMatches = null, showMinCharsMessage = false) {
|
|
3956
|
+
const counter = document.getElementById('chatSearchCounter');
|
|
3957
|
+
const prevBtn = document.getElementById('chatSearchPrev');
|
|
3958
|
+
const nextBtn = document.getElementById('chatSearchNext');
|
|
2198
3959
|
|
|
3960
|
+
// Use provided values or fall back to instance values
|
|
3961
|
+
const current = currentIndex !== null ? currentIndex : this.chatSearchCurrentIndex;
|
|
3962
|
+
const total = totalMatches !== null ? totalMatches : this.chatSearchMatches.length;
|
|
2199
3963
|
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
items.forEach(item => {
|
|
2205
|
-
const name = item.querySelector('.conversation-name').textContent.toLowerCase();
|
|
2206
|
-
const preview = item.querySelector('.conversation-preview').textContent.toLowerCase();
|
|
2207
|
-
const matches = name.includes(term) || preview.includes(term);
|
|
2208
|
-
item.style.display = matches ? 'flex' : 'none';
|
|
3964
|
+
console.log('📊 Updating counter:', {
|
|
3965
|
+
currentIndex: current,
|
|
3966
|
+
totalMatches: total,
|
|
3967
|
+
showMinCharsMessage: showMinCharsMessage
|
|
2209
3968
|
});
|
|
3969
|
+
|
|
3970
|
+
if (showMinCharsMessage) {
|
|
3971
|
+
counter.textContent = 'Min 3 chars';
|
|
3972
|
+
counter.classList.remove('has-results');
|
|
3973
|
+
prevBtn.disabled = true;
|
|
3974
|
+
nextBtn.disabled = true;
|
|
3975
|
+
} else if (total === 0) {
|
|
3976
|
+
counter.textContent = '0/0';
|
|
3977
|
+
counter.classList.remove('has-results');
|
|
3978
|
+
prevBtn.disabled = true;
|
|
3979
|
+
nextBtn.disabled = true;
|
|
3980
|
+
} else {
|
|
3981
|
+
counter.textContent = `${current + 1}/${total}`;
|
|
3982
|
+
counter.classList.add('has-results');
|
|
3983
|
+
prevBtn.disabled = false;
|
|
3984
|
+
nextBtn.disabled = false;
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
|
|
3988
|
+
async getTotalMessageCount(conversationId) {
|
|
3989
|
+
const conversation = this.conversations.find(c => c.id === conversationId);
|
|
3990
|
+
return conversation ? conversation.messageCount : 0;
|
|
3991
|
+
}
|
|
3992
|
+
|
|
3993
|
+
escapeRegex(string) {
|
|
3994
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
3995
|
+
}
|
|
3996
|
+
|
|
3997
|
+
resetAdvancedSearch() {
|
|
3998
|
+
// Clear all filter inputs
|
|
3999
|
+
document.getElementById('filterQuery').value = '';
|
|
4000
|
+
document.getElementById('filterWorkingDirectory').value = '';
|
|
4001
|
+
document.getElementById('filterDateFrom').value = '';
|
|
4002
|
+
document.getElementById('filterDateTo').value = '';
|
|
4003
|
+
document.getElementById('filterContentSearch').value = '';
|
|
4004
|
+
}
|
|
4005
|
+
|
|
4006
|
+
async clearAllFilters() {
|
|
4007
|
+
const searchResultsInfo = document.getElementById('searchResultsInfo');
|
|
4008
|
+
const searchInput = document.getElementById('searchInput');
|
|
4009
|
+
|
|
4010
|
+
// Clear simple search
|
|
4011
|
+
searchInput.value = '';
|
|
4012
|
+
|
|
4013
|
+
// Clear advanced search filters
|
|
4014
|
+
this.resetAdvancedSearch();
|
|
4015
|
+
|
|
4016
|
+
// Reset expanded projects state (close all projects)
|
|
4017
|
+
if (this.expandedProjects) {
|
|
4018
|
+
this.expandedProjects.clear();
|
|
4019
|
+
}
|
|
4020
|
+
|
|
4021
|
+
// Hide search results info
|
|
4022
|
+
searchResultsInfo.classList.remove('active');
|
|
4023
|
+
|
|
4024
|
+
// Reload all conversations
|
|
4025
|
+
await this.loadConversations();
|
|
2210
4026
|
}
|
|
2211
4027
|
|
|
2212
4028
|
getStateLabel(state) {
|
|
@@ -2906,6 +4722,199 @@
|
|
|
2906
4722
|
}
|
|
2907
4723
|
}
|
|
2908
4724
|
|
|
4725
|
+
// Share conversation functions
|
|
4726
|
+
function shareConversation() {
|
|
4727
|
+
const shareBtn = document.getElementById('shareConversation');
|
|
4728
|
+
const conversationId = shareBtn.getAttribute('data-conversation-id');
|
|
4729
|
+
|
|
4730
|
+
if (!conversationId) {
|
|
4731
|
+
console.error('No conversation ID found');
|
|
4732
|
+
return;
|
|
4733
|
+
}
|
|
4734
|
+
|
|
4735
|
+
console.log('📤 Opening share confirmation for conversation:', conversationId);
|
|
4736
|
+
|
|
4737
|
+
// Show confirmation modal first
|
|
4738
|
+
const confirmModal = document.getElementById('confirmShareModal');
|
|
4739
|
+
confirmModal.classList.add('show');
|
|
4740
|
+
|
|
4741
|
+
// Close modal when clicking outside
|
|
4742
|
+
confirmModal.addEventListener('click', (e) => {
|
|
4743
|
+
if (e.target === confirmModal) {
|
|
4744
|
+
closeConfirmShareModal();
|
|
4745
|
+
}
|
|
4746
|
+
});
|
|
4747
|
+
}
|
|
4748
|
+
|
|
4749
|
+
function closeConfirmShareModal() {
|
|
4750
|
+
const confirmModal = document.getElementById('confirmShareModal');
|
|
4751
|
+
confirmModal.classList.remove('show');
|
|
4752
|
+
}
|
|
4753
|
+
|
|
4754
|
+
async function proceedWithShare() {
|
|
4755
|
+
// Close confirmation modal
|
|
4756
|
+
closeConfirmShareModal();
|
|
4757
|
+
|
|
4758
|
+
const shareBtn = document.getElementById('shareConversation');
|
|
4759
|
+
const conversationId = shareBtn.getAttribute('data-conversation-id');
|
|
4760
|
+
|
|
4761
|
+
if (!conversationId) {
|
|
4762
|
+
console.error('No conversation ID found');
|
|
4763
|
+
return;
|
|
4764
|
+
}
|
|
4765
|
+
|
|
4766
|
+
console.log('📤 User confirmed - Proceeding with share for conversation:', conversationId);
|
|
4767
|
+
|
|
4768
|
+
// Show share modal with loading state
|
|
4769
|
+
const modal = document.getElementById('shareModal');
|
|
4770
|
+
const loadingDiv = document.getElementById('shareModalLoading');
|
|
4771
|
+
const contentDiv = document.getElementById('shareModalContent');
|
|
4772
|
+
const copyBtn = document.getElementById('copyShareCommandBtn');
|
|
4773
|
+
|
|
4774
|
+
modal.classList.add('show');
|
|
4775
|
+
loadingDiv.style.display = 'block';
|
|
4776
|
+
contentDiv.style.display = 'none';
|
|
4777
|
+
copyBtn.style.display = 'none';
|
|
4778
|
+
|
|
4779
|
+
// Close modal when clicking outside
|
|
4780
|
+
modal.addEventListener('click', (e) => {
|
|
4781
|
+
if (e.target === modal) {
|
|
4782
|
+
closeShareModal();
|
|
4783
|
+
}
|
|
4784
|
+
});
|
|
4785
|
+
|
|
4786
|
+
try {
|
|
4787
|
+
// Call API to share the conversation
|
|
4788
|
+
const response = await fetch(`/api/conversations/${conversationId}/share`, {
|
|
4789
|
+
method: 'POST',
|
|
4790
|
+
headers: {
|
|
4791
|
+
'Content-Type': 'application/json'
|
|
4792
|
+
}
|
|
4793
|
+
});
|
|
4794
|
+
|
|
4795
|
+
if (!response.ok) {
|
|
4796
|
+
throw new Error(`Failed to share session: ${response.statusText}`);
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4799
|
+
const data = await response.json();
|
|
4800
|
+
|
|
4801
|
+
console.log('✅ Session shared successfully:', data);
|
|
4802
|
+
|
|
4803
|
+
// Update modal with share data
|
|
4804
|
+
const qrCodeImg = document.getElementById('shareQRCode');
|
|
4805
|
+
const commandDiv = document.getElementById('shareModalCommand');
|
|
4806
|
+
const directUrlLink = document.getElementById('shareDirectUrl');
|
|
4807
|
+
|
|
4808
|
+
// Set QR code
|
|
4809
|
+
if (data.qrCode && data.qrCode.dataUrl) {
|
|
4810
|
+
qrCodeImg.src = data.qrCode.dataUrl;
|
|
4811
|
+
qrCodeImg.style.display = 'block';
|
|
4812
|
+
} else {
|
|
4813
|
+
qrCodeImg.style.display = 'none';
|
|
4814
|
+
}
|
|
4815
|
+
|
|
4816
|
+
// Set command
|
|
4817
|
+
commandDiv.textContent = data.shareCommand;
|
|
4818
|
+
commandDiv.setAttribute('data-command', data.shareCommand);
|
|
4819
|
+
|
|
4820
|
+
// Set direct URL
|
|
4821
|
+
directUrlLink.textContent = data.uploadUrl;
|
|
4822
|
+
directUrlLink.href = data.uploadUrl;
|
|
4823
|
+
|
|
4824
|
+
// Show message count information
|
|
4825
|
+
const messageInfoDiv = document.getElementById('shareMessageInfo');
|
|
4826
|
+
const messageCountSpan = document.getElementById('shareMessageCount');
|
|
4827
|
+
|
|
4828
|
+
if (data.wasLimited) {
|
|
4829
|
+
messageCountSpan.innerHTML = `⚠️ This session has <strong>${data.totalMessageCount}</strong> messages. Sharing last <strong>${data.messageCount}</strong> messages to keep file size manageable.`;
|
|
4830
|
+
messageInfoDiv.style.display = 'block';
|
|
4831
|
+
} else {
|
|
4832
|
+
messageCountSpan.innerHTML = `✅ Sharing <strong>${data.messageCount}</strong> messages from this conversation.`;
|
|
4833
|
+
messageInfoDiv.style.display = 'block';
|
|
4834
|
+
}
|
|
4835
|
+
|
|
4836
|
+
// Show content and hide loading
|
|
4837
|
+
loadingDiv.style.display = 'none';
|
|
4838
|
+
contentDiv.style.display = 'block';
|
|
4839
|
+
copyBtn.style.display = 'block';
|
|
4840
|
+
|
|
4841
|
+
} catch (error) {
|
|
4842
|
+
console.error('❌ Failed to share session:', error);
|
|
4843
|
+
alert(`Failed to share session: ${error.message}`);
|
|
4844
|
+
closeShareModal();
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4847
|
+
|
|
4848
|
+
function closeShareModal() {
|
|
4849
|
+
const modal = document.getElementById('shareModal');
|
|
4850
|
+
modal.classList.remove('show');
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4853
|
+
function copyShareCommand() {
|
|
4854
|
+
const commandDiv = document.getElementById('shareModalCommand');
|
|
4855
|
+
const copyBtn = document.getElementById('copyShareCommandBtn');
|
|
4856
|
+
const command = commandDiv.getAttribute('data-command');
|
|
4857
|
+
|
|
4858
|
+
if (!command) {
|
|
4859
|
+
console.error('No command found to copy');
|
|
4860
|
+
return;
|
|
4861
|
+
}
|
|
4862
|
+
|
|
4863
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
4864
|
+
navigator.clipboard.writeText(command).then(() => {
|
|
4865
|
+
// Show success feedback
|
|
4866
|
+
const originalText = copyBtn.textContent;
|
|
4867
|
+
copyBtn.textContent = '✅ Copied!';
|
|
4868
|
+
copyBtn.style.backgroundColor = 'rgba(63, 185, 80, 0.8)';
|
|
4869
|
+
copyBtn.style.borderColor = 'rgba(63, 185, 80, 1)';
|
|
4870
|
+
|
|
4871
|
+
setTimeout(() => {
|
|
4872
|
+
copyBtn.textContent = originalText;
|
|
4873
|
+
copyBtn.style.backgroundColor = '';
|
|
4874
|
+
copyBtn.style.borderColor = '';
|
|
4875
|
+
}, 1500);
|
|
4876
|
+
|
|
4877
|
+
console.log('📋 Share command copied to clipboard:', command);
|
|
4878
|
+
}).catch(err => {
|
|
4879
|
+
console.error('Failed to copy to clipboard:', err);
|
|
4880
|
+
fallbackCopyShare(command);
|
|
4881
|
+
});
|
|
4882
|
+
} else {
|
|
4883
|
+
// Fallback for browsers without clipboard API
|
|
4884
|
+
fallbackCopyShare(command);
|
|
4885
|
+
}
|
|
4886
|
+
}
|
|
4887
|
+
|
|
4888
|
+
function fallbackCopyShare(command) {
|
|
4889
|
+
// Create a temporary text area to select and copy
|
|
4890
|
+
const tempTextArea = document.createElement('textarea');
|
|
4891
|
+
tempTextArea.value = command;
|
|
4892
|
+
tempTextArea.style.position = 'fixed';
|
|
4893
|
+
tempTextArea.style.left = '-9999px';
|
|
4894
|
+
document.body.appendChild(tempTextArea);
|
|
4895
|
+
tempTextArea.select();
|
|
4896
|
+
|
|
4897
|
+
try {
|
|
4898
|
+
document.execCommand('copy');
|
|
4899
|
+
const copyBtn = document.getElementById('copyShareCommandBtn');
|
|
4900
|
+
const originalText = copyBtn.textContent;
|
|
4901
|
+
copyBtn.textContent = '✅ Copied!';
|
|
4902
|
+
copyBtn.style.backgroundColor = 'rgba(63, 185, 80, 0.8)';
|
|
4903
|
+
|
|
4904
|
+
setTimeout(() => {
|
|
4905
|
+
copyBtn.textContent = originalText;
|
|
4906
|
+
copyBtn.style.backgroundColor = '';
|
|
4907
|
+
}, 1500);
|
|
4908
|
+
|
|
4909
|
+
console.log('📋 Share command copied using fallback method:', command);
|
|
4910
|
+
} catch (err) {
|
|
4911
|
+
console.error('Fallback copy failed:', err);
|
|
4912
|
+
alert(`Please copy this command manually:\n\n${command}`);
|
|
4913
|
+
} finally {
|
|
4914
|
+
document.body.removeChild(tempTextArea);
|
|
4915
|
+
}
|
|
4916
|
+
}
|
|
4917
|
+
|
|
2909
4918
|
// Initialize the app
|
|
2910
4919
|
document.addEventListener('DOMContentLoaded', () => {
|
|
2911
4920
|
new ChatsMobileApp();
|